UNPKG

@odata2ts/odata2ts

Version:

Flexible generator to produce various TypeScript artefacts (from simple model interfaces to complete odata clients) from OData metadata files

352 lines (351 loc) 15.2 kB
import { TypeConverterConfig } from "@odata2ts/converter-runtime"; import { AxiosRequestConfig } from "axios"; import { NameSettings, OverridableNamingOptions } from "./NamingModel.js"; import { TypeModel } from "./TypeModel.js"; /** * Generation mode, by default "all". */ export declare enum Modes { models = 0, qobjects = 1, service = 2, all = 3 } /** * What kind of stuff to emit: Either raw TS or TS that has been compiled to JS / DTS. */ export declare enum EmitModes { ts = "ts", js = "js", dts = "dts", js_dts = "js_dts" } /** * Config options for CLI. */ export interface CliOptions { /** * The URL to the root of your OData service. The URL might end in a slash or not, it might also end * in $metadata, but we usually add this for you. * * Specifying the URL is a convenience feature to download the metadata file from the given URL. * You can configure this request via `sourceConfig` option. * * The `source` option must still be specified as it is used to store the downloaded file on your disk. * By default, the file is used once it has been downloaded. */ sourceUrl?: string; /** * Downloads the metadata file and overwrites the existing one, if any. * * Only takes effect, if option `sourceUrl` is specified. */ refreshFile?: boolean; /** * The source is the file to use (must be an EDMX compliant XML file) or the URL to the * metadata (ROOT_SERVICE/$metadata). * * If not specified, at least one service must be configured in config file. */ source?: string; /** * Specifies the output directory for the generated stuff. * * If not specified, at least one service must be configured in config file. */ output?: string; /** * Only generates the specified services. * Relies on an existing config file where these service names are maintained. */ services?: Array<string>; /** * Specifies what to generate: * - {@code Modes.models} will only generate TS interfaces * - {@code Modes.qobjects} will generate functional units used in QueryBuilder and for functions and actions * - {@code Modes.service} will generate one main OData service client and one per each entity * - {@code Modes.all} the same as {@code Modes.service} * * QObjects will also generate models, and generating the service client will also generate models and QObjects. * Defaults to {@code Modes.all} */ mode?: Modes; /** * Specifies the type of the output files: TypeScript, JS, DTS only, JS with DTS. * Defaults to {@code EmitModes.js_dts} */ emitMode?: EmitModes; /** * Uses prettier with your local configuration to pretty print TypeScript files. * Only applies if mode is set to {@code EmitModes.ts}. */ prettier?: boolean; /** * When compiling TypeScript to JS, "tsconfig.json" is used by default to add compilerOptions. * This option allows to specify an alternative file. * * Only takes effect, when mode is set to anything else than {@code EmitModes.ts}. */ tsconfig?: string; /** * Verbose debugging information. */ debug?: boolean; /** * Overrides the service name found in the source file. * * The service name is the basis for all file names and the name of the main OData client service * that serves as entry point for the user. */ serviceName?: string; /** * odata2ts will automatically decide if a key prop is managed on the server side. * If managed, the property will not be editable (create, update, patch). * The following rule applies: * If a property is the only key prop of an entity, then the prop is deemed to be managed; * in ony other case the prop is unmanaged. */ disableAutoManagedKey?: boolean; /** * By default, odata2ts doesn't change param, operation, property or model names. * The generated models and their properties are named exactly as advertised by the server. * * By allowing odata2ts to change these names, certain predefined formatting strategies are used: * Model / class names are formatted with PascalCase; property, param, and operation names with camelCase. * * The naming configuration allows to control this and other naming related settings. * Note: Even if renaming is disabled, model prefixing / suffixing still applies. */ allowRenaming?: boolean; } /** * Configuration options of the request to retrieve the metadata. * Only takes effect if `source` is a URL. */ export interface UrlSourceConfiguration { /** * Basic auth credentials: the username. * Only takes effect if `password` has also been set. */ username?: string; /** * Basic auth credentials: the password. * Only takes effect if `username` has also been set. */ password?: string; /** * Custom request configuration. * URL and method `GET` are set by default, but can be overwritten. */ custom?: AxiosRequestConfig; } /** * Available options for configuration files, i.e. odata2ts.config.ts. */ export interface ConfigFileOptions extends Omit<CliOptions, "sourceUrl" | "source" | "output" | "services"> { /** * Configuration options of the request to retrieve the metadata. * Only takes effect if `sourceUrl` is a URL. */ sourceUrlConfig?: UrlSourceConfiguration; /** * Configuration of each service. * * @example { services: { trippin: { source: "...", ... } }} */ services?: { [serviceName: string]: ServiceGenerationOptions; }; /** * Specify which converters to use by their package name, e.g. "@odata2ts/converter-v2-to-v4". * Each converter knows which data type to map. * * To only use specific converters, the object syntax must be used, where supported converters * must be listed by their ids. */ converters?: Array<string | TypeConverterConfig>; /** * For each model an editable version is generated which represents the model definition for * create, update and patch actions. * * You can skip the generation altogether, not generating editable model variants, * if the generation mode is {@code Mode.model} or {@code Mode.qobject}. */ skipEditableModels?: boolean; /** * ID models are generated from entity id parameters. * The generation for one entity entails one model interface representing the id parameters and * one QId function which allows to format the parameters for URL usage and to parse parameters * from a URL string. * * You can skip the generation altogether, not generating models and QId objects, if the * generation mode is {@code Mode.model} or {@code Mode.qobject}. */ skipIdModels?: boolean; /** * Operations are functions and actions of the OData service. * The generation for one operation entails one parameter model interface * and one QFunction / QAction class. * * You can skip the generation altogether, neither generating model nor query object, * if the generation mode is {@code Mode.model} or {@code Mode.qobject}. */ skipOperations?: boolean; /** * Model properties have explaining comments by default. * With this option you can turn that off. */ skipComments?: boolean; /** * With OData you can read, update and delete data on a primitive property (`Edm.*`). * Usually, you wouldn't do that, but go for a bigger request, fetching more relevant information in one go. * * There's one exception: Handling `Edm.Stream´ properties and Media entities. Services for stream / media * stuff are generated regardless of this setting. */ enablePrimitivePropertyServices?: boolean; /** * The naming options regarding the generated artefacts. */ naming?: OverridableNamingOptions; /** * Some OData V2 services generate an extra wrapping for entity collection attributes: * <code>trips: {results: [...]}</code>. So instead of directly returning an array of entities * an object with the property "results" is wrapped around the entity collection. * * If you're using the odata client then there's a build-in workaround in place which transforms * the results to remove this extra mapping. However, if you're only interested in the types, then * the generated models will not match that extra wrapping. * * Setting this configuration option to <code>true</code> (default: false) will add this extra * wrapping to the generated models. But this option is only valid if the generation mode is set * to <code>models</code>; it is ignored otherwise. */ v2ModelsWithExtraResultsWrapping?: boolean; /** * Numbers of type `Edm.Int64` and `Edm.Decimal` are represented as `number` in V4. * However, these numbers might not fit into JS' number type, which might result in precision loss. * * OData offers a special IEEE754 format option to get those types as `string` instead to prevent any * precision loss. So if you're handling very large or very small numbers (JS roughly supports 15 digits), * then you should use this option and, probably, also an appropriate converter (see available converters). * * Activating this option affects the type generation and will use `string` for both mentioned types. * All requests are executed with the "accept" header set to "application/json;IEEE754Compatible=true". * Additionally, when sending data the very same value will be set for the "content-type" header. */ v4BigNumberAsString?: boolean; /** * OData allows for namespaces so any entity is unique by virtue of it's name within a namespace. * odata2ts works with these fully qualified names internally, but only uses the plain name when generating * stuff. This might lead to name clashes (same name in different namespaces). * * odata2ts employs a simple, automatic resolution strategy: Adding a counter at the end of the name. * Set this property to true in order to disable this automatism. */ disableAutomaticNameClashResolution?: boolean; /** * By default, odata2ts generates a folder structure with individual files per entity. * This allows for handling and scaling the generation process for large data structures. * * However, especially UI5 has problems with recursive structures, which are absolutely valid within * OData. Here the solution is to generate only one file per type to circumvent cyclic imports. * To enable this behaviour set this option to true. */ bundledFileGeneration?: boolean; /** * If enabled, odata2ts will generate numeric enums instead of string enums. * This */ numericEnums?: boolean; } /** * Custom generation options which are dependent on a specific odata service. */ export interface ServiceGenerationOptions extends Required<Pick<CliOptions, "source" | "output">>, Pick<CliOptions, "sourceUrl" | "refreshFile">, Omit<ConfigFileOptions, "services"> { /** * Configure generation process for individual properties based on their name. */ propertiesByName?: Array<PropertyGenerationOptions>; /** * Rename any EntityType, ComplexType, EnumType, Function or Action. * * You must match the simple name (e.g. "Person") or the fully qualified name * (e.g. "Trippin.Person") exactly. Alternatively, you can rename a bunch of types * by using regular expressions. * * By providing additional type information via the "type" attribute you get even more options which only apply * to the given type. */ byTypeAndName?: Array<ComplexTypeGenerationOptions | EntityTypeGenerationOptions | GenericTypeGenerationOptions>; } /** * Available options for the actual generation run. * Every property is required, except the overriding service name. */ export interface RunOptions extends Required<Omit<ServiceGenerationOptions, "serviceName" | "sourceUrl" | "sourceUrlConfig" | "refreshFile">>, Pick<ServiceGenerationOptions, "serviceName" | "sourceUrl" | "sourceUrlConfig" | "refreshFile"> { naming: NameSettings; } export interface RenameOptions { /** * Matcher for the name of any EntityType, ComplexType, EnumType, Function or Action * as it is stated in the EDMX model, e.g. "Person". As OData supports namespaces * you can also use the fully qualified name (including the namespace) to address any model, * e.g. "Trippin.Person". You can also match properties by their name. * * If the name is specified as plain string, it must match either the name or the fully qualified name * exactly (case-sensitive). * * Alternatively, a regular expression can be used which is always applied to the fully qualified name * (e.g. Trippin.Person). The regular expression must match the whole string * (e.g. `/Person/` won't do, `/.*\.Person/` would work). * * To make regular expressions useful, captured groups are also supported in combination with * the `mappedName` attribute. */ name: string | RegExp; /** * If specified, this attribute value is used as final name for the matched name as it will * appear in the generated typescript. * * When using a regular expression for matching the name, then captured groups can be referenced * as usual via $1, $2, etc. For example: * - name: /Trippin\.(.+)/ * - mappedName: "T_$1" * The result would be "T_Person". */ mappedName?: string; } export type TypeBasedGenerationOptions = GenericTypeGenerationOptions | ComplexTypeGenerationOptions | EntityTypeGenerationOptions; export interface GenericTypeGenerationOptions extends RenameOptions { type: TypeModel.Any | TypeModel.EnumType | TypeModel.OperationType | TypeModel.OperationImportType | TypeModel.Singleton | TypeModel.EntitySet; } export interface ComplexTypeGenerationOptions extends RenameOptions { type: TypeModel.ComplexType; /** * Configuration of individual properties. */ properties?: Array<PropertyGenerationOptions>; } /** * Configuration options for EntityTypes and ComplexTypes. * This config applies if the name matches the name of an EntityType or ComplexType as it is specified * in the metadata (e.g. in EDMX <EntityType name="Test" ...) */ export interface EntityTypeGenerationOptions extends Omit<ComplexTypeGenerationOptions, "type"> { type: TypeModel.EntityType; /** * Overwrite the key specification by naming the props by their EDMX name. */ keys?: Array<string>; } /** * All configuration options for properties of models. */ export interface PropertyGenerationOptions extends RenameOptions { /** * Managed attributes - i.e. managed by the server - cannot be created or updated. * Hence, they are left out of the editable model versions. */ managed?: boolean; }