├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── codeql-analysis.yml │ └── node.js.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── angular.json ├── docs ├── README.md ├── api │ ├── changelog.html │ ├── classes │ │ ├── Aggregate.html │ │ ├── ApiConfig.html │ │ ├── ApplyExpression.html │ │ ├── ArithmeticFunctions.html │ │ ├── ArithmeticOperators.html │ │ ├── Base.html │ │ ├── Callable.html │ │ ├── CollectionFunctions.html │ │ ├── ComputeExpression.html │ │ ├── ConditionalFunctions.html │ │ ├── CountExpression.html │ │ ├── CountField.html │ │ ├── CsdlAction-1.html │ │ ├── CsdlAction.html │ │ ├── CsdlActionImport-1.html │ │ ├── CsdlActionImport.html │ │ ├── CsdlAnnotable-1.html │ │ ├── CsdlAnnotable.html │ │ ├── CsdlAnnotation-1.html │ │ ├── CsdlAnnotation.html │ │ ├── CsdlAnnotations-1.html │ │ ├── CsdlAnnotations.html │ │ ├── CsdlCallable-1.html │ │ ├── CsdlCallable.html │ │ ├── CsdlCollection-1.html │ │ ├── CsdlCollection.html │ │ ├── CsdlComplexType-1.html │ │ ├── CsdlComplexType.html │ │ ├── CsdlEntityContainer-1.html │ │ ├── CsdlEntityContainer.html │ │ ├── CsdlEntitySet-1.html │ │ ├── CsdlEntitySet.html │ │ ├── CsdlEntityType-1.html │ │ ├── CsdlEntityType.html │ │ ├── CsdlEnumMember-1.html │ │ ├── CsdlEnumMember.html │ │ ├── CsdlEnumType-1.html │ │ ├── CsdlEnumType.html │ │ ├── CsdlFunction-1.html │ │ ├── CsdlFunction.html │ │ ├── CsdlFunctionImport-1.html │ │ ├── CsdlFunctionImport.html │ │ ├── CsdlInclude-1.html │ │ ├── CsdlInclude.html │ │ ├── CsdlIncludeAnnotations-1.html │ │ ├── CsdlIncludeAnnotations.html │ │ ├── CsdlKey-1.html │ │ ├── CsdlKey.html │ │ ├── CsdlMember-1.html │ │ ├── CsdlMember.html │ │ ├── CsdlNavigationProperty-1.html │ │ ├── CsdlNavigationProperty.html │ │ ├── CsdlNavigationPropertyBinding-1.html │ │ ├── CsdlNavigationPropertyBinding.html │ │ ├── CsdlNavigationPropertyPath-1.html │ │ ├── CsdlNavigationPropertyPath.html │ │ ├── CsdlOnDelete-1.html │ │ ├── CsdlOnDelete.html │ │ ├── CsdlParameter-1.html │ │ ├── CsdlParameter.html │ │ ├── CsdlProperty-1.html │ │ ├── CsdlProperty.html │ │ ├── CsdlPropertyPath-1.html │ │ ├── CsdlPropertyPath.html │ │ ├── CsdlPropertyRef-1.html │ │ ├── CsdlPropertyRef.html │ │ ├── CsdlPropertyValue-1.html │ │ ├── CsdlPropertyValue.html │ │ ├── CsdlRecord-1.html │ │ ├── CsdlRecord.html │ │ ├── CsdlReference-1.html │ │ ├── CsdlReference.html │ │ ├── CsdlReferentialConstraint-1.html │ │ ├── CsdlReferentialConstraint.html │ │ ├── CsdlReturnType-1.html │ │ ├── CsdlReturnType.html │ │ ├── CsdlSchema-1.html │ │ ├── CsdlSchema.html │ │ ├── CsdlSingleton-1.html │ │ ├── CsdlSingleton.html │ │ ├── CsdlString-1.html │ │ ├── CsdlString.html │ │ ├── CsdlStructuralProperty-1.html │ │ ├── CsdlStructuralProperty.html │ │ ├── CsdlStructuredType-1.html │ │ ├── CsdlStructuredType.html │ │ ├── CsdlTerm-1.html │ │ ├── CsdlTerm.html │ │ ├── CsdlTypeDefinition-1.html │ │ ├── CsdlTypeDefinition.html │ │ ├── DateAndTimeFunctions.html │ │ ├── Entity.html │ │ ├── EntityProperty.html │ │ ├── Enum.html │ │ ├── EnumValue.html │ │ ├── ExpandExpression.html │ │ ├── ExpandField.html │ │ ├── Expression.html │ │ ├── Field-1.html │ │ ├── Field.html │ │ ├── FilterExpression.html │ │ ├── Function.html │ │ ├── GeoFunctions.html │ │ ├── GroupBy.html │ │ ├── GroupByTransformations.html │ │ ├── Grouping.html │ │ ├── GroupingOperators.html │ │ ├── Import.html │ │ ├── Index.html │ │ ├── Lambda.html │ │ ├── LambdaOperators.html │ │ ├── LogicalOperators.html │ │ ├── Metadata.html │ │ ├── Module.html │ │ ├── ODataActionResource.html │ │ ├── ODataAnnotatable.html │ │ ├── ODataAnnotation.html │ │ ├── ODataAnnotations.html │ │ ├── ODataApi.html │ │ ├── ODataApiOptions.html │ │ ├── ODataBaseCache.html │ │ ├── ODataBaseService.html │ │ ├── ODataBatchRequest.html │ │ ├── ODataBatchResource.html │ │ ├── ODataCallable.html │ │ ├── ODataCallableParser.html │ │ ├── ODataCollection.html │ │ ├── ODataConfigAsyncLoader.html │ │ ├── ODataConfigLoader.html │ │ ├── ODataConfigSyncLoader.html │ │ ├── ODataCountResource.html │ │ ├── ODataEntitiesAnnotations.html │ │ ├── ODataEntityAnnotations.html │ │ ├── ODataEntityContainer.html │ │ ├── ODataEntityResource.html │ │ ├── ODataEntityService.html │ │ ├── ODataEntitySet.html │ │ ├── ODataEntitySetResource.html │ │ ├── ODataEntitySetService.html │ │ ├── ODataEntityTypeKey.html │ │ ├── ODataEnumType.html │ │ ├── ODataEnumTypeFieldParser.html │ │ ├── ODataEnumTypeParser.html │ │ ├── ODataFunctionResource.html │ │ ├── ODataInMemoryCache.html │ │ ├── ODataInStorageCache.html │ │ ├── ODataMediaResource.html │ │ ├── ODataMetadata-1.html │ │ ├── ODataMetadata.html │ │ ├── ODataMetadataLoader.html │ │ ├── ODataMetadataParser-1.html │ │ ├── ODataMetadataParser.html │ │ ├── ODataMetadataResource.html │ │ ├── ODataModel.html │ │ ├── ODataModelAttribute.html │ │ ├── ODataModelEvent.html │ │ ├── ODataModelEventEmitter.html │ │ ├── ODataModelField.html │ │ ├── ODataModelOptions.html │ │ ├── ODataNavigationPropertyResource.html │ │ ├── ODataParameterParser.html │ │ ├── ODataParserSchemaElement.html │ │ ├── ODataPathSegments.html │ │ ├── ODataPathSegmentsHandler.html │ │ ├── ODataPropertyAnnotations.html │ │ ├── ODataPropertyResource.html │ │ ├── ODataQueryOptionHandler.html │ │ ├── ODataQueryOptions.html │ │ ├── ODataQueryOptionsHandler.html │ │ ├── ODataReferenceResource.html │ │ ├── ODataReferential.html │ │ ├── ODataRequest.html │ │ ├── ODataResource.html │ │ ├── ODataResponse.html │ │ ├── ODataResponseOptions.html │ │ ├── ODataSchema.html │ │ ├── ODataSchemaElement.html │ │ ├── ODataSettings.html │ │ ├── ODataSingleton.html │ │ ├── ODataSingletonResource.html │ │ ├── ODataSingletonService.html │ │ ├── ODataStructuredType.html │ │ ├── ODataStructuredTypeFieldParser.html │ │ ├── ODataStructuredTypeParser.html │ │ ├── ODataValueResource.html │ │ ├── Operator.html │ │ ├── OrderByExpression.html │ │ ├── OrderByField.html │ │ ├── SearchExpression.html │ │ ├── SearchTerm.html │ │ ├── SegmentHandler.html │ │ ├── SelectExpression.html │ │ ├── Service.html │ │ ├── StringAndCollectionFunctions.html │ │ ├── StringFunctions.html │ │ ├── Transformations.html │ │ ├── Type.html │ │ └── TypeFunctions.html │ ├── contributing.html │ ├── dependencies.html │ ├── fonts │ │ ├── ionicons.eot │ │ ├── ionicons.svg │ │ ├── ionicons.ttf │ │ ├── ionicons.woff │ │ ├── ionicons.woff2 │ │ ├── roboto-v15-latin-300.eot │ │ ├── roboto-v15-latin-300.svg │ │ ├── roboto-v15-latin-300.ttf │ │ ├── roboto-v15-latin-300.woff │ │ ├── roboto-v15-latin-300.woff2 │ │ ├── roboto-v15-latin-700.eot │ │ ├── roboto-v15-latin-700.svg │ │ ├── roboto-v15-latin-700.ttf │ │ ├── roboto-v15-latin-700.woff │ │ ├── roboto-v15-latin-700.woff2 │ │ ├── roboto-v15-latin-italic.eot │ │ ├── roboto-v15-latin-italic.svg │ │ ├── roboto-v15-latin-italic.ttf │ │ ├── roboto-v15-latin-italic.woff │ │ ├── roboto-v15-latin-italic.woff2 │ │ ├── roboto-v15-latin-regular.eot │ │ ├── roboto-v15-latin-regular.svg │ │ ├── roboto-v15-latin-regular.ttf │ │ ├── roboto-v15-latin-regular.woff │ │ └── roboto-v15-latin-regular.woff2 │ ├── images │ │ ├── compodoc-vectorise-inverted.png │ │ ├── compodoc-vectorise-inverted.svg │ │ ├── compodoc-vectorise.png │ │ ├── compodoc-vectorise.svg │ │ └── favicon.ico │ ├── index.html │ ├── injectables │ │ ├── ODataClient.html │ │ └── ODataServiceFactory.html │ ├── interfaces │ │ ├── FieldParser.html │ │ ├── ODataApiConfigOptions.html │ │ ├── ODataCache.html │ │ ├── ODataCacheEntry.html │ │ ├── ODataVersionHelper.html │ │ ├── Parser.html │ │ ├── ParserOptions.html │ │ ├── PassedInitialConfig.html │ │ ├── Renderable.html │ │ ├── ResponseJson.html │ │ ├── ResponseOptions.html │ │ ├── Schema.html │ │ └── StructuredTypeFieldOptions.html │ ├── js │ │ ├── compodoc.js │ │ ├── lazy-load-graphs.js │ │ ├── libs │ │ │ ├── EventDispatcher.js │ │ │ ├── bootstrap-native.js │ │ │ ├── clipboard.min.js │ │ │ ├── custom-elements-es5-adapter.js │ │ │ ├── custom-elements.min.js │ │ │ ├── d3.v3.min.js │ │ │ ├── deep-iterator.js │ │ │ ├── es6-shim.min.js │ │ │ ├── htmlparser.js │ │ │ ├── innersvg.js │ │ │ ├── lit-html.js │ │ │ ├── prism.js │ │ │ ├── promise.min.js │ │ │ ├── svg-pan-zoom.min.js │ │ │ ├── tablesort.min.js │ │ │ ├── tablesort.number.min.js │ │ │ ├── vis.min.js │ │ │ └── zepto.min.js │ │ ├── menu-wc.js │ │ ├── menu-wc_es5.js │ │ ├── menu.js │ │ ├── routes.js │ │ ├── search │ │ │ ├── lunr.min.js │ │ │ ├── search-lunr.js │ │ │ ├── search.js │ │ │ └── search_index.js │ │ ├── sourceCode.js │ │ ├── svg-pan-zoom.controls.js │ │ ├── tabs.js │ │ └── tree.js │ ├── license.html │ ├── miscellaneous │ │ ├── enumerations.html │ │ ├── functions.html │ │ ├── typealiases.html │ │ └── variables.html │ ├── modules.html │ ├── modules │ │ └── ODataModule.html │ ├── overview.html │ ├── properties.html │ └── styles │ │ ├── bootstrap-card.css │ │ ├── bootstrap.min.css │ │ ├── compodoc.css │ │ ├── dark.css │ │ ├── ionicons.min.css │ │ ├── laravel.css │ │ ├── material.css │ │ ├── original.css │ │ ├── postmark.css │ │ ├── prism.css │ │ ├── readthedocs.css │ │ ├── reset.css │ │ ├── stripe.css │ │ ├── style.css │ │ ├── tablesort.css │ │ └── vagrant.css ├── apigentool.md ├── models.md ├── queries.md ├── schema.md ├── schemaless.md └── schematics.md ├── package-lock.json ├── package.json ├── projects └── angular-odata │ ├── README.md │ ├── karma.config.js │ ├── ng-package.json │ ├── package-lock.json │ ├── package.json │ ├── schematics │ ├── apigen │ │ ├── angular │ │ │ ├── api-config.ts │ │ │ ├── base.ts │ │ │ ├── entity.ts │ │ │ ├── enum.ts │ │ │ ├── import.ts │ │ │ ├── module.ts │ │ │ └── service.ts │ │ ├── files │ │ │ ├── api-config │ │ │ │ └── __fileName__.ts │ │ │ ├── entity │ │ │ │ └── __fileName__.ts │ │ │ ├── entitycontainer-service │ │ │ │ └── __fileName__.ts │ │ │ ├── entityset-service │ │ │ │ └── __fileName__.ts │ │ │ ├── enum │ │ │ │ └── __fileName__.ts │ │ │ ├── index │ │ │ │ └── __fileName__.ts │ │ │ ├── metadata │ │ │ │ └── metadata.json │ │ │ ├── module │ │ │ │ └── __fileName__.ts │ │ │ └── singleton-service │ │ │ │ └── __fileName__.ts │ │ ├── index.ts │ │ ├── metadata │ │ │ ├── csdl │ │ │ │ ├── csdl-annotation.ts │ │ │ │ ├── csdl-entity-container.ts │ │ │ │ ├── csdl-entity-set.ts │ │ │ │ ├── csdl-enum-type.ts │ │ │ │ ├── csdl-function-action.ts │ │ │ │ ├── csdl-navigation-property-binding.ts │ │ │ │ ├── csdl-reference.ts │ │ │ │ ├── csdl-schema.ts │ │ │ │ ├── csdl-singleton.ts │ │ │ │ ├── csdl-structural-property.ts │ │ │ │ ├── csdl-structured-type.ts │ │ │ │ └── csdl-type-definition.ts │ │ │ ├── index.ts │ │ │ ├── metadata.ts │ │ │ └── parser.ts │ │ ├── schema.json │ │ ├── schema.ts │ │ └── utils.ts │ ├── collection.json │ ├── ng-add │ │ └── index.ts │ └── random.ts │ ├── src │ ├── lib │ │ ├── annotations.spec.ts │ │ ├── annotations.ts │ │ ├── api.ts │ │ ├── cache │ │ │ ├── cache.ts │ │ │ ├── index.ts │ │ │ ├── memory.ts │ │ │ └── storage.ts │ │ ├── client.spec.ts │ │ ├── client.ts │ │ ├── constants.ts │ │ ├── helper.ts │ │ ├── index.ts │ │ ├── loaders.ts │ │ ├── metadata │ │ │ ├── csdl │ │ │ │ ├── csdl-annotation.ts │ │ │ │ ├── csdl-entity-container.ts │ │ │ │ ├── csdl-entity-set.ts │ │ │ │ ├── csdl-enum-type.ts │ │ │ │ ├── csdl-function-action.ts │ │ │ │ ├── csdl-navigation-property-binding.ts │ │ │ │ ├── csdl-reference.ts │ │ │ │ ├── csdl-schema.ts │ │ │ │ ├── csdl-singleton.ts │ │ │ │ ├── csdl-structural-property.ts │ │ │ │ ├── csdl-structured-type.ts │ │ │ │ └── csdl-type-definition.ts │ │ │ ├── index.ts │ │ │ ├── metadata.ts │ │ │ └── parser.ts │ │ ├── models │ │ │ ├── collection.ts │ │ │ ├── index.ts │ │ │ ├── model.ts │ │ │ └── options.ts │ │ ├── module.ts │ │ ├── options.ts │ │ ├── resources │ │ │ ├── index.ts │ │ │ ├── options.ts │ │ │ ├── path │ │ │ │ ├── handlers.ts │ │ │ │ ├── index.ts │ │ │ │ ├── segments.spec.ts │ │ │ │ └── segments.ts │ │ │ ├── query │ │ │ │ ├── builder.spec.ts │ │ │ │ ├── builder.ts │ │ │ │ ├── expressions │ │ │ │ │ ├── apply.ts │ │ │ │ │ ├── base.ts │ │ │ │ │ ├── compute.spec.ts │ │ │ │ │ ├── compute.ts │ │ │ │ │ ├── count.spec.ts │ │ │ │ │ ├── count.ts │ │ │ │ │ ├── expand.spec.ts │ │ │ │ │ ├── expand.ts │ │ │ │ │ ├── filter.spec.ts │ │ │ │ │ ├── filter.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── orderby.spec.ts │ │ │ │ │ ├── orderby.ts │ │ │ │ │ ├── search.spec.ts │ │ │ │ │ ├── search.ts │ │ │ │ │ ├── select.spec.ts │ │ │ │ │ ├── select.ts │ │ │ │ │ └── syntax.ts │ │ │ │ ├── handlers.ts │ │ │ │ ├── index.ts │ │ │ │ ├── options.spec.ts │ │ │ │ └── options.ts │ │ │ ├── request.ts │ │ │ ├── resource.spec.ts │ │ │ ├── resource.ts │ │ │ ├── response.ts │ │ │ └── types │ │ │ │ ├── action.ts │ │ │ │ ├── batch.ts │ │ │ │ ├── count.ts │ │ │ │ ├── entity-set.ts │ │ │ │ ├── entity.ts │ │ │ │ ├── function.ts │ │ │ │ ├── index.ts │ │ │ │ ├── media.ts │ │ │ │ ├── metadata.ts │ │ │ │ ├── navigation-property.ts │ │ │ │ ├── options.ts │ │ │ │ ├── property.ts │ │ │ │ ├── reference.ts │ │ │ │ ├── singleton.ts │ │ │ │ └── value.ts │ │ ├── schema │ │ │ ├── annotation.ts │ │ │ ├── callable.ts │ │ │ ├── element.ts │ │ │ ├── entity-container.ts │ │ │ ├── entity-set.ts │ │ │ ├── enum-type.ts │ │ │ ├── index.ts │ │ │ ├── parsers │ │ │ │ ├── callable.ts │ │ │ │ ├── edm.ts │ │ │ │ ├── enum-type.ts │ │ │ │ ├── index.ts │ │ │ │ ├── parser.spec.ts │ │ │ │ └── structured-type.ts │ │ │ ├── schema.ts │ │ │ ├── singleton.ts │ │ │ └── structured-type.ts │ │ ├── services │ │ │ ├── base.ts │ │ │ ├── entity-set.ts │ │ │ ├── entity.ts │ │ │ ├── factory.ts │ │ │ ├── index.ts │ │ │ └── singleton.ts │ │ ├── settings.ts │ │ ├── trippin.spec.ts │ │ ├── types.ts │ │ └── utils │ │ │ ├── arraybuffers.ts │ │ │ ├── arrays.spec.ts │ │ │ ├── arrays.ts │ │ │ ├── dates.ts │ │ │ ├── durations.ts │ │ │ ├── enums.spec.ts │ │ │ ├── enums.ts │ │ │ ├── http.spec.ts │ │ │ ├── http.ts │ │ │ ├── index.ts │ │ │ ├── objects.ts │ │ │ ├── odata.ts │ │ │ ├── strings.spec.ts │ │ │ ├── strings.ts │ │ │ ├── types.ts │ │ │ └── urls.ts │ └── public-api.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.schematics.json │ └── tsconfig.spec.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | ij_typescript_use_double_quotes = false 14 | 15 | [*.md] 16 | max_line_length = off 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [diegomvh] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '23 19 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | branches: [main] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | matrix: 18 | node-version: [18.x, 20.x] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Use Node.js ${{ matrix.node-version }} 23 | uses: actions/setup-node@v1 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | - run: npm ci 27 | - run: npm run build --if-present 28 | - run: npm test 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | 12 | ### Changed 13 | 14 | ### Removed 15 | 16 | ## [0.128.0] - 2024-06-03 17 | 18 | ### Added 19 | 20 | ### Fixed 21 | 22 | ### Changed 23 | 24 | ### Removed 25 | 26 | [unreleased]: https://github.com/diegomvh/angular-odata/compare/v0.128.0...main 27 | [0.128.0]: https://github.com/diegomvh/angular-odata/compare/v0.127.0...v0.128.0 28 | [0.127.0]: https://github.com/diegomvh/angular-odata/compare/v0.126.0...v0.127.0 29 | [0.126.0]: https://github.com/diegomvh/angular-odata/compare/v0.125.0...v0.126.0 30 | [0.125.0]: https://github.com/diegomvh/angular-odata/compare/v0.124.0...v0.125.0 31 | [0.124.0]: https://github.com/diegomvh/angular-odata/compare/v0.123.0...v0.124.0 32 | [0.123.0]: https://github.com/diegomvh/angular-odata/compare/v0.122.0...v0.123.0 33 | [0.122.0]: https://github.com/diegomvh/angular-odata/compare/v0.121.0...v0.122.0 34 | [0.121.0]: https://github.com/diegomvh/angular-odata/compare/v0.120.0...v0.121.0 35 | [0.120.0]: https://github.com/diegomvh/angular-odata/compare/v0.115.0...v0.120.0 36 | [0.115.0]: https://github.com/diegomvh/angular-odata/compare/v0.110.0...v0.115.0 37 | [0.110.0]: https://github.com/diegomvh/angular-odata/compare/v0.105.0...v0.110.0 38 | [0.105.0]: https://github.com/diegomvh/angular-odata/compare/v0.102.0...v0.105.0 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Diego van Haaster 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Angular OData

2 | 3 |

4 | A fluent API for querying, creating, updating and deleting OData resources in Angular. 5 |
6 |

7 | 8 |

9 | Contributing 10 | · 11 | Documentation 12 | · 13 | Demo 14 |
15 |
16 |

17 | 18 |

19 | 20 | CI status 21 |   22 | 23 | Angular OData on npm 24 | 25 |

26 | 27 |
28 | 29 | ## Installation 30 | 31 | Install from npm: 32 | 33 | ```bash 34 | npm i angular-odata 35 | ``` 36 | 37 | ## OData Version 38 | 39 | The library works mainly with OData Version 4, however, it incorporates basic support for versions 3 and 2. 40 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-odata": { 7 | "projectType": "library", 8 | "root": "projects/angular-odata", 9 | "sourceRoot": "projects/angular-odata/src", 10 | "prefix": "lib", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-angular:ng-packagr", 14 | "options": { 15 | "project": "projects/angular-odata/ng-package.json" 16 | }, 17 | "configurations": { 18 | "production": { 19 | "tsConfig": "projects/angular-odata/tsconfig.lib.prod.json" 20 | }, 21 | "development": { 22 | "tsConfig": "projects/angular-odata/tsconfig.lib.json" 23 | } 24 | }, 25 | "defaultConfiguration": "production" 26 | }, 27 | "test": { 28 | "builder": "@angular-devkit/build-angular:karma", 29 | "options": { 30 | "tsConfig": "projects/angular-odata/tsconfig.spec.json", 31 | "karmaConfig": "projects/angular-odata/karma.config.js", 32 | "polyfills": [ 33 | "zone.js", 34 | "zone.js/testing" 35 | ] 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Table of contents 2 | 3 | - [Schema](./schema.md) 4 | - [OData API Generator](./apigentool.md) 5 | - [Schematics](./schematics.md) 6 | - [Usage](./schema.md#usage) 7 | - [Schemaless](./schemaless.md) 8 | - [Usage](./schemaless.md#usage) 9 | - [Queries](./queries.md#query-builder) 10 | - [Models](./models.md) 11 | - [Compodoc](https://diegomvh.github.io/angular-odata/docs/api/) -------------------------------------------------------------------------------- /docs/api/fonts/ionicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/ionicons.eot -------------------------------------------------------------------------------- /docs/api/fonts/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/ionicons.ttf -------------------------------------------------------------------------------- /docs/api/fonts/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/ionicons.woff -------------------------------------------------------------------------------- /docs/api/fonts/ionicons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/ionicons.woff2 -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-300.eot -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-300.ttf -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-300.woff -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-300.woff2 -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-700.eot -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-700.ttf -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-700.woff -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-700.woff2 -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-italic.eot -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-italic.ttf -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-italic.woff -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-italic.woff2 -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-regular.eot -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-regular.ttf -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-regular.woff -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/fonts/roboto-v15-latin-regular.woff2 -------------------------------------------------------------------------------- /docs/api/images/compodoc-vectorise-inverted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/images/compodoc-vectorise-inverted.png -------------------------------------------------------------------------------- /docs/api/images/compodoc-vectorise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/images/compodoc-vectorise.png -------------------------------------------------------------------------------- /docs/api/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/56d35c621d92f39d299726f65a2db05f9d3b2cdc/docs/api/images/favicon.ico -------------------------------------------------------------------------------- /docs/api/js/compodoc.js: -------------------------------------------------------------------------------- 1 | var compodoc = { 2 | EVENTS: { 3 | READY: 'compodoc.ready', 4 | SEARCH_READY: 'compodoc.search.ready' 5 | } 6 | }; 7 | 8 | Object.assign( compodoc, EventDispatcher.prototype ); 9 | 10 | document.addEventListener('DOMContentLoaded', function() { 11 | compodoc.dispatchEvent({ 12 | type: compodoc.EVENTS.READY 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /docs/api/js/lazy-load-graphs.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | var lazyGraphs = [].slice.call(document.querySelectorAll('[lazy]')); 3 | var active = false; 4 | 5 | var lazyLoad = function() { 6 | if (active === false) { 7 | active = true; 8 | 9 | setTimeout(function() { 10 | lazyGraphs.forEach(function(lazyGraph) { 11 | if ( 12 | lazyGraph.getBoundingClientRect().top <= window.innerHeight && 13 | lazyGraph.getBoundingClientRect().bottom >= 0 && 14 | getComputedStyle(lazyGraph).display !== 'none' 15 | ) { 16 | lazyGraph.data = lazyGraph.getAttribute('lazy'); 17 | lazyGraph.removeAttribute('lazy'); 18 | 19 | lazyGraphs = lazyGraphs.filter(function(image) { return image !== lazyGraph}); 20 | 21 | if (lazyGraphs.length === 0) { 22 | document.removeEventListener('scroll', lazyLoad); 23 | window.removeEventListener('resize', lazyLoad); 24 | window.removeEventListener('orientationchange', lazyLoad); 25 | } 26 | } 27 | }); 28 | 29 | active = false; 30 | }, 200); 31 | } 32 | }; 33 | 34 | // initial load 35 | lazyLoad(); 36 | 37 | var container = document.querySelector('.container-fluid.modules'); 38 | if (container) { 39 | container.addEventListener('scroll', lazyLoad); 40 | window.addEventListener('resize', lazyLoad); 41 | window.addEventListener('orientationchange', lazyLoad); 42 | } 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /docs/api/js/libs/EventDispatcher.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | var EventDispatcher=function(){};Object.assign(EventDispatcher.prototype,{addEventListener:function(i,t){void 0===this._listeners&&(this._listeners={});var e=this._listeners;void 0===e[i]&&(e[i]=[]),-1===e[i].indexOf(t)&&e[i].push(t)},hasEventListener:function(i,t){if(void 0===this._listeners)return!1;var e=this._listeners;return void 0!==e[i]&&-1!==e[i].indexOf(t)},removeEventListener:function(i,t){if(void 0!==this._listeners){var e=this._listeners[i];if(void 0!==e){var s=e.indexOf(t);-1!==s&&e.splice(s,1)}}},dispatchEvent:function(i){if(void 0!==this._listeners){var t=this._listeners[i.type];if(void 0!==t){i.target=this;var e=[],s=0,n=t.length;for(s=0;s",">"));else if(1==i){if(r.push("<",e.tagName),e.hasAttributes())for(var n=e.attributes,s=0,o=n.length;s");for(var h=e.childNodes,s=0,o=h.length;s")}else r.push("/>")}else{if(8!=i)throw"Error serializing XML. Unhandled node of type: "+i;r.push("\x3c!--",e.nodeValue,"--\x3e")}};Object.defineProperty(e.prototype,"innerHTML",{get:function(){for(var e=[],r=this.firstChild;r;)t(r,e),r=r.nextSibling;return e.join("")},set:function(e){for(;this.firstChild;)this.removeChild(this.firstChild);try{var t=new DOMParser;t.async=!1,sXML=""+e+"";for(var r=t.parseFromString(sXML,"text/xml").documentElement.firstChild;r;)this.appendChild(this.ownerDocument.importNode(r,!0)),r=r.nextSibling}catch(e){throw new Error("Error parsing XML string")}}})}}((0,eval)("this").SVGElement); -------------------------------------------------------------------------------- /docs/api/js/libs/promise.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2013 (c) Pierre Duquesne 3 | * Licensed under the New BSD License. 4 | * https://github.com/stackp/promisejs 5 | */ 6 | (function(a){function b(){this._callbacks=[];}b.prototype.then=function(a,c){var d;if(this._isdone)d=a.apply(c,this.result);else{d=new b();this._callbacks.push(function(){var b=a.apply(c,arguments);if(b&&typeof b.then==='function')b.then(d.done,d);});}return d;};b.prototype.done=function(){this.result=arguments;this._isdone=true;for(var a=0;a=300)&&j.status!==304);h.done(a,j.responseText,j);}};j.send(k);return h;}function h(a){return function(b,c,d){return g(a,b,c,d);};}var i={Promise:b,join:c,chain:d,ajax:g,get:h('GET'),post:h('POST'),put:h('PUT'),del:h('DELETE'),ENOXHR:1,ETIMEOUT:2,ajaxTimeout:0};if(typeof define==='function'&&define.amd)define(function(){return i;});else a.promise=i;})(this); -------------------------------------------------------------------------------- /docs/api/js/libs/tablesort.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * tablesort v5.2.1 (2021-10-30) 3 | * http://tristen.ca/tablesort/demo/ 4 | * Copyright (c) 2021 ; Licensed MIT 5 | */ 6 | !function(){function a(b,c){if(!(this instanceof a))return new a(b,c);if(!b||"TABLE"!==b.tagName)throw new Error("Element must be a table");this.init(b,c||{})}var b=[],c=function(a){var b;return window.CustomEvent&&"function"==typeof window.CustomEvent?b=new CustomEvent(a):(b=document.createEvent("CustomEvent"),b.initCustomEvent(a,!1,!1,void 0)),b},d=function(a,b){return a.getAttribute(b.sortAttribute||"data-sort")||a.textContent||a.innerText||""},e=function(a,b){return a=a.trim().toLowerCase(),b=b.trim().toLowerCase(),a===b?0:a0)if(a.tHead&&a.tHead.rows.length>0){for(e=0;e0&&n.push(m),o++;if(!n)return}for(o=0;o 0) { 9 | tabs = tabs[0].querySelectorAll('li'); 10 | for (var i = 0; i < tabs.length; i++) { 11 | tabs[i].addEventListener('click', updateAddress); 12 | var linkTag = tabs[i].querySelector('a'); 13 | if (location.hash !== '') { 14 | var currentHash = location.hash.substr(1); 15 | if (currentHash === linkTag.dataset.link) { 16 | linkTag.click(); 17 | } 18 | } 19 | } 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /docs/api/styles/dark.css: -------------------------------------------------------------------------------- 1 | body.dark { 2 | background: #212121; 3 | color: #fafafa; 4 | } 5 | 6 | .dark code { 7 | color: #e09393; 8 | } 9 | 10 | .dark a, 11 | .dark .menu ul.list li a.active { 12 | color: #7fc9ff; 13 | } 14 | 15 | .dark .menu { 16 | background: #212121; 17 | border-right: 1px solid #444; 18 | } 19 | 20 | .dark .menu ul.list li a { 21 | color: #fafafa; 22 | } 23 | 24 | .dark .menu ul.list li.divider { 25 | background: #444; 26 | } 27 | 28 | .dark .xs-menu ul.list li:nth-child(2) { 29 | margin: 0; 30 | background: none; 31 | } 32 | 33 | .dark .menu ul.list li:nth-child(2) { 34 | margin: 0; 35 | background: none; 36 | } 37 | 38 | .dark #book-search-input { 39 | background: #212121; 40 | border-top: 1px solid #444; 41 | border-bottom: 1px solid #444; 42 | color: #fafafa; 43 | } 44 | 45 | .dark .table.metadata > tbody > tr:hover { 46 | color: #555; 47 | } 48 | 49 | .dark .table-bordered { 50 | border: 1px solid #444; 51 | } 52 | 53 | .dark .table-bordered > tbody > tr > td, 54 | .dark .table-bordered > tbody > tr > th, 55 | .dark .table-bordered > tfoot > tr > td, 56 | .dark .table-bordered > tfoot > tr > th, 57 | .dark .table-bordered > thead > tr > td, 58 | .dark .table-bordered > thead > tr > th { 59 | border: 1px solid #444; 60 | } 61 | 62 | .dark .coverage a, 63 | .dark .coverage-count { 64 | color: #fafafa; 65 | } 66 | 67 | .dark .coverage-header { 68 | color: black; 69 | } 70 | 71 | .dark .routes svg text, 72 | .dark .routes svg a { 73 | fill: white; 74 | } 75 | .dark .routes svg rect { 76 | fill: #212121 !important; 77 | } 78 | 79 | .dark .navbar-default, 80 | .dark .btn-default { 81 | background-color: black; 82 | border-color: #444; 83 | color: #fafafa; 84 | } 85 | 86 | .dark .navbar-default .navbar-brand { 87 | color: #fafafa; 88 | } 89 | 90 | .dark .overview .card, 91 | .dark .modules .card { 92 | background: #171717; 93 | color: #fafafa; 94 | border: 1px solid #444; 95 | } 96 | .dark .overview .card a { 97 | color: #fafafa; 98 | } 99 | 100 | .dark .modules .card-header { 101 | background: none; 102 | border-bottom: 1px solid #444; 103 | } 104 | 105 | .dark .module .list-group-item { 106 | background: none; 107 | border: 1px solid #444; 108 | } 109 | 110 | .dark .container-fluid.module h3 a { 111 | color: #337ab7; 112 | } 113 | 114 | .dark table.params thead { 115 | background: #484848; 116 | color: #fafafa; 117 | } 118 | 119 | .dark .content table { 120 | --bs-table-color: #fafafa; 121 | } 122 | -------------------------------------------------------------------------------- /docs/api/styles/laravel.css: -------------------------------------------------------------------------------- 1 | .nav-tabs > li > a { 2 | text-decoration: none; 3 | } 4 | 5 | .navbar-default .navbar-brand { 6 | color: #f4645f; 7 | text-decoration: none; 8 | font-size: 16px; 9 | } 10 | 11 | .menu ul.list li a[data-type='chapter-link'], 12 | .menu ul.list li.chapter .simple { 13 | color: #525252; 14 | border-bottom: 1px dashed rgba(0, 0, 0, 0.1); 15 | } 16 | 17 | .content h1, 18 | .content h2, 19 | .content h3, 20 | .content h4, 21 | .content h5 { 22 | color: #292e31; 23 | font-weight: normal; 24 | } 25 | 26 | .content { 27 | color: #4c555a; 28 | } 29 | 30 | a { 31 | color: #f4645f; 32 | text-decoration: underline; 33 | } 34 | a:hover { 35 | color: #f1362f; 36 | } 37 | 38 | .menu ul.list li:nth-child(2) { 39 | margin-top: 0; 40 | } 41 | 42 | .menu ul.list li.title a { 43 | color: #f4645f; 44 | text-decoration: none; 45 | font-size: 16px; 46 | } 47 | 48 | .menu ul.list li a { 49 | color: #f4645f; 50 | text-decoration: none; 51 | } 52 | .menu ul.list li a.active { 53 | color: #f4645f; 54 | font-weight: bold; 55 | } 56 | 57 | code { 58 | box-sizing: border-box; 59 | display: inline-block; 60 | padding: 0 5px; 61 | background: #f0f2f1; 62 | border-radius: 3px; 63 | color: #b93d6a; 64 | font-size: 13px; 65 | line-height: 20px; 66 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.125); 67 | } 68 | 69 | pre { 70 | margin: 0; 71 | padding: 12px 12px; 72 | background: rgba(238, 238, 238, 0.35); 73 | border-radius: 3px; 74 | font-size: 13px; 75 | line-height: 1.5em; 76 | font-weight: 500; 77 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.125); 78 | } 79 | 80 | .dark body { 81 | color: #fafafa; 82 | } 83 | .dark .content h1, 84 | .dark .content h2, 85 | .dark .content h3, 86 | .dark .content h4, 87 | .dark .content h5 { 88 | color: #fafafa; 89 | } 90 | 91 | .dark code { 92 | background: none; 93 | } 94 | 95 | .dark .content { 96 | color: #fafafa; 97 | } 98 | 99 | .dark .menu ul.list li a[data-type='chapter-link'], 100 | .dark .menu ul.list li.chapter .simple { 101 | color: #fafafa; 102 | } 103 | 104 | .dark .menu ul.list li.title a { 105 | color: #fafafa; 106 | } 107 | 108 | .dark .menu ul.list li a { 109 | color: #fafafa; 110 | } 111 | .dark .menu ul.list li a.active { 112 | color: #7fc9ff; 113 | } 114 | -------------------------------------------------------------------------------- /docs/api/styles/material.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | background: none; 3 | } 4 | 5 | a:hover { 6 | text-decoration: none; 7 | } 8 | 9 | /** LINK **/ 10 | 11 | .menu ul.list li a { 12 | text-decoration: none; 13 | } 14 | 15 | .menu ul.list li a:hover, 16 | .menu ul.list li.chapter .simple:hover { 17 | background-color: #f8f9fa; 18 | text-decoration: none; 19 | } 20 | 21 | #book-search-input { 22 | margin-bottom: 0; 23 | } 24 | 25 | .menu ul.list li.divider { 26 | margin-top: 0; 27 | background: #e9ecef; 28 | } 29 | 30 | .menu .title:hover { 31 | background-color: #f8f9fa; 32 | } 33 | 34 | /** CARD **/ 35 | 36 | .card { 37 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 38 | 0 1px 5px 0 rgba(0, 0, 0, 0.12); 39 | border-radius: 0.125rem; 40 | border: 0; 41 | margin-top: 1px; 42 | } 43 | 44 | .card-header { 45 | background: none; 46 | } 47 | 48 | /** BUTTON **/ 49 | 50 | .btn { 51 | border-radius: 0.125rem; 52 | } 53 | 54 | /** NAV BAR **/ 55 | 56 | .nav { 57 | border: 0; 58 | } 59 | .nav-tabs > li > a { 60 | border: 0; 61 | border-bottom: 0.214rem solid transparent; 62 | color: rgba(0, 0, 0, 0.54); 63 | margin-right: 0; 64 | } 65 | .nav-tabs > li.active > a, 66 | .nav-tabs > li.active > a:focus, 67 | .nav-tabs > li.active > a:hover { 68 | color: rgba(0, 0, 0, 0.87); 69 | border-top: 0; 70 | border-left: 0; 71 | border-right: 0; 72 | border-bottom: 0.214rem solid transparent; 73 | border-color: #008cff; 74 | font-weight: bold; 75 | } 76 | .nav > li > a:focus, 77 | .nav > li > a:hover { 78 | background: none; 79 | } 80 | 81 | /** LIST **/ 82 | 83 | .list-group-item:first-child { 84 | border-top-left-radius: 0.125rem; 85 | border-top-right-radius: 0.125rem; 86 | } 87 | .list-group-item:last-child { 88 | border-bottom-left-radius: 0.125rem; 89 | border-bottom-right-radius: 0.125rem; 90 | } 91 | 92 | /** MISC **/ 93 | 94 | .modifier { 95 | border-radius: 0.125rem; 96 | } 97 | 98 | pre[class*='language-'] { 99 | border-radius: 0.125rem; 100 | } 101 | 102 | /** TABLE **/ 103 | 104 | .table-hover > tbody > tr:hover { 105 | background: rgba(0, 0, 0, 0.075); 106 | } 107 | 108 | table.params thead { 109 | background: none; 110 | } 111 | table.params thead td { 112 | color: rgba(0, 0, 0, 0.54); 113 | font-weight: bold; 114 | } 115 | 116 | .dark .menu .title:hover { 117 | background-color: #2d2d2d; 118 | } 119 | .dark .menu ul.list li a:hover, 120 | .dark .menu ul.list li.chapter .simple:hover { 121 | background-color: #2d2d2d; 122 | } 123 | .dark .nav-tabs > li:not(.active) > a { 124 | color: #fafafa; 125 | } 126 | .dark table.params thead { 127 | background: #484848; 128 | } 129 | .dark table.params thead td { 130 | color: #fafafa; 131 | } 132 | -------------------------------------------------------------------------------- /docs/api/styles/original.css: -------------------------------------------------------------------------------- 1 | .navbar-default .navbar-brand, 2 | .menu ul.list li.title { 3 | font-weight: bold; 4 | color: #3c3c3c; 5 | padding-bottom: 5px; 6 | } 7 | 8 | .menu ul.list li a[data-type='chapter-link'], 9 | .menu ul.list li.chapter .simple { 10 | font-weight: bold; 11 | font-size: 14px; 12 | } 13 | 14 | .menu ul.list li a[href='./routes.html'] { 15 | border-bottom: none; 16 | } 17 | 18 | .menu ul.list > li:nth-child(2) { 19 | display: none; 20 | } 21 | 22 | .menu ul.list li.chapter ul.links { 23 | background: #fff; 24 | padding-left: 0; 25 | } 26 | 27 | .menu ul.list li.chapter ul.links li { 28 | border-bottom: 1px solid #ddd; 29 | padding-left: 20px; 30 | } 31 | 32 | .menu ul.list li.chapter ul.links li:last-child { 33 | border-bottom: none; 34 | } 35 | 36 | .menu ul.list li a.active { 37 | color: #337ab7; 38 | font-weight: bold; 39 | } 40 | 41 | #book-search-input { 42 | margin-bottom: 0; 43 | border-bottom: none; 44 | } 45 | .menu ul.list li.divider { 46 | margin: 0; 47 | } 48 | 49 | .dark .menu ul.list li.chapter ul.links { 50 | background: none; 51 | } 52 | -------------------------------------------------------------------------------- /docs/api/styles/readthedocs.css: -------------------------------------------------------------------------------- 1 | .navbar-default { 2 | background: #2980b9; 3 | border: none; 4 | } 5 | 6 | .navbar-default .navbar-brand { 7 | color: #fcfcfc; 8 | } 9 | 10 | .menu { 11 | background: #343131; 12 | color: #fcfcfc; 13 | } 14 | 15 | .menu ul.list li a { 16 | color: #fcfcfc; 17 | } 18 | 19 | .menu ul.list li.title { 20 | background: #2980b9; 21 | padding-bottom: 5px; 22 | } 23 | 24 | .menu ul.list li:nth-child(2) { 25 | margin-top: 0; 26 | } 27 | 28 | .menu ul.list li.chapter a, 29 | .menu ul.list li.chapter .simple { 30 | color: #555; 31 | text-transform: uppercase; 32 | text-decoration: none; 33 | } 34 | 35 | .menu ul.list li.chapter ul.links a { 36 | color: #b3b3b3; 37 | text-transform: none; 38 | padding-left: 35px; 39 | } 40 | 41 | .menu ul.list li.chapter ul.links a:hover { 42 | background: #4e4a4a; 43 | } 44 | 45 | .menu ul.list li.chapter a.active, 46 | .menu ul.list li.chapter ul.links a.active { 47 | color: #0099e5; 48 | } 49 | 50 | .menu ul.list li.chapter ul.links { 51 | padding-left: 0; 52 | } 53 | 54 | .menu ul.list li.divider { 55 | background: rgba(255, 255, 255, 0.07); 56 | } 57 | 58 | #book-search-input input, 59 | #book-search-input input:focus, 60 | #book-search-input input:hover { 61 | color: #949494; 62 | } 63 | 64 | .copyright { 65 | color: #b3b3b3; 66 | background: #272525; 67 | } 68 | 69 | .content { 70 | background: #fcfcfc; 71 | } 72 | 73 | .content a { 74 | color: #2980b9; 75 | } 76 | 77 | .content a:hover { 78 | color: #3091d1; 79 | } 80 | 81 | .content a:visited { 82 | color: #9b59b6; 83 | } 84 | 85 | .menu ul.list li:nth-last-child(2) { 86 | background: none; 87 | } 88 | 89 | code { 90 | white-space: nowrap; 91 | max-width: 100%; 92 | background: #fff; 93 | padding: 2px 5px; 94 | color: #e74c3c; 95 | overflow-x: auto; 96 | border-radius: 0; 97 | } 98 | 99 | pre { 100 | white-space: pre; 101 | margin: 0; 102 | padding: 12px 12px; 103 | font-size: 12px; 104 | line-height: 1.5; 105 | display: block; 106 | overflow: auto; 107 | color: #404040; 108 | background: rgba(238, 238, 238, 0.35); 109 | } 110 | 111 | .dark .content { 112 | background: none; 113 | } 114 | .dark code { 115 | background: none; 116 | color: #e09393; 117 | } 118 | -------------------------------------------------------------------------------- /docs/api/styles/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, 7 | body, 8 | div, 9 | span, 10 | applet, 11 | object, 12 | iframe, 13 | h1, 14 | h2, 15 | h3, 16 | h4, 17 | h5, 18 | h6, 19 | p, 20 | blockquote, 21 | pre, 22 | a, 23 | abbr, 24 | acronym, 25 | address, 26 | big, 27 | cite, 28 | code, 29 | del, 30 | dfn, 31 | em, 32 | img, 33 | ins, 34 | kbd, 35 | q, 36 | s, 37 | samp, 38 | small, 39 | strike, 40 | strong, 41 | sub, 42 | sup, 43 | tt, 44 | var, 45 | b, 46 | u, 47 | i, 48 | center, 49 | dl, 50 | dt, 51 | dd, 52 | ol, 53 | ul, 54 | li, 55 | fieldset, 56 | form, 57 | label, 58 | legend, 59 | table, 60 | caption, 61 | tbody, 62 | tfoot, 63 | thead, 64 | tr, 65 | th, 66 | td, 67 | article, 68 | aside, 69 | canvas, 70 | details, 71 | embed, 72 | figure, 73 | figcaption, 74 | footer, 75 | header, 76 | hgroup, 77 | menu, 78 | nav, 79 | output, 80 | ruby, 81 | section, 82 | summary, 83 | time, 84 | mark, 85 | audio, 86 | video { 87 | margin: 0; 88 | padding: 0; 89 | border: 0; 90 | font: inherit; 91 | font-size: 100%; 92 | vertical-align: baseline; 93 | } 94 | /* HTML5 display-role reset for older browsers */ 95 | article, 96 | aside, 97 | details, 98 | figcaption, 99 | figure, 100 | footer, 101 | header, 102 | hgroup, 103 | menu, 104 | nav, 105 | section { 106 | display: block; 107 | } 108 | body { 109 | line-height: 1; 110 | } 111 | ol, 112 | ul { 113 | list-style: none; 114 | } 115 | blockquote, 116 | q { 117 | quotes: none; 118 | } 119 | blockquote:before, 120 | blockquote:after, 121 | q:before, 122 | q:after { 123 | content: ''; 124 | content: none; 125 | } 126 | table { 127 | border-collapse: collapse; 128 | border-spacing: 0; 129 | } 130 | -------------------------------------------------------------------------------- /docs/api/styles/stripe.css: -------------------------------------------------------------------------------- 1 | .navbar-default .navbar-brand { 2 | color: #0099e5; 3 | } 4 | 5 | .menu ul.list li a[data-type='chapter-link'], 6 | .menu ul.list li.chapter .simple { 7 | color: #939da3; 8 | text-transform: uppercase; 9 | } 10 | 11 | .content h1, 12 | .content h2, 13 | .content h3, 14 | .content h4, 15 | .content h5 { 16 | color: #292e31; 17 | font-weight: normal; 18 | } 19 | 20 | .content { 21 | color: #4c555a; 22 | } 23 | 24 | .menu ul.list li.title { 25 | padding: 5px 0; 26 | } 27 | 28 | a { 29 | color: #0099e5; 30 | text-decoration: none; 31 | } 32 | a:hover { 33 | color: #292e31; 34 | text-decoration: none; 35 | } 36 | 37 | .menu ul.list li:nth-child(2) { 38 | margin-top: 0; 39 | } 40 | 41 | .menu ul.list li.title a, 42 | .navbar a { 43 | color: #0099e5; 44 | text-decoration: none; 45 | font-size: 16px; 46 | } 47 | 48 | .menu ul.list li a.active { 49 | color: #0099e5; 50 | } 51 | 52 | code { 53 | box-sizing: border-box; 54 | display: inline-block; 55 | padding: 0 5px; 56 | background: #fafcfc; 57 | border-radius: 4px; 58 | color: #b93d6a; 59 | font-size: 13px; 60 | line-height: 20px; 61 | } 62 | 63 | pre { 64 | margin: 0; 65 | padding: 12px 12px; 66 | background: #272b2d; 67 | border-radius: 5px; 68 | font-size: 13px; 69 | line-height: 1.5em; 70 | font-weight: 500; 71 | } 72 | 73 | .dark body { 74 | color: #fafafa; 75 | } 76 | .dark .content h1, 77 | .dark .content h2, 78 | .dark .content h3, 79 | .dark .content h4, 80 | .dark .content h5 { 81 | color: #fafafa; 82 | } 83 | 84 | .dark code { 85 | background: none; 86 | } 87 | 88 | .dark .content { 89 | color: #fafafa; 90 | } 91 | 92 | .dark .menu ul.list li a[data-type='chapter-link'], 93 | .dark .menu ul.list li.chapter .simple { 94 | color: #fafafa; 95 | } 96 | 97 | .dark .menu ul.list li.title a { 98 | color: #fafafa; 99 | } 100 | 101 | .dark .menu ul.list li a { 102 | color: #fafafa; 103 | } 104 | .dark .menu ul.list li a.active { 105 | color: #7fc9ff; 106 | } 107 | -------------------------------------------------------------------------------- /docs/api/styles/style.css: -------------------------------------------------------------------------------- 1 | @import "./reset.css"; 2 | @import "./bootstrap.min.css"; 3 | @import "./bootstrap-card.css"; 4 | @import "./prism.css"; 5 | @import "./ionicons.min.css"; 6 | @import "./compodoc.css"; 7 | @import "./tablesort.css"; 8 | -------------------------------------------------------------------------------- /docs/api/styles/tablesort.css: -------------------------------------------------------------------------------- 1 | th[role=columnheader]:not(.no-sort) { 2 | cursor: pointer; 3 | } 4 | 5 | th[role=columnheader]:not(.no-sort):after { 6 | content: ''; 7 | float: right; 8 | margin-top: 7px; 9 | border-width: 0 4px 4px; 10 | border-style: solid; 11 | border-color: #404040 transparent; 12 | visibility: visible; 13 | opacity: 1; 14 | -ms-user-select: none; 15 | -webkit-user-select: none; 16 | -moz-user-select: none; 17 | user-select: none; 18 | } 19 | 20 | th[aria-sort=ascending]:not(.no-sort):after { 21 | border-bottom: none; 22 | border-width: 4px 4px 0; 23 | } 24 | 25 | th[aria-sort]:not(.no-sort):after { 26 | visibility: visible; 27 | opacity: 0.4; 28 | } 29 | 30 | th[role=columnheader]:not(.no-sort):hover:after { 31 | visibility: visible; 32 | opacity: 1; 33 | } 34 | -------------------------------------------------------------------------------- /docs/api/styles/vagrant.css: -------------------------------------------------------------------------------- 1 | .navbar-default .navbar-brand { 2 | background: white; 3 | color: #8d9ba8; 4 | } 5 | 6 | .menu .list { 7 | background: #0c5593; 8 | } 9 | 10 | .menu .chapter { 11 | padding: 0 20px; 12 | } 13 | 14 | .menu ul.list li a[data-type='chapter-link'], 15 | .menu ul.list li.chapter .simple { 16 | color: white; 17 | text-transform: uppercase; 18 | border-bottom: 1px solid rgba(255, 255, 255, 0.4); 19 | } 20 | 21 | .content h1, 22 | .content h2, 23 | .content h3, 24 | .content h4, 25 | .content h5 { 26 | color: #292e31; 27 | font-weight: normal; 28 | } 29 | 30 | .content { 31 | color: #4c555a; 32 | } 33 | 34 | a { 35 | color: #0094bf; 36 | text-decoration: underline; 37 | } 38 | a:hover { 39 | color: #f1362f; 40 | } 41 | 42 | .menu ul.list li.title { 43 | background: white; 44 | padding-bottom: 5px; 45 | } 46 | 47 | .menu ul.list li:nth-child(2) { 48 | margin-top: 0; 49 | } 50 | 51 | .menu ul.list li:nth-last-child(2) { 52 | background: none; 53 | } 54 | 55 | .menu ul.list li.title a { 56 | padding: 10px 15px; 57 | } 58 | 59 | .menu ul.list li.title a, 60 | .navbar a { 61 | color: #8d9ba8; 62 | text-decoration: none; 63 | font-size: 16px; 64 | font-weight: 300; 65 | } 66 | 67 | .menu ul.list li a { 68 | color: white; 69 | padding: 10px; 70 | font-weight: 300; 71 | text-decoration: none; 72 | } 73 | .menu ul.list li a.active { 74 | color: white; 75 | font-weight: bold; 76 | } 77 | 78 | .copyright { 79 | color: white; 80 | background: #000; 81 | } 82 | 83 | code { 84 | box-sizing: border-box; 85 | display: inline-block; 86 | padding: 0 5px; 87 | background: rgba(0, 148, 191, 0.1); 88 | border-radius: 3px; 89 | color: #0094bf; 90 | font-size: 13px; 91 | line-height: 20px; 92 | } 93 | 94 | pre { 95 | margin: 0; 96 | padding: 12px 12px; 97 | background: rgba(238, 238, 238, 0.35); 98 | border-radius: 3px; 99 | font-size: 13px; 100 | line-height: 1.5em; 101 | font-weight: 500; 102 | } 103 | 104 | .dark body { 105 | color: #fafafa; 106 | } 107 | .dark .content h1, 108 | .dark .content h2, 109 | .dark .content h3, 110 | .dark .content h4, 111 | .dark .content h5 { 112 | color: #fafafa; 113 | } 114 | 115 | .dark code { 116 | background: none; 117 | } 118 | 119 | .dark .content { 120 | color: #fafafa; 121 | } 122 | 123 | .dark .menu ul.list li.title a, 124 | .dark .navbar a { 125 | color: #8d9ba8; 126 | } 127 | 128 | .dark .menu ul.list li a { 129 | color: #fafafa; 130 | } 131 | -------------------------------------------------------------------------------- /docs/apigentool.md: -------------------------------------------------------------------------------- 1 | # OData API Generator 2 | 3 | Please check also my other related project, [OData Angular Generator](https://github.com/diegomvh/ODataApiGen) 4 | -------------------------------------------------------------------------------- /docs/models.md: -------------------------------------------------------------------------------- 1 | # Models and Collections 2 | 3 | Essentially, the goal is to encapsulate entities with behavior within structures known as models, and to group sets of these entities with behavior into structures called collections. 4 | 5 | If you're familiar with Backbone.js, you'll notice that much of the inspiration for this approach comes from there. -------------------------------------------------------------------------------- /docs/queries.md: -------------------------------------------------------------------------------- 1 | # Queries 2 | 3 | ## Query Builder 4 | 5 | For a deep query customizations the library use `odata-query` and `odata-filter-builder` as a builders. 6 | 7 | - [OData v4 query builder](https://github.com/techniq/odata-query) 8 | - [OData Filter Builder](https://github.com/bodia-uz/odata-filter-builder) 9 | -------------------------------------------------------------------------------- /docs/schema.md: -------------------------------------------------------------------------------- 1 | # Schema 2 | 3 | Use [OData Angular Generator](https://github.com/diegomvh/ODataApiGen) for generate the \Config and the \Module definition. 4 | 5 | Import ODataModule, \Config and \Module into your application module. 6 | Setup ODataModule with \Config and import it along with \Module. 7 | 8 | ```typescript 9 | import { NgModule } from '@angular/core'; 10 | 11 | import { ODataModule } from 'angular-odata'; 12 | import { TripPinConfig, TripPinModule } from './trippin'; 13 | 14 | @NgModule({ 15 | imports: [ 16 | ... 17 | ODataModule.forRoot({ config: TripPinConfig }), 18 | TripPinModule 19 | ] 20 | ... 21 | }) 22 | export class AppModule {} 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/schematics.md: -------------------------------------------------------------------------------- 1 | # Schematics 2 | 3 | The angular-odata library includes schematics that allow generating TypeScript code from OData metadata. 4 | By using the ng generate command along with the appropriate parameters, it is possible to create a module that defines all the types specified in the OData metadata document. 5 | These types are mapped to their TypeScript equivalents as follows: 6 | 7 | **EdmTypes**: mapped to the corresponding primitive types in TypeScript, such as string, number, boolean, etc. 8 | 9 | **EnumType**: converted to a TypeScript enum. 10 | 11 | **EntityType and ComplexType**: converted to TypeScript interfaces. 12 | 13 | **EntitySets**: converted to Angular services. 14 | 15 | **Functions**: added to the generated services. 16 | 17 | **Actions**: added to the generated services. 18 | 19 | 20 | ```bash 21 | ng generate angular-odata:apigen --name=TripPin --metadata=https://services.odata.org/V4/TripPinServiceRW/\$metadata 22 | 23 | ``` 24 | 25 | 26 | ```json 27 | { 28 | "compilerOptions": { 29 | "resolveJsonModule": true 30 | } 31 | } 32 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-odata", 3 | "version": "0.130.0", 4 | "license": "MIT", 5 | "description": "Client side OData typescript library for Angular", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/diegomvh/angular-odata.git" 9 | }, 10 | "keywords": [ 11 | "client", 12 | "odata", 13 | "odata v4", 14 | "odata v3", 15 | "odata v2", 16 | "typescript", 17 | "api", 18 | "@angular", 19 | "angular" 20 | ], 21 | "author": "Diego van Haaster", 22 | "bugs": { 23 | "url": "https://github.com/diegomvh/angular-odata/issues" 24 | }, 25 | "homepage": "https://github.com/diegomvh/angular-odata", 26 | "private": false, 27 | "scripts": { 28 | "ng": "ng", 29 | "start": "ng serve", 30 | "build": "ng test angular-odata && ng build angular-odata --configuration production", 31 | "schematics": "cd projects/angular-odata && npm run build && npm run postbuild", 32 | "link": "npm link angular-odata", 33 | "test": "ng test", 34 | "watch": "ng build --watch --configuration development", 35 | "docs": "compodoc -p projects/angular-odata/tsconfig.lib.json -d docs/api --disableGraph --disableCoverage", 36 | "versioning": "npm version 0.130.0 --allow-same-version && cd projects/angular-odata && npm version 0.130.0 --allow-same-version && cd ../..", 37 | "publish": "npm run build && npm run schematics && npm publish dist/angular-odata/", 38 | "release": "cp README.md projects/angular-odata/README.md && npm run docs && npm run publish", 39 | "prettier": "prettier --write \"projects/angular-odata/**/*.ts\"" 40 | }, 41 | "dependencies": { 42 | "@angular/common": "^19.2.0", 43 | "@angular/compiler": "^19.2.0", 44 | "@angular/core": "^19.2.0", 45 | "@angular/forms": "^19.2.0", 46 | "@angular/platform-browser": "^19.2.0", 47 | "@angular/platform-browser-dynamic": "^19.2.0", 48 | "@angular/router": "^19.2.0", 49 | "rxjs": "~7.8.0", 50 | "tslib": "^2.3.0", 51 | "zone.js": "~0.15.0" 52 | }, 53 | "devDependencies": { 54 | "@angular-devkit/build-angular": "^19.2.13", 55 | "@angular/cli": "^19.2.13", 56 | "@angular/compiler-cli": "^19.2.0", 57 | "@compodoc/compodoc": "^1.1.26", 58 | "@types/jasmine": "~5.1.0", 59 | "@types/jsdom": "^21.1.7", 60 | "jasmine-core": "~5.6.0", 61 | "jsdom": "^26.1.0", 62 | "karma": "~6.4.0", 63 | "karma-chrome-launcher": "~3.2.0", 64 | "karma-coverage": "~2.2.0", 65 | "karma-jasmine": "~5.1.0", 66 | "karma-jasmine-html-reporter": "~2.1.0", 67 | "ng-packagr": "^19.2.0", 68 | "prettier": "^3.5.3", 69 | "typescript": "~5.7.2" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /projects/angular-odata/README.md: -------------------------------------------------------------------------------- 1 | # AngularOdata 2 | 3 | This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.0. 4 | 5 | ## Code scaffolding 6 | 7 | Angular CLI includes powerful code scaffolding tools. To generate a new component, run: 8 | 9 | ```bash 10 | ng generate component component-name 11 | ``` 12 | 13 | For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: 14 | 15 | ```bash 16 | ng generate --help 17 | ``` 18 | 19 | ## Building 20 | 21 | To build the library, run: 22 | 23 | ```bash 24 | ng build angular-odata 25 | ``` 26 | 27 | This command will compile your project, and the build artifacts will be placed in the `dist/` directory. 28 | 29 | ### Publishing the Library 30 | 31 | Once the project is built, you can publish your library by following these steps: 32 | 33 | 1. Navigate to the `dist` directory: 34 | ```bash 35 | cd dist/angular-odata 36 | ``` 37 | 38 | 2. Run the `npm publish` command to publish your library to the npm registry: 39 | ```bash 40 | npm publish 41 | ``` 42 | 43 | ## Running unit tests 44 | 45 | To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: 46 | 47 | ```bash 48 | ng test 49 | ``` 50 | 51 | ## Running end-to-end tests 52 | 53 | For end-to-end (e2e) testing, run: 54 | 55 | ```bash 56 | ng e2e 57 | ``` 58 | 59 | Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. 60 | 61 | ## Additional Resources 62 | 63 | For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. 64 | -------------------------------------------------------------------------------- /projects/angular-odata/karma.config.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, '../../coverage/angular-odata'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['ChromeHeadless'], 41 | singleRun: true, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /projects/angular-odata/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/angular-odata", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /projects/angular-odata/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-odata", 3 | "version": "0.130.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "angular-odata", 9 | "version": "0.130.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "tslib": "^2.3.0" 13 | }, 14 | "peerDependencies": { 15 | "@angular/common": ">=16.0.0", 16 | "@angular/core": ">=16.0.0" 17 | } 18 | }, 19 | "node_modules/@angular/common": { 20 | "version": "19.2.13", 21 | "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.2.13.tgz", 22 | "integrity": "sha512-k7I4bLH+bgI02VL81MaL0NcZPfVl153KAiARwk+ZlkmQjMnWlmsAHQ6054SWoNEXwP855ATR6YYDVqJh8TZaqw==", 23 | "license": "MIT", 24 | "peer": true, 25 | "dependencies": { 26 | "tslib": "^2.3.0" 27 | }, 28 | "engines": { 29 | "node": "^18.19.1 || ^20.11.1 || >=22.0.0" 30 | }, 31 | "peerDependencies": { 32 | "@angular/core": "19.2.13", 33 | "rxjs": "^6.5.3 || ^7.4.0" 34 | } 35 | }, 36 | "node_modules/@angular/core": { 37 | "version": "19.2.13", 38 | "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.2.13.tgz", 39 | "integrity": "sha512-HpzDI3TSQzVV2mmQ8KwH0JSLNlYNemNrEo3L3hcqqYwTzqFgAK4y1Q2Xym3yiRSLTenYhW5D4CQqOHUQ26HxwQ==", 40 | "license": "MIT", 41 | "peer": true, 42 | "dependencies": { 43 | "tslib": "^2.3.0" 44 | }, 45 | "engines": { 46 | "node": "^18.19.1 || ^20.11.1 || >=22.0.0" 47 | }, 48 | "peerDependencies": { 49 | "rxjs": "^6.5.3 || ^7.4.0", 50 | "zone.js": "~0.15.0" 51 | } 52 | }, 53 | "node_modules/rxjs": { 54 | "version": "7.8.2", 55 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", 56 | "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", 57 | "license": "Apache-2.0", 58 | "peer": true, 59 | "dependencies": { 60 | "tslib": "^2.1.0" 61 | } 62 | }, 63 | "node_modules/tslib": { 64 | "version": "2.8.1", 65 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 66 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 67 | "license": "0BSD" 68 | }, 69 | "node_modules/zone.js": { 70 | "version": "0.15.1", 71 | "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz", 72 | "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==", 73 | "license": "MIT", 74 | "peer": true 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /projects/angular-odata/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-odata", 3 | "version": "0.130.0", 4 | "license": "MIT", 5 | "description": "Client side OData typescript library for Angular", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/diegomvh/angular-odata.git" 9 | }, 10 | "keywords": [ 11 | "client", 12 | "odata", 13 | "odata v4", 14 | "odata v3", 15 | "odata v2", 16 | "typescript", 17 | "api", 18 | "@angular", 19 | "angular" 20 | ], 21 | "author": "Diego van Haaster", 22 | "bugs": { 23 | "url": "https://github.com/diegomvh/angular-odata/issues" 24 | }, 25 | "homepage": "https://github.com/diegomvh/angular-odata", 26 | "private": false, 27 | "scripts": { 28 | "build": "../../node_modules/.bin/tsc -p tsconfig.schematics.json", 29 | "copy:schemas": "cp --parents schematics/*/schema.json ../../dist/angular-odata/", 30 | "copy:files": "cp --parents -p schematics/*/files/**/* ../../dist/angular-odata/", 31 | "copy:collection": "cp schematics/collection.json ../../dist/angular-odata/schematics/collection.json", 32 | "postbuild": "npm run copy:schemas && npm run copy:files && npm run copy:collection" 33 | }, 34 | "peerDependencies": { 35 | "@angular/common": ">=16.0.0", 36 | "@angular/core": ">=16.0.0" 37 | }, 38 | "schematics": "./schematics/collection.json", 39 | "ng-add": { 40 | "save": "dependencies" 41 | }, 42 | "dependencies": { 43 | "tslib": "^2.3.0" 44 | }, 45 | "sideEffects": false 46 | } 47 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/angular/api-config.ts: -------------------------------------------------------------------------------- 1 | import { strings } from '@angular-devkit/core'; 2 | import { Base } from './base'; 3 | import { url, Source } from '@angular-devkit/schematics'; 4 | import { Schema as ApiGenSchema } from '../schema'; 5 | 6 | export class ApiConfig extends Base { 7 | constructor(options: ApiGenSchema) { 8 | super(options); 9 | } 10 | public override template(): Source { 11 | return url('./files/api-config'); 12 | } 13 | public override variables(): { [name: string]: any } { 14 | return { 15 | serviceRootUrl: this.options.serviceRootUrl, 16 | metadataUrl: this.options.metadata, 17 | apiConfigName: this.options.name, 18 | version: this.options.version, 19 | creation: this.options.creation, 20 | }; 21 | } 22 | public override name() { 23 | return strings.classify(this.options.name) + 'Config'; 24 | } 25 | public override fileName() { 26 | return strings.dasherize(this.options.name) + '.config'; 27 | } 28 | public override directory() { 29 | return ''; 30 | } 31 | public override fullName() { 32 | return this.name(); 33 | } 34 | public override importTypes(): string[] { 35 | return []; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/angular/entity.ts: -------------------------------------------------------------------------------- 1 | import { strings } from '@angular-devkit/core'; 2 | import { Base } from './base'; 3 | import { 4 | CsdlComplexType, 5 | CsdlEntityType, 6 | } from '../metadata/csdl/csdl-structured-type'; 7 | import { url, Source } from '@angular-devkit/schematics'; 8 | import { Schema as ApiGenSchema } from '../schema'; 9 | import { 10 | CsdlNavigationProperty, 11 | CsdlProperty, 12 | } from '../metadata/csdl/csdl-structural-property'; 13 | import { toTypescriptType } from '../utils'; 14 | 15 | export class EntityProperty { 16 | constructor(protected edmType: CsdlProperty | CsdlNavigationProperty) {} 17 | 18 | name() { 19 | return this.edmType.Name; 20 | } 21 | 22 | type() { 23 | let type = toTypescriptType(this.edmType.Type); 24 | type += this.edmType.Collection ? '[]' : ''; 25 | type += this.edmType.Nullable ? ' | null' : ''; 26 | return type; 27 | } 28 | } 29 | 30 | export class Entity extends Base { 31 | constructor( 32 | options: ApiGenSchema, 33 | protected edmType: CsdlEntityType | CsdlComplexType, 34 | ) { 35 | super(options); 36 | } 37 | 38 | public override template(): Source { 39 | return url('./files/entity'); 40 | } 41 | public override variables(): { [name: string]: any } { 42 | return { 43 | type: 44 | this.name() + 45 | (this.edmType instanceof CsdlEntityType ? 'EntityType' : 'ComplexType'), 46 | baseType: this.edmType.BaseType, 47 | properties: [ 48 | ...(this.edmType.Property ?? []).map((p) => new EntityProperty(p)), 49 | ...(this.edmType.NavigationProperty ?? []).map( 50 | (p) => new EntityProperty(p), 51 | ), 52 | ], 53 | }; 54 | } 55 | public override name() { 56 | return strings.classify(this.edmType.name()); 57 | } 58 | public override fileName() { 59 | return ( 60 | strings.dasherize(this.edmType.name()) + 61 | (this.edmType instanceof CsdlEntityType ? '.entity' : '.complex') 62 | ); 63 | } 64 | public override directory() { 65 | return this.edmType.namespace().replace(/\./g, '/'); 66 | } 67 | public override fullName() { 68 | return this.edmType.fullName(); 69 | } 70 | public override importTypes(): string[] { 71 | const imports = []; 72 | if (this.edmType.BaseType) { 73 | imports.push(this.edmType.BaseType); 74 | } 75 | for (let prop of this.edmType?.Property ?? []) { 76 | if (!prop.Type.startsWith('Edm.')) { 77 | imports.push(prop.Type); 78 | } 79 | } 80 | for (let prop of this.edmType?.NavigationProperty ?? []) { 81 | if (!prop.Type.startsWith('Edm.')) { 82 | imports.push(prop.Type); 83 | } 84 | } 85 | return imports; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/angular/enum.ts: -------------------------------------------------------------------------------- 1 | import { CsdlEnumType, CsdlMember } from '../metadata/csdl/csdl-enum-type'; 2 | import { strings } from '@angular-devkit/core'; 3 | import { Base } from './base'; 4 | import { Import } from './import'; 5 | import { url, Source } from '@angular-devkit/schematics'; 6 | import { Schema as ApiGenSchema } from '../schema'; 7 | 8 | export class EnumValue { 9 | constructor(private edmType: CsdlMember) {} 10 | public name() { 11 | return this.edmType.Name; 12 | } 13 | public value() { 14 | return this.edmType.Value; 15 | } 16 | } 17 | export class Enum extends Base { 18 | constructor( 19 | options: ApiGenSchema, 20 | protected edmType: CsdlEnumType, 21 | ) { 22 | super(options); 23 | } 24 | public override template(): Source { 25 | return url('./files/enum'); 26 | } 27 | public override variables(): { [name: string]: any } { 28 | return { 29 | type: this.name() + 'EnumType', 30 | values: (this.edmType.Member ?? []).map((m) => new EnumValue(m)), 31 | }; 32 | } 33 | public override name() { 34 | return strings.classify(this.edmType.name()); 35 | } 36 | public override fileName() { 37 | return strings.dasherize(this.edmType.name()) + '.enum'; 38 | } 39 | public override directory() { 40 | return this.edmType.namespace().replace(/\./g, '/'); 41 | } 42 | public override fullName() { 43 | return this.edmType.fullName(); 44 | } 45 | public members() { 46 | return this.edmType.Member.map((m) => `${m.Name} = ${m.Value}`); 47 | } 48 | public flags() { 49 | return this.edmType.IsFlags; 50 | } 51 | public override importTypes(): string[] { 52 | return []; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/angular/import.ts: -------------------------------------------------------------------------------- 1 | export class Import { 2 | public names: string[] = []; 3 | public from: string; 4 | public constructor(names: string[], from: string) { 5 | this.names = names; 6 | this.from = from; 7 | } 8 | 9 | public path(): string { 10 | var path = this.from.toString(); 11 | if (!path.startsWith('../')) path = `./${path}`; 12 | return path; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/angular/module.ts: -------------------------------------------------------------------------------- 1 | import { strings } from '@angular-devkit/core'; 2 | import { Base } from './base'; 3 | import { url, Source } from '@angular-devkit/schematics'; 4 | import { Schema as ApiGenSchema } from '../schema'; 5 | import { Service } from './service'; 6 | 7 | export class Module extends Base { 8 | services: Service[] = []; 9 | constructor(options: ApiGenSchema) { 10 | super(options); 11 | } 12 | public override template(): Source { 13 | return url('./files/module'); 14 | } 15 | public override variables(): { [name: string]: any } { 16 | return { 17 | services: this.services, 18 | }; 19 | } 20 | public addService(service: Service) { 21 | this.services.push(service); 22 | this.addDependency(service); 23 | } 24 | public override name() { 25 | return strings.classify(this.options.name) + 'Module'; 26 | } 27 | public override fileName() { 28 | return strings.dasherize(this.options.name) + '.module'; 29 | } 30 | public override directory() { 31 | return ''; 32 | } 33 | public override fullName() { 34 | return this.name(); 35 | } 36 | public override importTypes(): string[] { 37 | return []; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/angular/service.ts: -------------------------------------------------------------------------------- 1 | import { strings } from '@angular-devkit/core'; 2 | import { Base } from './base'; 3 | import { CsdlEntityContainer } from '../metadata/csdl/csdl-entity-container'; 4 | import { CsdlSingleton } from '../metadata/csdl/csdl-singleton'; 5 | import { CsdlEntitySet } from '../metadata/csdl/csdl-entity-set'; 6 | import { url, Source } from '@angular-devkit/schematics'; 7 | import { Schema as ApiGenSchema } from '../schema'; 8 | 9 | export class Service extends Base { 10 | constructor( 11 | options: ApiGenSchema, 12 | protected edmElement: CsdlEntitySet | CsdlEntityContainer | CsdlSingleton, 13 | ) { 14 | super(options); 15 | } 16 | public override template(): Source { 17 | return this.edmElement instanceof CsdlEntitySet 18 | ? url('./files/entityset-service') 19 | : this.edmElement instanceof CsdlSingleton 20 | ? url('./files/singleton-service') 21 | : url('./files/entitycontainer-service'); 22 | } 23 | public override variables(): { [name: string]: any } { 24 | return { 25 | path: this.edmElement.name(), 26 | type: 27 | this.edmElement instanceof CsdlEntitySet 28 | ? this.edmElement.EntityType 29 | : this.edmElement instanceof CsdlSingleton 30 | ? this.edmElement.Type 31 | : this.options.name, 32 | callables: this.callables ?? [], 33 | }; 34 | } 35 | public entityType() { 36 | return this.edmElement instanceof CsdlEntitySet 37 | ? this.edmElement.EntityType 38 | : this.edmElement instanceof CsdlSingleton 39 | ? this.edmElement.Type 40 | : ''; 41 | } 42 | 43 | public override name() { 44 | return strings.classify(this.edmElement.name()) + 'Service'; 45 | } 46 | public override fileName() { 47 | return strings.dasherize(this.edmElement.name()) + '.service'; 48 | } 49 | public override directory() { 50 | return this.edmElement.namespace().replace(/\./g, '/'); 51 | } 52 | public override fullName() { 53 | return this.edmElement.fullName(); 54 | } 55 | public override importTypes(): string[] { 56 | const imports = []; 57 | if (this.edmElement instanceof CsdlEntitySet) { 58 | imports.push(this.edmElement.EntityType); 59 | } else if (this.edmElement instanceof CsdlSingleton) { 60 | imports.push(this.edmElement.Type); 61 | } 62 | for (var call of this.callables ?? []) { 63 | const ret = call.returnType(); 64 | if (ret !== undefined && !ret.Type.startsWith('Edm.')) { 65 | imports.push(ret.Type); 66 | } 67 | const { binding, required, optional } = call.parameters(); 68 | for (let param of [...required, ...optional]) { 69 | if (!param.Type.startsWith('Edm.')) { 70 | imports.push(param.Type); 71 | } 72 | } 73 | } 74 | return imports; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/api-config/__fileName__.ts: -------------------------------------------------------------------------------- 1 | import { ODataApiConfig, EDM_PARSERS, ODataMetadata, ODataVersion } from 'angular-odata'; 2 | import * as json from './metadata.json'; 3 | 4 | export const <%= classify(name) %> = ODataMetadata.fromJson(json).toConfig({ 5 | serviceRootUrl: '<%= serviceRootUrl %>', 6 | metadataUrl: '<%= metadataUrl %>', 7 | name: '<%= apiConfigName %>', 8 | version: '<%= version %>' as ODataVersion, 9 | creation: new Date('<%= creation.toISOString() %>'), 10 | parsers: EDM_PARSERS 11 | }) as ODataApiConfig; 12 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/entity/__fileName__.ts: -------------------------------------------------------------------------------- 1 | import { Duration } from 'angular-odata';<% for (let imp of imports) { %> 2 | import { <%= imp.names.join(", ") %> } from '<%= imp.path() %>';<% } %> 3 | 4 | export const <%= type %> = '<%= fullName %>'; 5 | export interface <%= classify(name) %><% if (baseType) { %> extends <%= toTypescriptType(baseType) %><% } %> {<% for(let prop of properties) { %> 6 | <%= prop.name() %>: <%= prop.type() %>;<% } %> 7 | } 8 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/entitycontainer-service/__fileName__.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ODataClient, 3 | ODataActionResource, 4 | ODataActionOptions, 5 | ODataFunctionResource, 6 | ODataFunctionOptions, 7 | ODataEntitySetService, 8 | ODataOptions, 9 | ODataBaseService, 10 | EntityKey } from 'angular-odata';<% for (let imp of imports) { %> 11 | import { <%= imp.names.join(", ") %> } from '<%= imp.path() %>';<% } %> 12 | 13 | @Injectable() 14 | export class <%= classify(name) %> extends ODataBaseService { 15 | constructor(client: ODataClient) { 16 | super(client, '<%= path %>', '<%= type %>'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/entityset-service/__fileName__.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ODataClient, 3 | ODataActionResource, 4 | ODataActionOptions, 5 | ODataFunctionResource, 6 | ODataFunctionOptions, 7 | ODataEntitySetService, 8 | ODataOptions, 9 | EntityKey } from 'angular-odata';<% for (let imp of imports) { %> 10 | import { <%= imp.names.join(", ") %> } from '<%= imp.path() %>';<% } %> 11 | 12 | @Injectable() 13 | export class <%= classify(name) %> extends ODataEntitySetService<<%= toTypescriptType(type) %>> { 14 | constructor(client: ODataClient) { 15 | super(client, '<%= path %>', '<%= type %>'); 16 | } 17 | <%= camelize(toTypescriptType(type)) %>Model(entity?: Partial<<%= toTypescriptType(type) %>>) { 18 | return this.model(entity); 19 | } 20 | <%= camelize(toTypescriptType(type)) %>Collection(entities?: Partial<<%= toTypescriptType(type) %>>[]) { 21 | return this.collection(entities); 22 | }<% for (let cal of callables) { %> 23 | // <%= cal.name() %> 24 | <%= cal.resourceFunction() %> 25 | <%= cal.callableFunction() %> 26 | <% } %> 27 | } 28 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/enum/__fileName__.ts: -------------------------------------------------------------------------------- 1 | export const <%= type %> = '<%= fullName %>'; 2 | export enum <%= classify(name) %> {<% for(let value of values) { %> 3 | <%= value.name() %> = <%= value.value() %>,<% } %> 4 | } 5 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/index/__fileName__.ts: -------------------------------------------------------------------------------- 1 | <% for (let imp of imports) { %>export * from '<%= imp.path() %>'; 2 | <% } %> 3 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/metadata/metadata.json: -------------------------------------------------------------------------------- 1 | <%= content %> -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/module/__fileName__.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core';<% for (let imp of imports) { %> 2 | import { <%= imp.names.join(", ") %> } from '<%= imp.path() %>';<% } %> 3 | 4 | @NgModule({ 5 | providers: [<% for(let service of services) { %> 6 | <%= service.name() %>,<% } %> 7 | ] 8 | }) 9 | export class <%= classify(name) %> { } 10 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/singleton-service/__fileName__.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ODataClient, 3 | ODataActionResource, 4 | ODataActionOptions, 5 | ODataFunctionResource, 6 | ODataFunctionOptions, 7 | ODataEntitySetService, 8 | ODataOptions, 9 | ODataSingletonService, 10 | EntityKey } from 'angular-odata';<% for (let imp of imports) { %> 11 | import { <%= imp.names.join(", ") %> } from '<%= imp.path() %>';<% } %> 12 | 13 | @Injectable() 14 | export class <%= classify(name) %> extends ODataSingletonService<<%= toTypescriptType(type) %>> { 15 | constructor(client: ODataClient) { 16 | super(client, '<%= path %>', '<%= type %>'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/csdl/csdl-entity-container.ts: -------------------------------------------------------------------------------- 1 | import { CsdlEntitySet } from './csdl-entity-set'; 2 | import { CsdlSingleton } from './csdl-singleton'; 3 | import { CsdlFunctionImport, CsdlActionImport } from './csdl-function-action'; 4 | import { CsdlAnnotable } from './csdl-annotation'; 5 | import type { CsdlSchema } from './csdl-schema'; 6 | 7 | export class CsdlEntityContainer extends CsdlAnnotable { 8 | public Name: string; 9 | public Extend?: string; 10 | public EntitySet?: CsdlEntitySet[]; 11 | public Singleton?: CsdlSingleton[]; 12 | public FunctionImport?: CsdlFunctionImport[]; 13 | public ActionImport?: CsdlActionImport[]; 14 | 15 | constructor( 16 | private schema: CsdlSchema, 17 | { 18 | Name, 19 | Extend, 20 | EntitySet, 21 | Singleton, 22 | FunctionImport, 23 | ActionImport, 24 | Annotation, 25 | }: { 26 | Name: string; 27 | Extend?: string; 28 | EntitySet?: any[]; 29 | Singleton?: any[]; 30 | FunctionImport?: any[]; 31 | ActionImport?: any[]; 32 | Annotation?: any[]; 33 | }, 34 | ) { 35 | super({ Annotation }); 36 | 37 | this.Name = Name; 38 | this.Extend = Extend; 39 | this.EntitySet = EntitySet?.map((e) => new CsdlEntitySet(this, e)); 40 | this.Singleton = Singleton?.map((s) => new CsdlSingleton(this, s)); 41 | this.FunctionImport = FunctionImport?.map( 42 | (f) => new CsdlFunctionImport(this, f), 43 | ); 44 | this.ActionImport = ActionImport?.map((a) => new CsdlActionImport(this, a)); 45 | } 46 | 47 | override toJson() { 48 | const json: { [key: string]: any } = { ...super.toJson() }; 49 | if (this.Extend !== undefined) { 50 | json['Extend'] = this.Extend; 51 | } 52 | if (Array.isArray(this.EntitySet) && this.EntitySet.length > 0) { 53 | json['EntitySet'] = this.EntitySet.map((a) => a.toJson()); 54 | } 55 | if (Array.isArray(this.Singleton) && this.Singleton.length > 0) { 56 | json['Singleton'] = this.Singleton.map((a) => a.toJson()); 57 | } 58 | if (Array.isArray(this.FunctionImport) && this.FunctionImport.length > 0) { 59 | json['FunctionImport'] = this.FunctionImport.map((a) => a.toJson()); 60 | } 61 | if (Array.isArray(this.ActionImport) && this.ActionImport.length > 0) { 62 | json['ActionImport'] = this.ActionImport.map((a) => a.toJson()); 63 | } 64 | return json; 65 | } 66 | 67 | name() { 68 | return `${this.Name}`; 69 | } 70 | 71 | namespace() { 72 | return `${this.schema.Namespace}`; 73 | } 74 | 75 | fullName() { 76 | return `${this.schema.Namespace}.${this.Name}`; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/csdl/csdl-entity-set.ts: -------------------------------------------------------------------------------- 1 | import { CsdlAnnotable } from './csdl-annotation'; 2 | import type { CsdlEntityContainer } from './csdl-entity-container'; 3 | import { CsdlNavigationPropertyBinding } from './csdl-navigation-property-binding'; 4 | 5 | export class CsdlEntitySet extends CsdlAnnotable { 6 | public Name: string; 7 | public EntityType: string; 8 | public NavigationPropertyBinding?: CsdlNavigationPropertyBinding[]; 9 | public IncludeInServiceDocument?: boolean; 10 | 11 | constructor( 12 | private container: CsdlEntityContainer, 13 | { 14 | Name, 15 | EntityType, 16 | NavigationPropertyBinding, 17 | IncludeInServiceDocument, 18 | Annotation, 19 | }: { 20 | Name: string; 21 | EntityType: string; 22 | NavigationPropertyBinding?: any[]; 23 | IncludeInServiceDocument?: boolean; 24 | Annotation?: any[]; 25 | }, 26 | ) { 27 | super({ Annotation }); 28 | 29 | this.Name = Name; 30 | this.EntityType = EntityType; 31 | this.NavigationPropertyBinding = NavigationPropertyBinding?.map( 32 | (n) => new CsdlNavigationPropertyBinding(n), 33 | ); 34 | this.IncludeInServiceDocument = IncludeInServiceDocument; 35 | } 36 | 37 | override toJson() { 38 | const json: { [key: string]: any } = { 39 | ...super.toJson(), 40 | Name: this.Name, 41 | EntityType: this.EntityType, 42 | }; 43 | if ( 44 | Array.isArray(this.NavigationPropertyBinding) && 45 | this.NavigationPropertyBinding.length > 0 46 | ) { 47 | json['NavigationPropertyBinding'] = this.NavigationPropertyBinding.map( 48 | (n) => n.toJson(), 49 | ); 50 | } 51 | if (this.IncludeInServiceDocument !== undefined) { 52 | json['IncludeInServiceDocument'] = this.IncludeInServiceDocument; 53 | } 54 | return json; 55 | } 56 | 57 | name() { 58 | return `${this.Name}`; 59 | } 60 | 61 | namespace() { 62 | return `${this.container.namespace()}`; 63 | } 64 | 65 | fullName() { 66 | return `${this.container.namespace()}.${this.Name}`; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/csdl/csdl-enum-type.ts: -------------------------------------------------------------------------------- 1 | import { CsdlAnnotable } from './csdl-annotation'; 2 | import type { CsdlSchema } from './csdl-schema'; 3 | 4 | export class CsdlEnumType extends CsdlAnnotable { 5 | Name: string; 6 | Member: CsdlMember[]; 7 | UnderlyingType?: string; 8 | IsFlags?: boolean; 9 | constructor( 10 | private schema: CsdlSchema, 11 | { 12 | Name, 13 | Member, 14 | UnderlyingType, 15 | IsFlags, 16 | Annotation, 17 | }: { 18 | Name: string; 19 | Member: any[]; 20 | UnderlyingType?: string; 21 | IsFlags?: boolean; 22 | Annotation?: any[]; 23 | }, 24 | ) { 25 | super({ Annotation }); 26 | this.Name = Name; 27 | this.Member = Member.map((m) => new CsdlMember(m)); 28 | this.UnderlyingType = UnderlyingType; 29 | this.IsFlags = IsFlags; 30 | } 31 | 32 | override toJson() { 33 | const json: { [key: string]: any } = { 34 | ...super.toJson(), 35 | Name: this.Name, 36 | Member: this.Member.map((m) => m.toJson()), 37 | }; 38 | if (this.UnderlyingType !== undefined) { 39 | json['UnderlyingType'] = this.UnderlyingType; 40 | } 41 | if (this.IsFlags !== undefined) { 42 | json['IsFlags'] = this.IsFlags; 43 | } 44 | return json; 45 | } 46 | 47 | name() { 48 | return `${this.Name}`; 49 | } 50 | 51 | namespace() { 52 | return `${this.schema.Namespace}`; 53 | } 54 | 55 | fullName() { 56 | return `${this.schema.Namespace}.${this.Name}`; 57 | } 58 | } 59 | 60 | export class CsdlMember extends CsdlAnnotable { 61 | Name: string; 62 | Value?: number; 63 | constructor({ 64 | Name, 65 | Value, 66 | Annotation, 67 | }: { 68 | Name: string; 69 | Value?: number; 70 | Annotation?: any[]; 71 | }) { 72 | super({ Annotation }); 73 | this.Name = Name; 74 | this.Value = Value; 75 | } 76 | 77 | override toJson() { 78 | const json: { [key: string]: any } = { ...super.toJson(), Name: this.Name }; 79 | if (this.Value !== undefined) { 80 | json['Value'] = this.Value; 81 | } 82 | return json; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/csdl/csdl-navigation-property-binding.ts: -------------------------------------------------------------------------------- 1 | export class CsdlNavigationPropertyBinding { 2 | Path: string; 3 | Target: string; 4 | 5 | constructor({ Path, Target }: { Path: string; Target: string }) { 6 | this.Path = Path; 7 | this.Target = Target; 8 | } 9 | 10 | toJson() { 11 | return { 12 | Path: this.Path, 13 | Target: this.Target, 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/csdl/csdl-reference.ts: -------------------------------------------------------------------------------- 1 | import { CsdlAnnotable } from './csdl-annotation'; 2 | 3 | export class CsdlReference extends CsdlAnnotable { 4 | Uri: string; 5 | Include?: CsdlInclude[]; 6 | IncludeAnnotations?: CsdlIncludeAnnotations[]; 7 | constructor({ 8 | Uri, 9 | Include, 10 | IncludeAnnotations, 11 | Annotation, 12 | }: { 13 | Uri: string; 14 | Include?: any[]; 15 | IncludeAnnotations?: any[]; 16 | Annotation?: any[]; 17 | }) { 18 | super({ Annotation }); 19 | this.Uri = Uri; 20 | this.Include = Include?.map((i) => new CsdlInclude(i)); 21 | this.IncludeAnnotations = IncludeAnnotations?.map( 22 | (i) => new CsdlIncludeAnnotations(i), 23 | ); 24 | } 25 | 26 | override toJson() { 27 | const json: { [key: string]: any } = { ...super.toJson(), Uri: this.Uri }; 28 | if (Array.isArray(this.Include) && this.Include.length > 0) { 29 | json['Include'] = this.Include.map((i) => i.toJson()); 30 | } 31 | if ( 32 | Array.isArray(this.IncludeAnnotations) && 33 | this.IncludeAnnotations.length > 0 34 | ) { 35 | json['IncludeAnnotations'] = this.IncludeAnnotations.map((i) => 36 | i.toJson(), 37 | ); 38 | } 39 | return json; 40 | } 41 | } 42 | 43 | export class CsdlInclude { 44 | Namespace: string; 45 | Alias?: string; 46 | constructor({ Namespace, Alias }: { Namespace: string; Alias?: string }) { 47 | this.Namespace = Namespace; 48 | this.Alias = Alias; 49 | } 50 | 51 | toJson() { 52 | return { 53 | Namespace: this.Namespace, 54 | Alias: this.Alias, 55 | }; 56 | } 57 | } 58 | 59 | export class CsdlIncludeAnnotations { 60 | TermNamespace: string; 61 | Qualifier?: string; 62 | TargetNamespace?: string; 63 | constructor({ 64 | TermNamespace, 65 | Qualifier, 66 | TargetNamespace, 67 | }: { 68 | TermNamespace: string; 69 | Qualifier?: string; 70 | TargetNamespace?: string; 71 | }) { 72 | this.TermNamespace = TermNamespace; 73 | this.Qualifier = Qualifier; 74 | this.TargetNamespace = TargetNamespace; 75 | } 76 | 77 | toJson() { 78 | return { 79 | TermNamespace: this.TermNamespace, 80 | Qualifier: this.Qualifier, 81 | TargetNamespace: this.TargetNamespace, 82 | }; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/csdl/csdl-schema.ts: -------------------------------------------------------------------------------- 1 | import { CsdlTerm, CsdlAnnotations } from './csdl-annotation'; 2 | import { CsdlTypeDefinition } from './csdl-type-definition'; 3 | import { CsdlEnumType } from './csdl-enum-type'; 4 | import { CsdlEntityType, CsdlComplexType } from './csdl-structured-type'; 5 | import { CsdlFunction, CsdlAction } from './csdl-function-action'; 6 | import { CsdlEntityContainer } from './csdl-entity-container'; 7 | 8 | export class CsdlSchema { 9 | Namespace: string; 10 | Alias?: string; 11 | EnumType?: CsdlEnumType[]; 12 | ComplexType?: CsdlComplexType[]; 13 | EntityType?: CsdlEntityType[]; 14 | Function?: CsdlFunction[]; 15 | Action?: CsdlAction[]; 16 | EntityContainer?: CsdlEntityContainer[]; 17 | TypeDefinition?: CsdlTypeDefinition[]; 18 | Term?: CsdlTerm[]; 19 | Annotations?: CsdlAnnotations[]; 20 | constructor({ 21 | Namespace, 22 | Alias, 23 | EnumType, 24 | ComplexType, 25 | EntityType, 26 | Function, 27 | Action, 28 | EntityContainer, 29 | TypeDefinition, 30 | Term, 31 | Annotations, 32 | }: { 33 | Namespace: string; 34 | Alias?: string; 35 | EnumType?: any[]; 36 | ComplexType?: any[]; 37 | EntityType?: any[]; 38 | Function?: any[]; 39 | Action?: any[]; 40 | EntityContainer?: any[]; 41 | TypeDefinition?: any[]; 42 | Term?: any[]; 43 | Annotations?: any[]; 44 | }) { 45 | this.Namespace = Namespace; 46 | this.Alias = Alias; 47 | this.EnumType = EnumType?.map((e) => new CsdlEnumType(this, e)); 48 | this.ComplexType = ComplexType?.map((c) => new CsdlComplexType(this, c)); 49 | this.EntityType = EntityType?.map((e) => new CsdlEntityType(this, e)); 50 | this.Function = Function?.map((f) => new CsdlFunction(this, f)); 51 | this.Action = Action?.map((a) => new CsdlAction(this, a)); 52 | this.EntityContainer = EntityContainer?.map( 53 | (e) => new CsdlEntityContainer(this, e), 54 | ); 55 | this.TypeDefinition = TypeDefinition?.map( 56 | (t) => new CsdlTypeDefinition(this, t), 57 | ); 58 | this.Term = Term?.map((t) => new CsdlTerm(this, t)); 59 | this.Annotations = Annotations?.map((a) => new CsdlAnnotations(this, a)); 60 | } 61 | 62 | toJson() { 63 | const json: { [key: string]: any } = { 64 | Namespace: this.Namespace, 65 | }; 66 | if (this.Alias !== undefined) { 67 | json['Alias'] = this.Alias; 68 | } 69 | if ( 70 | Array.isArray(this.EntityContainer) && 71 | this.EntityContainer.length > 0 72 | ) { 73 | json['EntityContainer'] = this.EntityContainer.map((a) => a.toJson()); 74 | } 75 | if (Array.isArray(this.EntityType) && this.EntityType.length > 0) { 76 | json['EntityType'] = this.EntityType.map((a) => a.toJson()); 77 | } 78 | if (Array.isArray(this.ComplexType) && this.ComplexType.length > 0) { 79 | json['ComplexType'] = this.ComplexType.map((a) => a.toJson()); 80 | } 81 | if (Array.isArray(this.EnumType) && this.EnumType.length > 0) { 82 | json['EnumType'] = this.EnumType.map((a) => a.toJson()); 83 | } 84 | if (Array.isArray(this.TypeDefinition) && this.TypeDefinition.length > 0) { 85 | json['TypeDefinition'] = this.TypeDefinition.map((a) => a.toJson()); 86 | } 87 | if (Array.isArray(this.Term) && this.Term.length > 0) { 88 | json['Term'] = this.Term.map((a) => a.toJson()); 89 | } 90 | if (Array.isArray(this.Annotations) && this.Annotations.length > 0) { 91 | json['Annotations'] = this.Annotations.map((a) => a.toJson()); 92 | } 93 | if (Array.isArray(this.Action) && this.Action.length > 0) { 94 | json['Action'] = this.Action.map((a) => a.toJson()); 95 | } 96 | if (Array.isArray(this.Function) && this.Function.length > 0) { 97 | json['Function'] = this.Function.map((a) => a.toJson()); 98 | } 99 | return json; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/csdl/csdl-singleton.ts: -------------------------------------------------------------------------------- 1 | import { CsdlAnnotable } from './csdl-annotation'; 2 | import type { CsdlEntityContainer } from './csdl-entity-container'; 3 | import { CsdlNavigationPropertyBinding } from './csdl-navigation-property-binding'; 4 | 5 | export class CsdlSingleton extends CsdlAnnotable { 6 | Name: string; 7 | Type: string; 8 | NavigationPropertyBindings?: CsdlNavigationPropertyBinding[]; 9 | 10 | constructor( 11 | private container: CsdlEntityContainer, 12 | { 13 | Name, 14 | Type, 15 | NavigationPropertyBindings, 16 | Annotation, 17 | }: { 18 | Name: string; 19 | Type: string; 20 | NavigationPropertyBindings?: any[]; 21 | Annotation?: any[]; 22 | }, 23 | ) { 24 | super({ Annotation }); 25 | this.Name = Name; 26 | this.Type = Type; 27 | this.NavigationPropertyBindings = NavigationPropertyBindings?.map( 28 | (n) => new CsdlNavigationPropertyBinding(n), 29 | ); 30 | } 31 | 32 | override toJson() { 33 | const json: { [key: string]: any } = { 34 | ...super.toJson(), 35 | Name: this.Name, 36 | Type: this.Type, 37 | }; 38 | if ( 39 | Array.isArray(this.NavigationPropertyBindings) && 40 | this.NavigationPropertyBindings.length > 0 41 | ) { 42 | json['NavigationPropertyBindings'] = this.NavigationPropertyBindings.map( 43 | (n) => n.toJson(), 44 | ); 45 | } 46 | return json; 47 | } 48 | 49 | name() { 50 | return `${this.Name}`; 51 | } 52 | 53 | namespace() { 54 | return `${this.container.namespace()}`; 55 | } 56 | 57 | fullName() { 58 | return `${this.container.namespace()}.${this.Name}`; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/csdl/csdl-type-definition.ts: -------------------------------------------------------------------------------- 1 | import { CsdlAnnotable, CsdlAnnotation } from './csdl-annotation'; 2 | import type { CsdlSchema } from './csdl-schema'; 3 | 4 | export class CsdlTypeDefinition extends CsdlAnnotable { 5 | public Name: string; 6 | public UnderlayingType: string; 7 | public MaxLength?: number; 8 | public Precision?: number; 9 | public Scale?: number; 10 | public Unicode?: boolean; 11 | public SRID?: string; 12 | 13 | constructor( 14 | private schema: CsdlSchema, 15 | { 16 | Name, 17 | UnderlayingType, 18 | MaxLength, 19 | Precision, 20 | Scale, 21 | Unicode, 22 | SRID, 23 | Annotation, 24 | }: { 25 | Name: string; 26 | UnderlayingType: string; 27 | MaxLength?: number; 28 | Precision?: number; 29 | Scale?: number; 30 | Unicode?: boolean; 31 | SRID?: string; 32 | Annotation?: CsdlAnnotation[]; 33 | }, 34 | ) { 35 | super({ Annotation }); 36 | this.Name = Name; 37 | this.UnderlayingType = UnderlayingType; 38 | this.MaxLength = MaxLength; 39 | this.Precision = Precision; 40 | this.Scale = Scale; 41 | this.Unicode = Unicode; 42 | this.SRID = SRID; 43 | } 44 | 45 | override toJson() { 46 | const json: { [key: string]: any } = { 47 | ...super.toJson(), 48 | Name: this.Name, 49 | UnderlayingType: this.UnderlayingType, 50 | }; 51 | if (this.MaxLength !== undefined) { 52 | json['MaxLength'] = this.MaxLength; 53 | } 54 | if (this.Precision !== undefined) { 55 | json['Precision'] = this.Precision; 56 | } 57 | if (this.Scale !== undefined) { 58 | json['Scale'] = this.Scale; 59 | } 60 | if (this.Unicode !== undefined) { 61 | json['Unicode'] = this.Unicode; 62 | } 63 | if (this.SRID !== undefined) { 64 | json['SRID'] = this.SRID; 65 | } 66 | return json; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/index.ts: -------------------------------------------------------------------------------- 1 | export * from './metadata'; 2 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/metadata.ts: -------------------------------------------------------------------------------- 1 | import { CsdlAction, CsdlFunction } from './csdl/csdl-function-action'; 2 | import { CsdlReference } from './csdl/csdl-reference'; 3 | import { CsdlSchema } from './csdl/csdl-schema'; 4 | 5 | export class ODataMetadata { 6 | Version: string; 7 | References: CsdlReference[]; 8 | Schemas: CsdlSchema[]; 9 | constructor(Version: string, References: any[], Schemas: any[]) { 10 | this.Version = Version; 11 | this.References = References?.map((r) => new CsdlReference(r)); 12 | this.Schemas = Schemas?.map((s) => new CsdlSchema(s)); 13 | } 14 | 15 | toJson() { 16 | return { 17 | Version: this.Version, 18 | References: this.References.map((r) => r.toJson()), 19 | Schemas: this.Schemas.map((s) => s.toJson()), 20 | }; 21 | } 22 | 23 | functions() { 24 | return this.Schemas.reduce((acc, s) => { 25 | return [...acc, ...(s.Function ?? [])]; 26 | }, [] as CsdlFunction[]); 27 | } 28 | 29 | actions() { 30 | return this.Schemas.reduce((acc, s) => { 31 | return [...acc, ...(s.Action ?? [])]; 32 | }, [] as CsdlAction[]); 33 | } 34 | 35 | static fromJson(json: any): ODataMetadata { 36 | return new ODataMetadata(json.Version, json.References, json.Schemas); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "$id": "SchematicsMetadata", 4 | "title": "Metadata Schema", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "description": "The name for the module.", 9 | "type": "string" 10 | }, 11 | "metadata": { 12 | "description": "The url of the metadata.", 13 | "type": "string" 14 | }, 15 | "path": { 16 | "type": "string", 17 | "format": "path", 18 | "description": "The path at which to create the module, relative to the workspace root.", 19 | "visible": false 20 | }, 21 | "project": { 22 | "type": "string", 23 | "description": "The name of the project.", 24 | "$default": { 25 | "$source": "projectName" 26 | } 27 | }, 28 | "serviceRootUrl": { 29 | "description": "The serviceRootUrl of the api.", 30 | "type": "string" 31 | } 32 | }, 33 | "required": [ 34 | "name", 35 | "metadata" 36 | ] 37 | } -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/schema.ts: -------------------------------------------------------------------------------- 1 | export interface Schema { 2 | name: string; 3 | project: string; 4 | metadata: string; 5 | path: string; 6 | serviceRootUrl?: string; 7 | version?: string; 8 | creation?: Date; 9 | } 10 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json", 3 | "schematics": { 4 | "ng-add": { 5 | "description": "Add my library to the project.", 6 | "factory": "./ng-add/index#ngAdd" 7 | }, 8 | "apigen": { 9 | "description": "Generate API from metadata file.", 10 | "factory": "./apigen/index#apigen", 11 | "schema": "./apigen/schema.json" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /projects/angular-odata/schematics/ng-add/index.ts: -------------------------------------------------------------------------------- 1 | import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; 2 | import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; 3 | 4 | // Just return the tree 5 | export function ngAdd(): Rule { 6 | return (tree: Tree, context: SchematicContext) => { 7 | context.addTask(new NodePackageInstallTask()); 8 | return tree; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/annotations.spec.ts: -------------------------------------------------------------------------------- 1 | import { VERSION_4_0 } from './constants'; 2 | import { ODataHelper } from './helper'; 3 | import { ODataEntitiesAnnotations } from './annotations'; 4 | 5 | describe('ODataEntitiesAnnotations', () => { 6 | let instance: ODataEntitiesAnnotations; 7 | let annots: Map; 8 | 9 | describe('version 4.0', () => { 10 | beforeEach(() => { 11 | const helper = ODataHelper[VERSION_4_0]; 12 | annots = new Map(); 13 | instance = new ODataEntitiesAnnotations(helper, annots); 14 | }); 15 | 16 | it('returns skipToken', () => { 17 | // Given 18 | const nextLink = 19 | 'https://graph.microsoft.com/v1.0/users?$skiptoken=RFNwdAoAAQAAAAAAAAAAFAAAAHoO-P3xtNOT90O-DRY2LSZF_AFWAAAAAQIAAAA'; 20 | annots.set('@odata.nextLink', nextLink); 21 | 22 | // When 23 | const actual = instance.skiptoken; 24 | 25 | // Then 26 | const expected = 27 | 'RFNwdAoAAQAAAAAAAAAAFAAAAHoO-P3xtNOT90O-DRY2LSZF_AFWAAAAAQIAAAA'; 28 | expect(actual).toBe(expected); 29 | }); 30 | }); 31 | }); 32 | 33 | interface AirPort {} 34 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/cache/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cache'; 2 | export * from './memory'; 3 | export * from './storage'; 4 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/cache/memory.ts: -------------------------------------------------------------------------------- 1 | import { ODataRequest, ODataResponse } from '../resources'; 2 | import { ODataBaseCache } from './cache'; 3 | 4 | export class ODataInMemoryCache extends ODataBaseCache { 5 | constructor({ timeout }: { timeout?: number } = {}) { 6 | super({ timeout }); 7 | } 8 | 9 | /** 10 | * Store the response in the cache 11 | * @param req The request with the resource to store the response 12 | * @param res The response to store in the cache 13 | */ 14 | putResponse(req: ODataRequest, res: ODataResponse) { 15 | let scope = this.scope(req); 16 | let tags = this.tags(res); 17 | this.put(req.cacheKey, res, { 18 | timeout: res.options.maxAge, 19 | scope, 20 | tags, 21 | }); 22 | } 23 | 24 | /** 25 | * Restore the response from the cache 26 | * @param req The request with the resource to get the response 27 | * @returns The response from the cache 28 | */ 29 | getResponse(req: ODataRequest): ODataResponse | undefined { 30 | let scope = this.scope(req); 31 | return this.get(req.cacheKey, { scope }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/cache/storage.ts: -------------------------------------------------------------------------------- 1 | import { ODataRequest, ODataResponse } from '../resources'; 2 | import { ODataBaseCache, ODataCacheEntry } from './cache'; 3 | 4 | interface ResponseJson { 5 | body: any | null; 6 | headers: { [name: string]: string | string[] }; 7 | status: number; 8 | statusText: string; 9 | url: string | null; 10 | } 11 | 12 | export class ODataInStorageCache extends ODataBaseCache { 13 | name: string; 14 | storage: Storage; 15 | 16 | constructor({ 17 | name, 18 | storage = sessionStorage, 19 | timeout, 20 | }: { 21 | timeout?: number; 22 | name: string; 23 | storage?: Storage; 24 | }) { 25 | super({ timeout }); 26 | this.name = name; 27 | this.storage = storage; 28 | this.restore(); 29 | window.addEventListener('beforeunload', () => this.store()); 30 | } 31 | 32 | /** 33 | * Store the cache in the storage 34 | */ 35 | store() { 36 | this.storage.setItem( 37 | this.name, 38 | JSON.stringify(Array.from(this.entries.entries())), 39 | ); 40 | } 41 | 42 | /** 43 | * Restore the cache from the storage 44 | */ 45 | restore() { 46 | this.entries = new Map>( 47 | JSON.parse(this.storage.getItem(this.name) || '[]'), 48 | ); 49 | } 50 | 51 | /** 52 | * Flush the cache and clean the storage 53 | */ 54 | override flush() { 55 | super.flush(); 56 | this.store(); 57 | } 58 | 59 | /** 60 | * Store the response in the cache 61 | * @param req The request with the resource to store the response 62 | * @param res The response to store in the cache 63 | */ 64 | putResponse(req: ODataRequest, res: ODataResponse) { 65 | const scope = this.scope(req); 66 | const tags = this.tags(res); 67 | this.put(req.cacheKey, res.toJson(), { 68 | timeout: res.options.maxAge, 69 | scope, 70 | tags, 71 | }); 72 | } 73 | 74 | /** 75 | * Restore the response from the cache 76 | * @param req The request with the resource to get the response 77 | * @returns The response from the cache 78 | */ 79 | getResponse(req: ODataRequest): ODataResponse | undefined { 80 | const scope = this.scope(req); 81 | const data = this.get(req.cacheKey, { scope }); 82 | 83 | return data !== undefined ? ODataResponse.fromJson(req, data) : undefined; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const $ID = '$id'; 2 | export const ODATA_ID = '@odata.id'; 3 | 4 | // SEGMENTS 5 | export const $METADATA = '$metadata'; 6 | export const $BATCH = '$batch'; 7 | export const $REF = '$ref'; 8 | export const $VALUE = '$value'; 9 | export const $COUNT = '$count'; 10 | export const $QUERY = '$query'; 11 | export const $INLINECOUNT = '$inlinecount'; 12 | 13 | // HTTP HEADERS 14 | export const IF_MATCH_HEADER = 'If-Match'; 15 | export const IF_NONE_MATCH_HEADER = 'If-None-Match'; 16 | export const CONTENT_TYPE = 'Content-Type'; 17 | export const HTTP11 = 'HTTP/1.1'; 18 | export const ACCEPT = 'Accept'; 19 | export const PREFER = 'Prefer'; 20 | export const CACHE_CONTROL = 'Cache-Control'; 21 | export const CACHE_CONTROL_HEADERS = [ 22 | CACHE_CONTROL, 23 | CACHE_CONTROL.toLowerCase(), 24 | ]; 25 | export const ODATA_VERSION = 'OData-Version'; 26 | export const ODATA_VERSION_HEADERS = [ 27 | ODATA_VERSION, 28 | ODATA_VERSION.toLowerCase(), 29 | 'dataserviceversion', 30 | ]; 31 | export const LOCATION_HEADER = 'Location'; 32 | export const LOCATION_HEADERS = [ 33 | LOCATION_HEADER, 34 | LOCATION_HEADER.toLowerCase(), 35 | ]; 36 | export const ODATA_ENTITYID = 'OData-EntityId'; 37 | export const ODATA_ENTITYID_HEADERS = [ 38 | ODATA_ENTITYID, 39 | ODATA_ENTITYID.toLowerCase(), 40 | ]; 41 | export const PREFERENCE_APPLIED = 'Preference-Applied'; 42 | export const PREFERENCE_APPLIED_HEADERS = [ 43 | PREFERENCE_APPLIED, 44 | PREFERENCE_APPLIED.toLowerCase(), 45 | ]; 46 | export const ETAG_HEADER = 'ETag'; 47 | export const ETAG_HEADERS = [ETAG_HEADER, ETAG_HEADER.toLowerCase()]; 48 | 49 | export const RETRY_AFTER = 'Retry-After'; 50 | export const RETRY_AFTER_HEADERS = [RETRY_AFTER, RETRY_AFTER.toLowerCase()]; 51 | 52 | // HTTP HEADER VALUES 53 | export const APPLICATION_JSON = 'application/json'; 54 | export const APPLICATION_HTTP = 'application/http'; 55 | export const APPLICATION_XHTML = 'application/xhtml+xml'; 56 | export const APPLICATION_XML = 'application/xml'; 57 | export const TEXT_PLAIN = 'text/plain'; 58 | export const CONTENT_TYPE_ANY = '*/*'; 59 | export const MULTIPART_MIXED = 'multipart/mixed'; 60 | export const MULTIPART_MIXED_BOUNDARY = 'multipart/mixed;boundary='; 61 | export const CONTENT_TRANSFER_ENCODING = 'Content-Transfer-Encoding'; 62 | export const CONTENT_ID = 'Content-ID'; 63 | export const MAX_AGE = 'max-age'; 64 | 65 | // VERSIONS 66 | export const VERSION_4_0 = '4.0'; 67 | export const VERSION_3_0 = '3.0'; 68 | export const VERSION_2_0 = '2.0'; 69 | export const DEFAULT_VERSION = VERSION_4_0; 70 | 71 | export const BINARY = 'binary'; 72 | export const BOUNDARY_PREFIX_SUFFIX = '--'; 73 | export const BATCH_PREFIX = 'batch_'; 74 | export const CHANGESET_PREFIX = 'changeset_'; 75 | export const DEFAULT_METADATA = 'minimal'; 76 | export const DEFAULT_STRIP_METADATA = 'full'; 77 | export const DEFAULT_FETCH_POLICY = 'network-only'; 78 | export const DEFAULT_TIMEOUT = 60; // Time in seconds 79 | export const CALLABLE_BINDING_PARAMETER = 'bindingParameter'; 80 | export const XSSI_PREFIX = /^\)\]\}',?\n/; 81 | 82 | // URL PARTS 83 | export const QUERY_SEPARATOR = '?'; 84 | export const PARAM_SEPARATOR = '&'; 85 | export const VALUE_SEPARATOR = '='; 86 | export const PATH_SEPARATOR = '/'; 87 | export const ODATA_PARAM_PREFIX = '$'; 88 | export const ODATA_ALIAS_PREFIX = '@'; 89 | 90 | export const NEWLINE = '\r\n'; 91 | export const NEWLINE_REGEXP = /\r?\n/; 92 | export const CACHE_KEY_SEPARATOR = ':'; 93 | 94 | // Models 95 | export const CID_FIELD_NAME = '_cid'; 96 | export const EVENT_SPLITTER = /\s+/; 97 | 98 | // Standard vocabularies for annotating OData services 99 | // https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Core.V1.md 100 | 101 | export const COMPUTED = /.*Computed$/; 102 | export const OPTIMISTIC_CONCURRENCY = /.*OptimisticConcurrency$/; 103 | export const DESCRIPTION = /.*Description$/; 104 | export const LONG_DESCRIPTION = /.*LongDescription$/; 105 | export const OPTIONARL_PARAMETER = /.*OptionalParameter$/; 106 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './settings'; 3 | export * from './annotations'; 4 | export * from './api'; 5 | export * from './client'; 6 | export * from './module'; 7 | export * from './loaders'; 8 | 9 | // Utils 10 | export * from './utils/index'; 11 | 12 | // Services 13 | export * from './services/index'; 14 | 15 | // Schema 16 | export * from './schema/index'; 17 | 18 | // Models 19 | export * from './models/index'; 20 | 21 | // Resources 22 | export * from './resources/index'; 23 | 24 | // Cache 25 | export * from './cache/index'; 26 | 27 | // Metadata 28 | export * from './metadata/index'; 29 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/loaders.ts: -------------------------------------------------------------------------------- 1 | import { Observable, forkJoin, map, of } from 'rxjs'; 2 | import { ODataApiConfig } from './types'; 3 | import { ODataMetadataParser } from './metadata'; 4 | 5 | export abstract class ODataConfigLoader { 6 | abstract loadConfigs(): Observable; 7 | } 8 | 9 | export class ODataConfigSyncLoader implements ODataConfigLoader { 10 | constructor( 11 | private readonly passedConfigs: ODataApiConfig | ODataApiConfig[], 12 | ) {} 13 | 14 | loadConfigs(): Observable { 15 | return Array.isArray(this.passedConfigs) 16 | ? of(this.passedConfigs) 17 | : of([this.passedConfigs]); 18 | } 19 | } 20 | 21 | export class ODataConfigAsyncLoader implements ODataConfigLoader { 22 | constructor( 23 | private readonly configs$: 24 | | Observable[] 25 | | Observable, 26 | ) {} 27 | 28 | loadConfigs(): Observable { 29 | return Array.isArray(this.configs$) 30 | ? forkJoin(this.configs$) 31 | : (this.configs$ as Observable).pipe( 32 | map((value) => 33 | Array.isArray(value) 34 | ? (value as ODataApiConfig[]) 35 | : ([value] as ODataApiConfig[]), 36 | ), 37 | ); 38 | } 39 | } 40 | 41 | export class ODataMetadataLoader implements ODataConfigLoader { 42 | constructor( 43 | private readonly sources$: Observable, 44 | private readonly baseConfigs: ODataApiConfig | ODataApiConfig[], 45 | ) {} 46 | 47 | loadConfigs(): Observable { 48 | const configs = Array.isArray(this.baseConfigs) 49 | ? this.baseConfigs 50 | : [this.baseConfigs]; 51 | return this.sources$.pipe( 52 | map((source) => 53 | (Array.isArray(source) ? source : [source]).map((m, i) => 54 | new ODataMetadataParser(m).metadata().toConfig(configs[i] ?? {}), 55 | ), 56 | ), 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/csdl/csdl-entity-container.ts: -------------------------------------------------------------------------------- 1 | import { CsdlEntitySet } from './csdl-entity-set'; 2 | import { CsdlSingleton } from './csdl-singleton'; 3 | import { CsdlFunctionImport, CsdlActionImport } from './csdl-function-action'; 4 | import { CsdlAnnotable } from './csdl-annotation'; 5 | import type { CsdlSchema } from './csdl-schema'; 6 | import { ODataEntityContainerConfig } from '../../types'; 7 | 8 | export class CsdlEntityContainer extends CsdlAnnotable { 9 | public Name: string; 10 | public Extend?: string; 11 | public EntitySet?: CsdlEntitySet[]; 12 | public Singleton?: CsdlSingleton[]; 13 | public FunctionImport?: CsdlFunctionImport[]; 14 | public ActionImport?: CsdlActionImport[]; 15 | 16 | constructor( 17 | private schema: CsdlSchema, 18 | { 19 | Name, 20 | Extend, 21 | EntitySet, 22 | Singleton, 23 | FunctionImport, 24 | ActionImport, 25 | Annotation, 26 | }: { 27 | Name: string; 28 | Extend?: string; 29 | EntitySet?: any[]; 30 | Singleton?: any[]; 31 | FunctionImport?: any[]; 32 | ActionImport?: any[]; 33 | Annotation?: any[]; 34 | }, 35 | ) { 36 | super({ Annotation }); 37 | 38 | this.Name = Name; 39 | this.Extend = Extend; 40 | this.EntitySet = EntitySet?.map((e) => new CsdlEntitySet(this, e)); 41 | this.Singleton = Singleton?.map((s) => new CsdlSingleton(this, s)); 42 | this.FunctionImport = FunctionImport?.map( 43 | (f) => new CsdlFunctionImport(this, f), 44 | ); 45 | this.ActionImport = ActionImport?.map((a) => new CsdlActionImport(this, a)); 46 | } 47 | 48 | override toJson() { 49 | const json: { [key: string]: any } = { ...super.toJson() }; 50 | if (this.Extend !== undefined) { 51 | json['Extend'] = this.Extend; 52 | } 53 | if (Array.isArray(this.EntitySet) && this.EntitySet.length > 0) { 54 | json['EntitySet'] = this.EntitySet.map((a) => a.toJson()); 55 | } 56 | if (Array.isArray(this.Singleton) && this.Singleton.length > 0) { 57 | json['Singleton'] = this.Singleton.map((a) => a.toJson()); 58 | } 59 | if (Array.isArray(this.FunctionImport) && this.FunctionImport.length > 0) { 60 | json['FunctionImport'] = this.FunctionImport.map((a) => a.toJson()); 61 | } 62 | if (Array.isArray(this.ActionImport) && this.ActionImport.length > 0) { 63 | json['ActionImport'] = this.ActionImport.map((a) => a.toJson()); 64 | } 65 | return json; 66 | } 67 | 68 | name() { 69 | return `${this.Name}`; 70 | } 71 | 72 | namespace() { 73 | return `${this.schema.Namespace}`; 74 | } 75 | 76 | fullName() { 77 | return `${this.schema.Namespace}.${this.Name}`; 78 | } 79 | 80 | override toConfig( 81 | base?: Partial, 82 | ): ODataEntityContainerConfig { 83 | return { 84 | ...super.toConfig(), 85 | name: this.Name, 86 | entitySets: this.EntitySet?.map((t) => t.toConfig()), 87 | }; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/csdl/csdl-entity-set.ts: -------------------------------------------------------------------------------- 1 | import { ODataEntitySetConfig } from '../../types'; 2 | import { CsdlAnnotable } from './csdl-annotation'; 3 | import type { CsdlEntityContainer } from './csdl-entity-container'; 4 | import { CsdlNavigationPropertyBinding } from './csdl-navigation-property-binding'; 5 | 6 | export class CsdlEntitySet extends CsdlAnnotable { 7 | public Name: string; 8 | public EntityType: string; 9 | public NavigationPropertyBinding?: CsdlNavigationPropertyBinding[]; 10 | public IncludeInServiceDocument?: boolean; 11 | 12 | constructor( 13 | private container: CsdlEntityContainer, 14 | { 15 | Name, 16 | EntityType, 17 | NavigationPropertyBinding, 18 | IncludeInServiceDocument, 19 | Annotation, 20 | }: { 21 | Name: string; 22 | EntityType: string; 23 | NavigationPropertyBinding?: any[]; 24 | IncludeInServiceDocument?: boolean; 25 | Annotation?: any[]; 26 | }, 27 | ) { 28 | super({ Annotation }); 29 | 30 | this.Name = Name; 31 | this.EntityType = EntityType; 32 | this.NavigationPropertyBinding = NavigationPropertyBinding?.map( 33 | (n) => new CsdlNavigationPropertyBinding(n), 34 | ); 35 | this.IncludeInServiceDocument = IncludeInServiceDocument; 36 | } 37 | 38 | override toJson() { 39 | const json: { [key: string]: any } = { 40 | ...super.toJson(), 41 | Name: this.Name, 42 | EntityType: this.EntityType, 43 | }; 44 | if ( 45 | Array.isArray(this.NavigationPropertyBinding) && 46 | this.NavigationPropertyBinding.length > 0 47 | ) { 48 | json['NavigationPropertyBinding'] = this.NavigationPropertyBinding.map( 49 | (n) => n.toJson(), 50 | ); 51 | } 52 | if (this.IncludeInServiceDocument !== undefined) { 53 | json['IncludeInServiceDocument'] = this.IncludeInServiceDocument; 54 | } 55 | return json; 56 | } 57 | 58 | name() { 59 | return `${this.Name}`; 60 | } 61 | 62 | namespace() { 63 | return `${this.container.namespace()}`; 64 | } 65 | 66 | fullName() { 67 | return `${this.container.namespace()}.${this.Name}`; 68 | } 69 | 70 | override toConfig(): ODataEntitySetConfig { 71 | return { 72 | ...super.toConfig(), 73 | name: this.Name, 74 | entityType: this.EntityType, 75 | service: {}, 76 | } as ODataEntitySetConfig; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/csdl/csdl-enum-type.ts: -------------------------------------------------------------------------------- 1 | import { ODataEnumTypeConfig, ODataEnumTypeFieldConfig } from '../../types'; 2 | import { CsdlAnnotable } from './csdl-annotation'; 3 | import type { CsdlSchema } from './csdl-schema'; 4 | 5 | export class CsdlEnumType extends CsdlAnnotable { 6 | Name: string; 7 | Member: CsdlMember[]; 8 | UnderlyingType?: string; 9 | IsFlags?: boolean; 10 | constructor( 11 | private schema: CsdlSchema, 12 | { 13 | Name, 14 | Member, 15 | UnderlyingType, 16 | IsFlags, 17 | Annotation, 18 | }: { 19 | Name: string; 20 | Member: any[]; 21 | UnderlyingType?: string; 22 | IsFlags?: boolean; 23 | Annotation?: any[]; 24 | }, 25 | ) { 26 | super({ Annotation }); 27 | this.Name = Name; 28 | this.Member = Member.map((m) => new CsdlMember(m)); 29 | this.UnderlyingType = UnderlyingType; 30 | this.IsFlags = IsFlags; 31 | } 32 | 33 | override toJson() { 34 | const json: { [key: string]: any } = { 35 | ...super.toJson(), 36 | Name: this.Name, 37 | Member: this.Member.map((m) => m.toJson()), 38 | }; 39 | if (this.UnderlyingType !== undefined) { 40 | json['UnderlyingType'] = this.UnderlyingType; 41 | } 42 | if (this.IsFlags !== undefined) { 43 | json['IsFlags'] = this.IsFlags; 44 | } 45 | return json; 46 | } 47 | 48 | name() { 49 | return `${this.Name}`; 50 | } 51 | 52 | namespace() { 53 | return `${this.schema.Namespace}`; 54 | } 55 | 56 | fullName() { 57 | return `${this.schema.Namespace}.${this.Name}`; 58 | } 59 | 60 | override toConfig(base?: Partial): ODataEnumTypeConfig { 61 | return { 62 | ...super.toConfig(), 63 | name: this.Name, 64 | fields: this.Member.reduce( 65 | (acc, m) => ({ 66 | ...acc, 67 | [m.Name]: m.toConfig(), 68 | }), 69 | {}, 70 | ), 71 | flags: this.IsFlags, 72 | } as ODataEnumTypeConfig; 73 | } 74 | } 75 | 76 | export class CsdlMember extends CsdlAnnotable { 77 | Name: string; 78 | Value?: number; 79 | constructor({ 80 | Name, 81 | Value, 82 | Annotation, 83 | }: { 84 | Name: string; 85 | Value?: number; 86 | Annotation?: any[]; 87 | }) { 88 | super({ Annotation }); 89 | this.Name = Name; 90 | this.Value = Value; 91 | } 92 | 93 | override toJson() { 94 | const json: { [key: string]: any } = { ...super.toJson(), Name: this.Name }; 95 | if (this.Value !== undefined) { 96 | json['Value'] = this.Value; 97 | } 98 | return json; 99 | } 100 | 101 | override toConfig( 102 | base?: Partial, 103 | ): ODataEnumTypeFieldConfig { 104 | const config: { [key: string]: any } = { 105 | ...super.toConfig(), 106 | value: this.Value, 107 | }; 108 | return config as ODataEnumTypeFieldConfig; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/csdl/csdl-navigation-property-binding.ts: -------------------------------------------------------------------------------- 1 | export class CsdlNavigationPropertyBinding { 2 | Path: string; 3 | Target: string; 4 | 5 | constructor({ Path, Target }: { Path: string; Target: string }) { 6 | this.Path = Path; 7 | this.Target = Target; 8 | } 9 | 10 | toJson() { 11 | return { 12 | Path: this.Path, 13 | Target: this.Target, 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/csdl/csdl-reference.ts: -------------------------------------------------------------------------------- 1 | import { ODataReferenceConfig } from '../../types'; 2 | import { CsdlAnnotable } from './csdl-annotation'; 3 | 4 | export class CsdlReference extends CsdlAnnotable { 5 | Uri: string; 6 | Include?: CsdlInclude[]; 7 | IncludeAnnotations?: CsdlIncludeAnnotations[]; 8 | constructor({ 9 | Uri, 10 | Include, 11 | IncludeAnnotations, 12 | Annotation, 13 | }: { 14 | Uri: string; 15 | Include?: any[]; 16 | IncludeAnnotations?: any[]; 17 | Annotation?: any[]; 18 | }) { 19 | super({ Annotation }); 20 | this.Uri = Uri; 21 | this.Include = Include?.map((i) => new CsdlInclude(i)); 22 | this.IncludeAnnotations = IncludeAnnotations?.map( 23 | (i) => new CsdlIncludeAnnotations(i), 24 | ); 25 | } 26 | 27 | override toJson() { 28 | const json: { [key: string]: any } = { ...super.toJson(), Uri: this.Uri }; 29 | if (Array.isArray(this.Include) && this.Include.length > 0) { 30 | json['Include'] = this.Include.map((i) => i.toJson()); 31 | } 32 | if ( 33 | Array.isArray(this.IncludeAnnotations) && 34 | this.IncludeAnnotations.length > 0 35 | ) { 36 | json['IncludeAnnotations'] = this.IncludeAnnotations.map((i) => 37 | i.toJson(), 38 | ); 39 | } 40 | return json; 41 | } 42 | 43 | override toConfig(base?: Partial) { 44 | return { 45 | ...super.toConfig(), 46 | uri: this.Uri, 47 | includes: this.Include?.map((i) => i.toConfig()), 48 | includeAnnotations: this.IncludeAnnotations?.map((i) => i.toConfig()), 49 | } as ODataReferenceConfig; 50 | } 51 | } 52 | 53 | export class CsdlInclude { 54 | Namespace: string; 55 | Alias?: string; 56 | constructor({ Namespace, Alias }: { Namespace: string; Alias?: string }) { 57 | this.Namespace = Namespace; 58 | this.Alias = Alias; 59 | } 60 | 61 | toJson() { 62 | return { 63 | Namespace: this.Namespace, 64 | Alias: this.Alias, 65 | }; 66 | } 67 | 68 | toConfig() { 69 | return { 70 | namespace: this.Namespace, 71 | alias: this.Alias, 72 | }; 73 | } 74 | } 75 | 76 | export class CsdlIncludeAnnotations { 77 | TermNamespace: string; 78 | Qualifier?: string; 79 | TargetNamespace?: string; 80 | constructor({ 81 | TermNamespace, 82 | Qualifier, 83 | TargetNamespace, 84 | }: { 85 | TermNamespace: string; 86 | Qualifier?: string; 87 | TargetNamespace?: string; 88 | }) { 89 | this.TermNamespace = TermNamespace; 90 | this.Qualifier = Qualifier; 91 | this.TargetNamespace = TargetNamespace; 92 | } 93 | 94 | toJson() { 95 | return { 96 | TermNamespace: this.TermNamespace, 97 | Qualifier: this.Qualifier, 98 | TargetNamespace: this.TargetNamespace, 99 | }; 100 | } 101 | 102 | toConfig() { 103 | return { 104 | termNamespace: this.TermNamespace, 105 | qualifier: this.Qualifier, 106 | targetNamespace: this.TargetNamespace, 107 | }; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/csdl/csdl-singleton.ts: -------------------------------------------------------------------------------- 1 | import { CsdlAnnotable } from './csdl-annotation'; 2 | import type { CsdlEntityContainer } from './csdl-entity-container'; 3 | import { CsdlNavigationPropertyBinding } from './csdl-navigation-property-binding'; 4 | 5 | export class CsdlSingleton extends CsdlAnnotable { 6 | Name: string; 7 | Type: string; 8 | NavigationPropertyBindings?: CsdlNavigationPropertyBinding[]; 9 | 10 | constructor( 11 | private container: CsdlEntityContainer, 12 | { 13 | Name, 14 | Type, 15 | NavigationPropertyBindings, 16 | Annotation, 17 | }: { 18 | Name: string; 19 | Type: string; 20 | NavigationPropertyBindings?: any[]; 21 | Annotation?: any[]; 22 | }, 23 | ) { 24 | super({ Annotation }); 25 | this.Name = Name; 26 | this.Type = Type; 27 | this.NavigationPropertyBindings = NavigationPropertyBindings?.map( 28 | (n) => new CsdlNavigationPropertyBinding(n), 29 | ); 30 | } 31 | 32 | override toJson() { 33 | const json: { [key: string]: any } = { 34 | ...super.toJson(), 35 | Name: this.Name, 36 | Type: this.Type, 37 | }; 38 | if ( 39 | Array.isArray(this.NavigationPropertyBindings) && 40 | this.NavigationPropertyBindings.length > 0 41 | ) { 42 | json['NavigationPropertyBindings'] = this.NavigationPropertyBindings.map( 43 | (n) => n.toJson(), 44 | ); 45 | } 46 | return json; 47 | } 48 | 49 | name() { 50 | return `${this.Name}`; 51 | } 52 | 53 | namespace() { 54 | return `${this.container.namespace()}`; 55 | } 56 | 57 | fullName() { 58 | return `${this.container.namespace()}.${this.Name}`; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/csdl/csdl-type-definition.ts: -------------------------------------------------------------------------------- 1 | import { CsdlAnnotable, CsdlAnnotation } from './csdl-annotation'; 2 | import type { CsdlSchema } from './csdl-schema'; 3 | 4 | export class CsdlTypeDefinition extends CsdlAnnotable { 5 | public Name: string; 6 | public UnderlayingType: string; 7 | public MaxLength?: number; 8 | public Precision?: number; 9 | public Scale?: number; 10 | public Unicode?: boolean; 11 | public SRID?: string; 12 | 13 | constructor( 14 | private schema: CsdlSchema, 15 | { 16 | Name, 17 | UnderlayingType, 18 | MaxLength, 19 | Precision, 20 | Scale, 21 | Unicode, 22 | SRID, 23 | Annotation, 24 | }: { 25 | Name: string; 26 | UnderlayingType: string; 27 | MaxLength?: number; 28 | Precision?: number; 29 | Scale?: number; 30 | Unicode?: boolean; 31 | SRID?: string; 32 | Annotation?: CsdlAnnotation[]; 33 | }, 34 | ) { 35 | super({ Annotation }); 36 | this.Name = Name; 37 | this.UnderlayingType = UnderlayingType; 38 | this.MaxLength = MaxLength; 39 | this.Precision = Precision; 40 | this.Scale = Scale; 41 | this.Unicode = Unicode; 42 | this.SRID = SRID; 43 | } 44 | 45 | override toJson() { 46 | const json: { [key: string]: any } = { 47 | ...super.toJson(), 48 | Name: this.Name, 49 | UnderlayingType: this.UnderlayingType, 50 | }; 51 | if (this.MaxLength !== undefined) { 52 | json['MaxLength'] = this.MaxLength; 53 | } 54 | if (this.Precision !== undefined) { 55 | json['Precision'] = this.Precision; 56 | } 57 | if (this.Scale !== undefined) { 58 | json['Scale'] = this.Scale; 59 | } 60 | if (this.Unicode !== undefined) { 61 | json['Unicode'] = this.Unicode; 62 | } 63 | if (this.SRID !== undefined) { 64 | json['SRID'] = this.SRID; 65 | } 66 | return json; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/index.ts: -------------------------------------------------------------------------------- 1 | export * from './metadata'; 2 | export * from './parser'; 3 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/metadata.ts: -------------------------------------------------------------------------------- 1 | import { CsdlAction, CsdlFunction } from './csdl/csdl-function-action'; 2 | import { CsdlReference } from './csdl/csdl-reference'; 3 | import { CsdlSchema } from './csdl/csdl-schema'; 4 | import { ODataApiConfig, ODataVersion } from '../types'; 5 | 6 | export class ODataMetadata { 7 | Version: string; 8 | References: CsdlReference[]; 9 | Schemas: CsdlSchema[]; 10 | constructor(Version: string, References: any[], Schemas: any[]) { 11 | this.Version = Version; 12 | this.References = References?.map((r) => new CsdlReference(r)); 13 | this.Schemas = Schemas?.map((s) => new CsdlSchema(s)); 14 | } 15 | 16 | toConfig(base?: Partial): ODataApiConfig { 17 | return { 18 | ...base, 19 | version: base?.version ?? (this.Version as ODataVersion), 20 | schemas: (this.Schemas ?? []).map((ms) => 21 | ms.toConfig(base?.schemas?.find((cs) => cs.namespace === ms.Namespace)), 22 | ), 23 | references: (this.References ?? []).map((mr) => 24 | mr.toConfig(base?.references?.find((cs) => cs.uri === mr.Uri)), 25 | ), 26 | } as ODataApiConfig; 27 | } 28 | 29 | toJson() { 30 | return { 31 | Version: this.Version, 32 | References: this.References.map((r) => r.toJson()), 33 | Schemas: this.Schemas.map((s) => s.toJson()), 34 | }; 35 | } 36 | 37 | functions() { 38 | return this.Schemas.reduce((acc, s) => { 39 | return [...acc, ...(s.Function ?? [])]; 40 | }, [] as CsdlFunction[]); 41 | } 42 | 43 | actions() { 44 | return this.Schemas.reduce((acc, s) => { 45 | return [...acc, ...(s.Action ?? [])]; 46 | }, [] as CsdlAction[]); 47 | } 48 | 49 | static fromJson(json: any): ODataMetadata { 50 | return new ODataMetadata(json.Version, json.References, json.Schemas); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './options'; 2 | export * from './model'; 3 | export * from './collection'; 4 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { HttpClientModule } from '@angular/common/http'; 3 | import { 4 | EnvironmentProviders, 5 | InjectionToken, 6 | ModuleWithProviders, 7 | NgModule, 8 | Provider, 9 | makeEnvironmentProviders, 10 | } from '@angular/core'; 11 | import { ODataClient } from './client'; 12 | import { ODataConfigLoader, ODataConfigSyncLoader } from './loaders'; 13 | import { ODataServiceFactory } from './services/index'; 14 | import { ODataApiConfig } from './types'; 15 | 16 | export interface PassedInitialConfig { 17 | config?: ODataApiConfig | ODataApiConfig[]; 18 | loader?: Provider; 19 | } 20 | 21 | export const ODATA_CONFIG = new InjectionToken('odata.config'); 22 | 23 | export function createSyncLoader(passedConfig: PassedInitialConfig) { 24 | return new ODataConfigSyncLoader(passedConfig.config!); 25 | } 26 | 27 | // Standalone version 28 | export function provideODataClient( 29 | passedConfig: PassedInitialConfig, 30 | ): EnvironmentProviders { 31 | return makeEnvironmentProviders([ 32 | { provide: ODATA_CONFIG, useValue: passedConfig }, 33 | passedConfig?.loader ?? { 34 | provide: ODataConfigLoader, 35 | useFactory: createSyncLoader, 36 | deps: [ODATA_CONFIG], 37 | }, 38 | ODataClient, 39 | ODataServiceFactory, 40 | ]); 41 | } 42 | 43 | // Module version 44 | @NgModule({ 45 | imports: [CommonModule, HttpClientModule], 46 | providers: [ODataClient, ODataServiceFactory], 47 | }) 48 | export class ODataModule { 49 | static forRoot( 50 | passedConfig: PassedInitialConfig, 51 | ): ModuleWithProviders { 52 | return { 53 | ngModule: ODataModule, 54 | providers: [ 55 | // Make the ODATA_CONFIG available through injection 56 | { provide: ODATA_CONFIG, useValue: passedConfig }, 57 | 58 | // Create the loader: Either the one getting passed or a sync one 59 | passedConfig?.loader ?? { 60 | provide: ODataConfigLoader, 61 | useFactory: createSyncLoader, 62 | deps: [ODATA_CONFIG], 63 | }, 64 | ODataClient, 65 | ODataServiceFactory, 66 | ], 67 | }; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/index.ts: -------------------------------------------------------------------------------- 1 | export * from './query'; 2 | export * from './path'; 3 | export * from './request'; 4 | export * from './resource'; 5 | export * from './types'; 6 | export * from './response'; 7 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/options.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CacheCacheability, 3 | ODataMetadataType, 4 | ODataVersion, 5 | ParserOptions, 6 | ResponseOptions, 7 | } from '../types'; 8 | import { 9 | DEFAULT_VERSION, 10 | MAX_AGE, 11 | VERSION_2_0, 12 | VERSION_3_0, 13 | VERSION_4_0, 14 | } from '../constants'; 15 | 16 | import { ODataHelper } from '../helper'; 17 | 18 | export class ODataResponseOptions implements ResponseOptions { 19 | version: ODataVersion; 20 | streaming?: boolean; 21 | // OData 22 | metadata?: ODataMetadataType; 23 | ieee754Compatible?: boolean; 24 | // Location 25 | location?: string; 26 | // Cache 27 | cacheability?: 'public' | 'private' | 'no-cache' | 'no-store'; 28 | maxAge?: number; 29 | 30 | constructor(config: ParserOptions) { 31 | this.version = config.version || DEFAULT_VERSION; 32 | } 33 | 34 | get helper() { 35 | return ODataHelper[this.version]; 36 | } 37 | 38 | clone() { 39 | return new ODataResponseOptions(this); 40 | } 41 | 42 | setFeatures(features: string) { 43 | features.split(';').forEach((o) => { 44 | let [k, v] = o.split('='); 45 | switch (k.trim()) { 46 | case 'odata.metadata': 47 | this.metadata = v as ODataMetadataType; 48 | break; 49 | case 'odata.streaming': 50 | this.streaming = v == 'true'; 51 | break; 52 | case 'IEEE754Compatible': 53 | this.ieee754Compatible = v == 'true'; 54 | break; 55 | } 56 | }); 57 | } 58 | 59 | setVersion(version: string) { 60 | const value = version.replace(/\;/g, '').trim(); 61 | if ([VERSION_2_0, VERSION_3_0, VERSION_4_0].indexOf(value) !== -1) 62 | this.version = value as ODataVersion; 63 | } 64 | 65 | setLocation(location: string) { 66 | // TODO: resolve location? 67 | this.location = location; 68 | } 69 | 70 | setPreferenceApplied(preference: string) { 71 | preference.split(',').forEach((prefer) => { 72 | // TODO: resolve preference 73 | }); 74 | } 75 | 76 | setCache(cacheControl: string) { 77 | cacheControl.split(',').forEach((directive) => { 78 | if (directive.startsWith(MAX_AGE)) { 79 | let maxAge = Number(directive.split('=')[1]); 80 | if (!Number.isNaN(maxAge)) this.maxAge = maxAge; 81 | } 82 | if ( 83 | ['public', 'private', 'no-cache', 'no-store'].indexOf(directive) !== -1 84 | ) { 85 | this.cacheability = directive as CacheCacheability; 86 | } 87 | }); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/path/handlers.ts: -------------------------------------------------------------------------------- 1 | import { PathSegment } from '../../types'; 2 | import { Types } from '../../utils'; 3 | import { EntityKey } from '../resource'; 4 | import { ODataPathSegments, ODataSegment } from './segments'; 5 | 6 | export class SegmentHandler { 7 | constructor(private segment: ODataSegment) {} 8 | get name() { 9 | return this.segment.name; 10 | } 11 | outgoingType(value?: string) { 12 | if (value !== undefined) this.segment.outgoingType = value; 13 | return this.segment.outgoingType; 14 | } 15 | incomingType(value?: string) { 16 | if (value !== undefined) this.segment.incomingType = value; 17 | return this.segment.incomingType; 18 | } 19 | bindingType(value?: string) { 20 | if (value !== undefined) this.segment.bindingType = value; 21 | return this.segment.bindingType; 22 | } 23 | path(value?: string) { 24 | if (value !== undefined) this.segment.path = value; 25 | return this.segment.path; 26 | } 27 | key(value?: EntityKey) { 28 | if (value !== undefined) this.segment.key = value; 29 | return this.segment.key as EntityKey; 30 | } 31 | hasKey() { 32 | return !Types.isEmpty(this.segment.key); 33 | } 34 | clearKey() { 35 | delete this.segment.key; 36 | } 37 | parameters(value?: T) { 38 | if (value !== undefined) this.segment.parameters = value; 39 | return this.segment.parameters as T; 40 | } 41 | hasParameters() { 42 | return !Types.isEmpty(this.segment.parameters); 43 | } 44 | clearParameters() { 45 | delete this.segment.parameters; 46 | } 47 | } 48 | 49 | export class ODataPathSegmentsHandler { 50 | constructor(protected segments: ODataPathSegments) {} 51 | entitySet() { 52 | return this.segments.get(PathSegment.entitySet); 53 | } 54 | singleton() { 55 | return this.segments.get(PathSegment.singleton); 56 | } 57 | action() { 58 | return this.segments.get(PathSegment.action); 59 | } 60 | function() { 61 | return this.segments.get(PathSegment.function); 62 | } 63 | keys(values?: (EntityKey | undefined)[]) { 64 | return this.segments.keys(values); 65 | } 66 | property() { 67 | return this.segments.get(PathSegment.property); 68 | } 69 | navigationProperty() { 70 | return this.segments.get(PathSegment.navigationProperty); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/path/index.ts: -------------------------------------------------------------------------------- 1 | export * from './segments'; 2 | export * from './handlers'; 3 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/base.ts: -------------------------------------------------------------------------------- 1 | import { Parser, ParserOptions } from '../../../types'; 2 | import { Types } from '../../../utils'; 3 | import { QueryCustomType } from '../builder'; 4 | import { Renderable } from './syntax'; 5 | 6 | export abstract class Expression implements Renderable { 7 | protected _children: Renderable[]; 8 | constructor({ 9 | children, 10 | }: { 11 | children?: Renderable[]; 12 | } = {}) { 13 | this._children = children || []; 14 | } 15 | 16 | get [Symbol.toStringTag]() { 17 | return 'Expression'; 18 | } 19 | 20 | abstract render({ 21 | aliases, 22 | escape, 23 | prefix, 24 | parser, 25 | options, 26 | }: { 27 | aliases?: QueryCustomType[]; 28 | escape?: boolean; 29 | prefix?: string; 30 | parser?: Parser; 31 | options?: ParserOptions; 32 | }): string; 33 | 34 | abstract clone(): Expression; 35 | 36 | children() { 37 | return [...this._children]; 38 | } 39 | 40 | length() { 41 | return this._children.length; 42 | } 43 | 44 | toJson() { 45 | return { 46 | $type: Types.rawType(this), 47 | children: this._children.map((c) => c.toJson()), 48 | }; 49 | } 50 | 51 | resolve(parser: any) { 52 | return parser; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/compute.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComputeExpression } from './compute'; 2 | 3 | describe('OData compute builder', () => { 4 | interface Pet { 5 | Id?: number; 6 | Name?: string; 7 | Age?: number; 8 | Person?: Person; 9 | } 10 | 11 | interface Model { 12 | Id?: number; 13 | } 14 | 15 | interface Car { 16 | Id?: number; 17 | Model?: Model; 18 | Year?: number; 19 | } 20 | 21 | interface Person { 22 | Id?: number; 23 | Name?: string; 24 | Age?: number; 25 | IsCorrect?: boolean; 26 | EditedOn?: Date; 27 | CreatedOn?: Date; 28 | BornOn?: Date; 29 | Car?: Car; 30 | Pets?: Pet[]; 31 | } 32 | 33 | describe('base condition', () => { 34 | it('field', () => { 35 | const compare1 = ComputeExpression.factory(({ e, t }) => 36 | e().field('Class', ({ f }) => f.year(t.BornOn)), 37 | ); 38 | 39 | expect(compare1.render()).toBe('year(BornOn) as Class'); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/compute.ts: -------------------------------------------------------------------------------- 1 | import { Parser, ParserOptions } from '../../../types'; 2 | import { QueryCustomType } from '../builder'; 3 | import { Expression } from './base'; 4 | import { 5 | FieldFactory, 6 | functions, 7 | ODataFunctions, 8 | ODataOperators, 9 | operators, 10 | Renderable, 11 | RenderableFactory, 12 | } from './syntax'; 13 | 14 | export type ComputeExpressionBuilder = { 15 | t: Required; 16 | e: () => ComputeExpression; 17 | }; 18 | export class ComputeExpression extends Expression { 19 | protected names: string[]; 20 | constructor({ 21 | children, 22 | names, 23 | }: { 24 | children?: Renderable[]; 25 | names?: string[]; 26 | } = {}) { 27 | super({ children }); 28 | this.names = names ?? []; 29 | } 30 | 31 | override get [Symbol.toStringTag]() { 32 | return 'ComputeExpression'; 33 | } 34 | 35 | static factory( 36 | opts: ( 37 | builder: ComputeExpressionBuilder, 38 | current: ComputeExpression, 39 | ) => ComputeExpression, 40 | current?: ComputeExpression, 41 | ): ComputeExpression { 42 | return opts( 43 | { 44 | t: FieldFactory>(), 45 | e: () => new ComputeExpression(), 46 | }, 47 | current ?? new ComputeExpression(), 48 | ) as ComputeExpression; 49 | } 50 | 51 | override toJson() { 52 | const json = super.toJson(); 53 | return Object.assign(json, { 54 | names: this.names, 55 | }); 56 | } 57 | 58 | static fromJson(json: { [name: string]: any }): ComputeExpression { 59 | return new ComputeExpression({ 60 | children: json['children'].map((c: any) => RenderableFactory(c)), 61 | names: json['names'], 62 | }); 63 | } 64 | 65 | render({ 66 | aliases, 67 | escape, 68 | prefix, 69 | parser, 70 | options, 71 | }: { 72 | aliases?: QueryCustomType[]; 73 | escape?: boolean; 74 | prefix?: string; 75 | parser?: Parser; 76 | options?: ParserOptions; 77 | } = {}): string { 78 | const children = this._children.map((n) => 79 | n.render({ aliases, escape, prefix, parser, options }), 80 | ); 81 | return this.names 82 | .map((name, index) => `${children[index]} as ${name}`) 83 | .join(','); 84 | } 85 | 86 | clone() { 87 | return new ComputeExpression({ 88 | children: this._children.map((c) => c.clone()), 89 | names: [...this.names], 90 | }); 91 | } 92 | 93 | private _add(name: string, node: Renderable): ComputeExpression { 94 | this.names.push(name); 95 | this._children.push(node); 96 | return this; 97 | } 98 | 99 | field( 100 | name: string, 101 | opts: (e: { o: ODataOperators; f: ODataFunctions }) => Renderable, 102 | ): ComputeExpression { 103 | const node = opts({ 104 | o: operators as ODataOperators, 105 | f: functions as ODataFunctions, 106 | }); 107 | return this._add(name, node); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/count.spec.ts: -------------------------------------------------------------------------------- 1 | import { CountExpression } from './count'; 2 | 3 | describe('OData orderBy builder', () => { 4 | interface Pet { 5 | Id?: number; 6 | Name?: string; 7 | Age?: number; 8 | Person?: Person; 9 | } 10 | 11 | interface Model { 12 | Id?: number; 13 | Name?: string; 14 | } 15 | 16 | interface Car { 17 | Id?: number; 18 | Model?: Model; 19 | Year?: number; 20 | } 21 | 22 | interface Person { 23 | Id?: number; 24 | Name?: string; 25 | Age?: number; 26 | IsCorrect?: boolean; 27 | EditedOn?: boolean; 28 | CreatedOn?: boolean; 29 | Car?: Car; 30 | Pets?: Pet[]; 31 | } 32 | 33 | describe('base condition', () => { 34 | describe('as factory function', () => { 35 | it('count', () => { 36 | const compare1 = CountExpression.factory(({ e, t }) => 37 | e().field(t.Pets), 38 | ); 39 | 40 | expect(compare1.render()).toBe('Pets/$count'); 41 | }); 42 | 43 | it('count filter', () => { 44 | const compare1 = CountExpression.factory(({ e, t }) => 45 | e().field(t.Pets, ({ f }) => 46 | f.filter(({ e, t }) => e().gt(t.Age, 3)), 47 | ), 48 | ); 49 | 50 | expect(compare1.render()).toBe('Pets/$count($filter=Age gt 3)'); 51 | }); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/expand.spec.ts: -------------------------------------------------------------------------------- 1 | import { ExpandExpression } from './expand'; 2 | 3 | describe('OData search builder', () => { 4 | interface Pet { 5 | Id?: number; 6 | Name?: string; 7 | Age?: number; 8 | Person?: Person; 9 | } 10 | 11 | interface Model { 12 | Id?: number; 13 | Name?: string; 14 | } 15 | 16 | interface Car { 17 | Id?: number; 18 | Model?: Model; 19 | Year?: number; 20 | } 21 | 22 | interface Person { 23 | Id?: number; 24 | Name?: string; 25 | Age?: number; 26 | IsCorrect?: boolean; 27 | EditedOn?: boolean; 28 | CreatedOn?: boolean; 29 | Car?: Car; 30 | Pets?: Pet[]; 31 | } 32 | 33 | describe('base condition', () => { 34 | describe('as factory function', () => { 35 | it('field', () => { 36 | const compare1 = ExpandExpression.factory(({ e, t }) => 37 | e().field(t.Car), 38 | ); 39 | 40 | expect(compare1.render()).toBe('Car'); 41 | }); 42 | 43 | it('navigation', () => { 44 | const compare1 = ExpandExpression.factory(({ e, t }) => 45 | e().field(t.Car?.Model), 46 | ); 47 | 48 | expect(compare1.render()).toBe('Car/Model'); 49 | }); 50 | }); 51 | }); 52 | 53 | describe('nested condition', () => { 54 | describe('as factory function', () => { 55 | it('field', () => { 56 | const compare1 = ExpandExpression.factory(({ e, t }) => 57 | e().field(t.Car, (f) => { 58 | f.expand(({ e, t }) => e().field(t.Model)); 59 | f.skip(1); 60 | f.filter(({ e, t }) => e().eq(t.Year, 2000)); 61 | }), 62 | ); 63 | 64 | expect(compare1.render()).toBe( 65 | 'Car($expand=Model;$filter=Year eq 2000;$skip=1)', 66 | ); 67 | }); 68 | 69 | it('navigation', () => { 70 | const compare1 = ExpandExpression.factory(({ e, t }) => 71 | e().field(t.Car.Model!, (f) => { 72 | f.filter(({ e, t }) => e().in(t.Name, ['BMW', 'Audi'])); 73 | f.skip(1); 74 | f.top(1); 75 | }), 76 | ); 77 | 78 | expect(compare1.render()).toBe( 79 | "Car/Model($filter=Name in ('BMW','Audi');$skip=1;$top=1)", 80 | ); 81 | }); 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './base'; 2 | export * from './compute'; 3 | export * from './apply'; 4 | export * from './filter'; 5 | export * from './orderby'; 6 | export * from './search'; 7 | export * from './select'; 8 | export * from './syntax'; 9 | export * from './expand'; 10 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/orderby.spec.ts: -------------------------------------------------------------------------------- 1 | import { OrderByExpression } from './orderby'; 2 | 3 | describe('OData orderBy builder', () => { 4 | interface Pet { 5 | Id?: number; 6 | Name?: string; 7 | Age?: number; 8 | Person?: Person; 9 | } 10 | 11 | interface Model { 12 | Id?: number; 13 | Name?: string; 14 | } 15 | 16 | interface Car { 17 | Id?: number; 18 | Model?: Model; 19 | Year?: number; 20 | } 21 | 22 | interface Person { 23 | Id?: number; 24 | Name?: string; 25 | Age?: number; 26 | IsCorrect?: boolean; 27 | EditedOn?: boolean; 28 | CreatedOn?: boolean; 29 | Car?: Car; 30 | Pets?: Pet[]; 31 | } 32 | 33 | describe('base condition', () => { 34 | describe('as factory function', () => { 35 | it('asc', () => { 36 | const compare1 = OrderByExpression.factory(({ e, t }) => 37 | e().ascending(t.Age), 38 | ); 39 | 40 | expect(compare1.render()).toBe('Age asc'); 41 | }); 42 | 43 | it('desc', () => { 44 | const compare1 = OrderByExpression.factory(({ e, t }) => 45 | e().descending(t.Age), 46 | ); 47 | 48 | expect(compare1.render()).toBe('Age desc'); 49 | }); 50 | }); 51 | 52 | describe('combination e().ascending(...).descending(...)', () => { 53 | it('asc,desc', () => { 54 | const compare = OrderByExpression.factory(({ e, t }) => 55 | e().ascending(t.Age).descending(t.CreatedOn), 56 | ); 57 | 58 | expect(compare.render()).toBe('Age asc,CreatedOn desc'); 59 | }); 60 | }); 61 | 62 | describe('navigate main', () => { 63 | it('navigate', () => { 64 | const compare1 = OrderByExpression.factory(({ e, t }) => 65 | e().ascending(t.Car!.Year), 66 | ); 67 | expect(compare1.render()).toBe('Car/Year asc'); 68 | }); 69 | 70 | it('combination navigate', () => { 71 | const compare1 = OrderByExpression.factory(({ e, t }) => 72 | e().ascending(t.Car!.Year).descending(t.Car!.Model!.Name), 73 | ); 74 | expect(compare1.render()).toBe('Car/Year asc,Car/Model/Name desc'); 75 | }); 76 | }); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/orderby.ts: -------------------------------------------------------------------------------- 1 | import { Parser, ParserOptions } from '../../../types'; 2 | import { Types } from '../../../utils'; 3 | import { QueryCustomType } from '../builder'; 4 | import { Expression } from './base'; 5 | import { render, FieldFactory, Renderable, RenderableFactory } from './syntax'; 6 | 7 | export type OrderAttribute = 'asc' | 'desc'; 8 | export class OrderByField implements Renderable { 9 | constructor( 10 | protected field: Renderable, 11 | protected order: OrderAttribute, 12 | ) {} 13 | 14 | get [Symbol.toStringTag]() { 15 | return 'OrderByField'; 16 | } 17 | 18 | toJson() { 19 | return { 20 | $type: Types.rawType(this), 21 | field: this.field.toJson(), 22 | order: this.order, 23 | }; 24 | } 25 | 26 | render({ 27 | aliases, 28 | escape, 29 | prefix, 30 | parser, 31 | options, 32 | }: { 33 | aliases?: QueryCustomType[]; 34 | escape?: boolean; 35 | prefix?: string; 36 | parser?: Parser; 37 | options?: ParserOptions; 38 | }): string { 39 | return `${render(this.field, { 40 | aliases, 41 | escape, 42 | prefix, 43 | parser, 44 | options, 45 | })} ${this.order}`; 46 | } 47 | 48 | clone() { 49 | return new OrderByField( 50 | typeof this.field !== 'string' ? this.field.clone() : this.field, 51 | this.order, 52 | ); 53 | } 54 | 55 | resolve(parser: any) { 56 | return parser; 57 | } 58 | } 59 | 60 | export type OrderByExpressionBuilder = { 61 | t: Required; 62 | e: () => OrderByExpression; 63 | }; 64 | 65 | export class OrderByExpression extends Expression { 66 | constructor({ 67 | children, 68 | }: { 69 | children?: Renderable[]; 70 | } = {}) { 71 | super({ children }); 72 | } 73 | 74 | override get [Symbol.toStringTag]() { 75 | return 'OrderByExpression'; 76 | } 77 | 78 | static factory( 79 | opts: ( 80 | builder: OrderByExpressionBuilder, 81 | current: OrderByExpression, 82 | ) => OrderByExpression, 83 | current?: OrderByExpression, 84 | ): OrderByExpression { 85 | return opts( 86 | { 87 | t: FieldFactory>(), 88 | e: () => new OrderByExpression(), 89 | }, 90 | current ?? new OrderByExpression(), 91 | ) as OrderByExpression; 92 | } 93 | 94 | private _add(node: Renderable): OrderByExpression { 95 | this._children.push(node); 96 | return this; 97 | } 98 | 99 | override toJson() { 100 | const json = super.toJson(); 101 | return Object.assign(json, {}); 102 | } 103 | 104 | static fromJson(json: { [name: string]: any }): OrderByExpression { 105 | return new OrderByExpression({ 106 | children: json['children'].map((c: any) => RenderableFactory(c)), 107 | }); 108 | } 109 | 110 | render({ 111 | aliases, 112 | escape, 113 | prefix, 114 | parser, 115 | options, 116 | }: { 117 | aliases?: QueryCustomType[]; 118 | escape?: boolean; 119 | prefix?: string; 120 | parser?: Parser; 121 | options?: ParserOptions; 122 | } = {}): string { 123 | let content = this._children 124 | .map((n) => n.render({ aliases, escape, prefix, parser, options })) 125 | .join(`,`); 126 | return content; 127 | } 128 | 129 | clone() { 130 | return new OrderByExpression({ 131 | children: this._children.map((c) => c.clone()), 132 | }); 133 | } 134 | 135 | ascending(field: any) { 136 | return this._add(new OrderByField(field, 'asc')); 137 | } 138 | 139 | descending(field: any) { 140 | return this._add(new OrderByField(field, 'desc')); 141 | } 142 | 143 | combine(expression: OrderByExpression): OrderByExpression { 144 | return this._add(expression); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/search.spec.ts: -------------------------------------------------------------------------------- 1 | import { SearchExpression } from './search'; 2 | 3 | describe('OData search builder', () => { 4 | interface Pet { 5 | Id?: number; 6 | Name?: string; 7 | Age?: number; 8 | Person?: Person; 9 | } 10 | 11 | interface Model { 12 | Id?: number; 13 | Name?: string; 14 | } 15 | 16 | interface Car { 17 | Id?: number; 18 | Model?: Model; 19 | Year?: number; 20 | } 21 | 22 | interface Person { 23 | Id?: number; 24 | Name?: string; 25 | Age?: number; 26 | IsCorrect?: boolean; 27 | EditedOn?: boolean; 28 | CreatedOn?: boolean; 29 | Car?: Car; 30 | Pets?: Pet[]; 31 | } 32 | 33 | describe('base condition', () => { 34 | describe('as factory function', () => { 35 | it('term', () => { 36 | const compare1 = SearchExpression.factory(({ e }) => 37 | e().term('John'), 38 | ); 39 | 40 | expect(compare1.render()).toBe('John'); 41 | }); 42 | }); 43 | 44 | describe('combination e().and(...).or(...)', () => { 45 | it('and,or', () => { 46 | const compare = SearchExpression.factory(({ e }) => 47 | e().term('John').and(e().term('Lennon')).or(e().term('Beatles')), 48 | ); 49 | 50 | expect(compare.render()).toBe('(John AND Lennon) OR Beatles'); 51 | }); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/select.spec.ts: -------------------------------------------------------------------------------- 1 | import { SelectExpression } from './select'; 2 | 3 | describe('OData search builder', () => { 4 | interface Pet { 5 | Id?: number; 6 | Name?: string; 7 | Age?: number; 8 | Person?: Person; 9 | } 10 | 11 | interface Model { 12 | Id?: number; 13 | Name?: string; 14 | } 15 | 16 | interface Car { 17 | Id?: number; 18 | Model?: Model; 19 | Year?: number; 20 | } 21 | 22 | interface Person { 23 | Id?: number; 24 | Name?: string; 25 | Age?: number; 26 | IsCorrect?: boolean; 27 | EditedOn?: boolean; 28 | CreatedOn?: boolean; 29 | Car?: Car; 30 | Pets?: Pet[]; 31 | } 32 | 33 | describe('base condition', () => { 34 | describe('as factory function', () => { 35 | it('select', () => { 36 | const compare1 = SelectExpression.factory(({ t, e }) => 37 | e().field(t.Car).field(t.Name), 38 | ); 39 | 40 | expect(compare1.render()).toBe('Car,Name'); 41 | }); 42 | }); 43 | 44 | describe('navigation e().field(...)', () => { 45 | it('navigate', () => { 46 | const compare = SelectExpression.factory(({ t, e }) => 47 | e().field(t.Car?.Model?.Name).field(t.Age), 48 | ); 49 | 50 | expect(compare.render()).toBe('Car/Model/Name,Age'); 51 | }); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/expressions/select.ts: -------------------------------------------------------------------------------- 1 | import { Parser, ParserOptions } from '../../../types'; 2 | import { QueryCustomType } from '../builder'; 3 | import { Expression } from './base'; 4 | import { FieldFactory, Renderable, RenderableFactory } from './syntax'; 5 | 6 | export type SelectExpressionBuilder = { 7 | t: Required; 8 | e: () => SelectExpression; 9 | }; 10 | export class SelectExpression extends Expression { 11 | constructor({ 12 | children, 13 | }: { 14 | children?: Renderable[]; 15 | } = {}) { 16 | super({ children }); 17 | } 18 | 19 | override get [Symbol.toStringTag]() { 20 | return 'SelectExpression'; 21 | } 22 | 23 | static factory( 24 | opts: ( 25 | builder: SelectExpressionBuilder, 26 | current: SelectExpression, 27 | ) => SelectExpression, 28 | current?: SelectExpression, 29 | ): SelectExpression { 30 | return opts( 31 | { 32 | t: FieldFactory>(), 33 | e: () => new SelectExpression(), 34 | }, 35 | current ?? new SelectExpression(), 36 | ) as SelectExpression; 37 | } 38 | 39 | override toJson() { 40 | const json = super.toJson(); 41 | return Object.assign(json, {}); 42 | } 43 | 44 | static fromJson(json: { [name: string]: any }): SelectExpression { 45 | return new SelectExpression({ 46 | children: json['children'].map((c: any) => RenderableFactory(c)), 47 | }); 48 | } 49 | render({ 50 | aliases, 51 | escape, 52 | prefix, 53 | parser, 54 | options, 55 | }: { 56 | aliases?: QueryCustomType[]; 57 | escape?: boolean; 58 | prefix?: string; 59 | parser?: Parser; 60 | options?: ParserOptions; 61 | } = {}): string { 62 | return this._children 63 | .map((n) => 64 | typeof n === 'string' 65 | ? n 66 | : n.render({ aliases, escape, prefix, parser, options }), 67 | ) 68 | .join(','); 69 | } 70 | 71 | clone() { 72 | return new SelectExpression({ 73 | children: this._children.map((c) => c.clone()), 74 | }); 75 | } 76 | 77 | private _add(node: Renderable): SelectExpression { 78 | this._children.push(node); 79 | return this; 80 | } 81 | 82 | field(field: any): SelectExpression { 83 | return this._add(field); 84 | } 85 | 86 | fields(...fields: any[]): SelectExpression { 87 | fields.forEach((f) => this._add(f)); 88 | return this; 89 | } 90 | 91 | combine(expression: SelectExpression): SelectExpression { 92 | return this._add(expression); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/query/index.ts: -------------------------------------------------------------------------------- 1 | export * from './builder'; 2 | export * from './options'; 3 | export * from './handlers'; 4 | export * from './expressions'; 5 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/types/count.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { ODataApi } from '../../api'; 3 | import { $COUNT } from '../../constants'; 4 | import { EdmType, PathSegment, QueryOption } from '../../types'; 5 | import { ODataPathSegments } from '../path'; 6 | import { ODataQueryOptions } from '../query'; 7 | import { ODataResource } from '../resource'; 8 | import { ODataOptions } from './options'; 9 | 10 | export class ODataCountResource extends ODataResource { 11 | //#region Factory 12 | static factory( 13 | api: ODataApi, 14 | { 15 | segments, 16 | query, 17 | }: { 18 | segments: ODataPathSegments; 19 | query?: ODataQueryOptions; 20 | }, 21 | ) { 22 | const currentType = segments.last()?.outgoingType(); 23 | const segment = segments.add(PathSegment.count, $COUNT); 24 | segment.outgoingType(currentType); 25 | segment.incomingType(EdmType.Int32); 26 | query?.keep(QueryOption.filter, QueryOption.search); 27 | return new ODataCountResource(api, { segments, query }); 28 | } 29 | 30 | override clone(): ODataCountResource { 31 | return super.clone() as ODataCountResource; 32 | } 33 | //#endregion 34 | 35 | //#region Requests 36 | protected override get(options?: ODataOptions): Observable { 37 | return super.get({ responseType: 'value', ...options }); 38 | } 39 | //#endregion 40 | 41 | //#region Shortcuts 42 | /** 43 | * Fetch the count of the set. 44 | * @param options Options for the request 45 | * @returns The count of the set 46 | */ 47 | fetch(options?: ODataOptions): Observable { 48 | return this.get(options); 49 | } 50 | //#endregion 51 | } 52 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './options'; 2 | export * from './action'; 3 | export * from './batch'; 4 | export * from './count'; 5 | export * from './entity'; 6 | export * from './entity-set'; 7 | export * from './function'; 8 | export * from './metadata'; 9 | export * from './navigation-property'; 10 | export * from './property'; 11 | export * from './reference'; 12 | export * from './singleton'; 13 | export * from './media'; 14 | export * from './value'; 15 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/types/media.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { ODataApi } from '../../api'; 3 | import { $VALUE } from '../../constants'; 4 | import { PathSegment } from '../../types'; 5 | import { Http } from '../../utils'; 6 | import { ODataPathSegments } from '../path'; 7 | import { ODataQueryOptions } from '../query'; 8 | import { ODataResource } from '../resource'; 9 | import { ODataOptions } from './options'; 10 | 11 | export class ODataMediaResource extends ODataResource { 12 | //#region Factory 13 | static factory( 14 | api: ODataApi, 15 | { 16 | segments, 17 | query, 18 | }: { 19 | segments: ODataPathSegments; 20 | query?: ODataQueryOptions; 21 | }, 22 | ) { 23 | segments.add(PathSegment.value, $VALUE); 24 | return new ODataMediaResource(api, { segments, query }); 25 | } 26 | 27 | override clone(): ODataMediaResource { 28 | return super.clone() as ODataMediaResource; 29 | } 30 | //#endregion 31 | 32 | //#region Requests 33 | protected override get( 34 | options: { responseType: 'arraybuffer' | 'blob' } & ODataOptions, 35 | ): Observable { 36 | return super.get(options); 37 | } 38 | 39 | protected override put( 40 | data: ArrayBuffer | Blob, 41 | options: ODataOptions = {}, 42 | ): Observable { 43 | return super.put(data, options); 44 | } 45 | //#endregion 46 | 47 | //#region Shortcuts 48 | fetch( 49 | options: { responseType: 'arraybuffer' } & ODataOptions, 50 | ): Observable; 51 | fetch(options: { responseType: 'blob' } & ODataOptions): Observable; 52 | fetch(options: { responseType: any } & ODataOptions): Observable { 53 | return this.get(options); 54 | } 55 | 56 | fetchArraybuffer(options: ODataOptions = {}): Observable { 57 | return this.fetch({ responseType: 'arraybuffer', ...options }); 58 | } 59 | 60 | fetchBlob(options: ODataOptions = {}): Observable { 61 | return this.fetch({ responseType: 'blob', ...options }); 62 | } 63 | 64 | upload( 65 | data: ArrayBuffer | Blob, 66 | options: ODataOptions = {}, 67 | ): Observable { 68 | return this.put(data, options); 69 | } 70 | 71 | uploadArrayBuffer( 72 | data: ArrayBuffer, 73 | contentType: string, 74 | options: ODataOptions = {}, 75 | ): Observable { 76 | options.headers = Http.mergeHttpHeaders(options.headers || {}, { 77 | 'Content-Type': contentType, 78 | }); 79 | return this.upload(data, options); 80 | } 81 | 82 | uploadBlob(data: Blob, options: ODataOptions = {}): Observable { 83 | return this.upload(data, options); 84 | } 85 | //#endregion 86 | } 87 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/types/metadata.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { map } from 'rxjs/operators'; 3 | import { ODataApi } from '../../api'; 4 | import { $METADATA, ACCEPT, APPLICATION_XML } from '../../constants'; 5 | import { PathSegment } from '../../types'; 6 | import { ODataPathSegments } from '../path'; 7 | import { ODataResource } from '../resource'; 8 | import { ODataOptions } from './options'; 9 | import { ODataMetadata } from '../../metadata/metadata'; 10 | import { ODataMetadataParser } from '../../metadata'; 11 | 12 | export class ODataMetadataResource extends ODataResource { 13 | constructor(api: ODataApi, segments?: ODataPathSegments) { 14 | super(api, { segments }); 15 | } 16 | 17 | //#region Factory 18 | static factory(api: ODataApi) { 19 | let segments = new ODataPathSegments(); 20 | segments.add(PathSegment.metadata, $METADATA); 21 | return new ODataMetadataResource(api, segments); 22 | } 23 | 24 | override clone(): ODataMetadataResource { 25 | return super.clone() as ODataMetadataResource; 26 | } 27 | //#endregion 28 | 29 | //#region Requests 30 | protected override get(options?: ODataOptions): Observable { 31 | return super.get({ 32 | responseType: 'text', 33 | ...options, 34 | headers: { [ACCEPT]: APPLICATION_XML }, 35 | }); 36 | } 37 | //#endregion 38 | 39 | //#region Shortcuts 40 | fetch(options?: ODataOptions): Observable { 41 | return this.get(options).pipe( 42 | map((body: any) => new ODataMetadataParser(body).metadata()), 43 | ); 44 | } 45 | //#endregion 46 | } 47 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/types/options.ts: -------------------------------------------------------------------------------- 1 | import { HttpContext, HttpHeaders, HttpParams } from '@angular/common/http'; 2 | import { FetchPolicy, ParserOptions } from '../../types'; 3 | import { ODataQueryArguments } from '../query'; 4 | 5 | export type ODataOptions = { 6 | etag?: string; 7 | context?: HttpContext; 8 | headers?: HttpHeaders | { [header: string]: string | string[] }; 9 | params?: 10 | | HttpParams 11 | | { 12 | [param: string]: 13 | | string 14 | | number 15 | | boolean 16 | | ReadonlyArray; 17 | }; 18 | reportProgress?: boolean; 19 | withCredentials?: boolean; 20 | fetchPolicy?: FetchPolicy; 21 | parserOptions?: ParserOptions; 22 | }; 23 | 24 | export type ODataEntityOptions = ODataOptions & { responseType?: 'entity' }; 25 | export type ODataEntitiesOptions = ODataOptions & { 26 | responseType?: 'entities'; 27 | withCount?: boolean; 28 | }; 29 | export type ODataPropertyOptions = ODataOptions & { responseType?: 'property' }; 30 | export type ODataQueryArgumentsOptions = ODataOptions & 31 | ODataQueryArguments; 32 | export type ODataActionOptions = ODataQueryArgumentsOptions; 33 | export type ODataFunctionOptions = ODataQueryArgumentsOptions & { 34 | alias?: boolean; 35 | }; 36 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/types/value.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { ODataApi } from '../../api'; 3 | import { $VALUE } from '../../constants'; 4 | import { PathSegment } from '../../types'; 5 | import { ODataPathSegments } from '../path'; 6 | import { ODataResource } from '../resource'; 7 | import { ODataOptions } from './options'; 8 | 9 | export class ODataValueResource extends ODataResource { 10 | //#region Factory 11 | static factory( 12 | api: ODataApi, 13 | { 14 | segments, 15 | }: { 16 | segments: ODataPathSegments; 17 | }, 18 | ) { 19 | const currentType = segments.last()?.outgoingType(); 20 | const segment = segments.add(PathSegment.value, $VALUE); 21 | segment.incomingType(currentType); 22 | return new ODataValueResource(api, { segments }); 23 | } 24 | 25 | static fromResource(resource: ODataResource) { 26 | const baseType = resource.outgoingType(); 27 | let baseSchema = 28 | baseType !== undefined 29 | ? resource.api.structuredType(baseType) 30 | : undefined; 31 | const value = ODataValueResource.factory(resource.api, { 32 | segments: resource.cloneSegments(), 33 | }); 34 | 35 | // Switch entitySet to binding type if available 36 | if (baseSchema !== undefined && baseSchema.type() !== baseType) { 37 | let entitySet = resource.api.findEntitySet(baseSchema.type()); 38 | if (entitySet !== undefined) { 39 | value.segment((s) => s.entitySet().path(entitySet!.name)); 40 | } 41 | } 42 | 43 | return value; 44 | } 45 | override clone(): ODataValueResource { 46 | return super.clone() as ODataValueResource; 47 | } 48 | //#endregion 49 | 50 | //#region Requests 51 | protected override get(options: ODataOptions = {}): Observable { 52 | return super.get({ responseType: 'value', ...options }); 53 | } 54 | //#endregion 55 | 56 | //#region Shortcuts 57 | 58 | /** 59 | * Fetch the value of the resource. 60 | * @param options OData options. 61 | * @returns Observable of the value. 62 | */ 63 | fetch(options?: ODataOptions): Observable { 64 | return this.get(options); 65 | } 66 | 67 | //#endregion 68 | } 69 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/annotation.ts: -------------------------------------------------------------------------------- 1 | import { ODataAnnotationConfig } from '../types'; 2 | 3 | export class ODataAnnotation { 4 | term: string; 5 | string?: string; 6 | bool?: boolean; 7 | int?: number; 8 | permissions?: string[]; 9 | properties?: string[]; 10 | 11 | constructor(annot: ODataAnnotationConfig) { 12 | this.term = annot.term; 13 | Object.assign(this, annot); 14 | } 15 | } 16 | 17 | export class ODataAnnotatable { 18 | annotations: ODataAnnotation[]; 19 | constructor(config: { annotations?: ODataAnnotationConfig[] }) { 20 | this.annotations = (config.annotations || []).map( 21 | (annot) => new ODataAnnotation(annot), 22 | ); 23 | } 24 | 25 | /** 26 | * Find an annotation inside the annotatable. 27 | * @param predicate Function that returns true if the annotation match. 28 | * @returns The annotation that matches the predicate. 29 | */ 30 | findAnnotation(predicate: (annot: ODataAnnotation) => boolean) { 31 | return this.annotations.find(predicate); 32 | } 33 | 34 | /** 35 | * Find an annotation inside the annotatable and return its value. 36 | * @param term The term of the annotation to find. 37 | * @returns The value of the annotation. 38 | */ 39 | annotatedValue(term: string | RegExp): T | undefined { 40 | const reg = term instanceof RegExp ? term : new RegExp(`^${term}$`); 41 | const annot = this.findAnnotation((a) => reg.test(a.term)); 42 | if (!annot) { 43 | return undefined; 44 | } 45 | return (annot.string || 46 | annot.bool || 47 | annot.int || 48 | annot.permissions || 49 | annot.properties) as any; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/callable.ts: -------------------------------------------------------------------------------- 1 | import { ODataCallableConfig, ParserOptions } from '../types'; 2 | import { ODataParserSchemaElement } from './element'; 3 | import { ODataCallableParser } from './parsers'; 4 | import { ODataSchema } from './schema'; 5 | 6 | export class ODataCallable extends ODataParserSchemaElement< 7 | R, 8 | ODataCallableParser 9 | > { 10 | entitySetPath?: string; 11 | bound?: boolean; 12 | composable?: boolean; 13 | 14 | constructor(config: ODataCallableConfig, schema: ODataSchema) { 15 | super( 16 | config, 17 | schema, 18 | new ODataCallableParser(config, schema.namespace, schema.alias), 19 | ); 20 | this.entitySetPath = config.entitySetPath; 21 | this.bound = config.bound; 22 | this.composable = config.composable; 23 | } 24 | 25 | path() { 26 | let path: string; 27 | if (this.entitySetPath) path = this.entitySetPath; 28 | else if (this.bound) path = `${this.schema.namespace}.${this.name}`; 29 | else 30 | path = this.parser.return 31 | ? this.api.findEntitySet(this.parser.return.type)?.name || this.name 32 | : this.name; 33 | return path; 34 | } 35 | 36 | configure({ options }: { options: ParserOptions }) { 37 | this.parser.configure({ 38 | options, 39 | parserForType: (t: string) => this.api.parserForType(t), 40 | }); 41 | } 42 | 43 | /** 44 | * Deseialize the given value from the callable. 45 | * @param value Value to deserialize 46 | * @param options Options for deserialization 47 | * @returns Deserialized value 48 | */ 49 | deserialize(value: any, options?: ParserOptions): any { 50 | return this.parser.deserialize(value, options); 51 | } 52 | 53 | /** 54 | * Serialize the given value for the callable. 55 | * @param value Value to serialize 56 | * @param options Options for serialization 57 | * @returns Serialized value 58 | */ 59 | serialize(value: any, options?: ParserOptions): any { 60 | return this.parser.serialize(value, options); 61 | } 62 | 63 | /** 64 | * Encode the given value for the callable. 65 | * @param value Value to encode 66 | * @param options Options for encoding 67 | * @returns Encoded value 68 | */ 69 | encode(value: any, options?: ParserOptions): any { 70 | return this.parser.encode(value, options); 71 | } 72 | 73 | /** 74 | * Returns the binding parameter of the callable. 75 | * @returns The binding parameter of the callable. 76 | */ 77 | binding() { 78 | return this.parser.binding(); 79 | } 80 | 81 | returnType() { 82 | return this.parser.returnType(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/element.ts: -------------------------------------------------------------------------------- 1 | import { ODataAnnotationConfig, Parser } from '../types'; 2 | import { Strings } from '../utils'; 3 | import { ODataAnnotatable } from './annotation'; 4 | import { ODataSchema } from './schema'; 5 | 6 | export class ODataSchemaElement extends ODataAnnotatable { 7 | name: string; 8 | schema: ODataSchema; 9 | 10 | constructor( 11 | config: { annotations?: ODataAnnotationConfig[]; name: string }, 12 | schema: ODataSchema, 13 | ) { 14 | super(config); 15 | this.schema = schema; 16 | this.name = config.name; 17 | } 18 | 19 | get api() { 20 | return this.schema.api; 21 | } 22 | 23 | /** 24 | * Create a nicer looking title. 25 | * Titleize is meant for creating pretty output. 26 | * @param term The term of the annotation to find. 27 | * @returns The titleized string. 28 | */ 29 | titleize(term?: string | RegExp): string { 30 | return (term && this.annotatedValue(term)) ?? Strings.titleCase(this.name); 31 | } 32 | 33 | /** 34 | * Returns a full type of the structured type including the namespace/alias. 35 | * @param alias Use the alias of the namespace instead of the namespace. 36 | * @returns The string representation of the type. 37 | */ 38 | type({ alias = false }: { alias?: boolean } = {}) { 39 | return `${alias ? this.schema.alias : this.schema.namespace}.${this.name}`; 40 | } 41 | 42 | /** 43 | * Returns a boolean indicating if the structured type is of the given type. 44 | * @param type String representation of the type 45 | * @returns True if the callable is type of the given type 46 | */ 47 | isTypeOf(element: ODataSchemaElement): boolean { 48 | const names = [`${this.schema.namespace}.${this.name}`]; 49 | if (this.schema.alias) names.push(`${this.schema.alias}.${this.name}`); 50 | return names.includes(element.type()); 51 | } 52 | 53 | /** 54 | * Returns a boolean indicating if the structured type is a subtype of the given type. 55 | * @param type String representation of the type 56 | * @returns True if the callable is type of the given type 57 | */ 58 | isSubtypeOf(element: ODataSchemaElement): boolean { 59 | if (this.isTypeOf(element)) return true; 60 | return false; 61 | } 62 | 63 | /** 64 | * Returns a boolean indicating if the structured type is a supertype of the given type. 65 | * @param type String representation of the type 66 | * @returns True if the callable is type of the given type 67 | */ 68 | isSupertypeOf(element: ODataSchemaElement): boolean { 69 | if (this.isTypeOf(element)) return true; 70 | return false; 71 | } 72 | } 73 | 74 | export class ODataParserSchemaElement< 75 | E, 76 | P extends Parser, 77 | > extends ODataSchemaElement { 78 | parser: P; 79 | constructor( 80 | config: { annotations?: ODataAnnotationConfig[]; name: string }, 81 | schema: ODataSchema, 82 | parser: P, 83 | ) { 84 | super(config, schema); 85 | this.parser = parser; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/entity-container.ts: -------------------------------------------------------------------------------- 1 | import { ODataEntityContainerConfig } from '../types'; 2 | import { ODataSchemaElement } from './element'; 3 | import { ODataEntitySet } from './entity-set'; 4 | import { ODataSchema } from './schema'; 5 | import { ODataSingleton } from './singleton'; 6 | 7 | export class ODataEntityContainer extends ODataSchemaElement { 8 | entitySets: ODataEntitySet[]; 9 | singletons: ODataSingleton[]; 10 | 11 | constructor(config: ODataEntityContainerConfig, schema: ODataSchema) { 12 | super(config, schema); 13 | this.entitySets = (config.entitySets ?? []).map( 14 | (config) => new ODataEntitySet(config, schema), 15 | ); 16 | this.singletons = (config.singletons ?? []).map( 17 | (config) => new ODataSingleton(config, schema), 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/entity-set.ts: -------------------------------------------------------------------------------- 1 | import { ODataEntitySetConfig } from '../types'; 2 | import { ODataSchemaElement } from './element'; 3 | import { ODataSchema } from './schema'; 4 | 5 | export class ODataEntitySet extends ODataSchemaElement { 6 | entityType: string; 7 | service: { new (...params: any[]): any }; 8 | constructor(config: ODataEntitySetConfig, schema: ODataSchema) { 9 | super(config, schema); 10 | this.entityType = config.entityType; 11 | this.service = config.service; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/enum-type.ts: -------------------------------------------------------------------------------- 1 | import { ODataEnumTypeConfig, ParserOptions } from '../types'; 2 | import { ODataParserSchemaElement } from './element'; 3 | import { ODataEnumTypeFieldParser, ODataEnumTypeParser } from './parsers'; 4 | import { ODataSchema } from './schema'; 5 | 6 | export class ODataEnumType extends ODataParserSchemaElement< 7 | E, 8 | ODataEnumTypeParser 9 | > { 10 | members: { [name: string]: number } | { [value: number]: string }; 11 | constructor(config: ODataEnumTypeConfig, schema: ODataSchema) { 12 | super( 13 | config, 14 | schema, 15 | new ODataEnumTypeParser(config, schema.namespace, schema.alias), 16 | ); 17 | this.members = config.members; 18 | } 19 | 20 | configure({ options }: { options: ParserOptions }) { 21 | this.parser.configure({ options }); 22 | } 23 | 24 | /** 25 | * Returns the fields of the enum type. 26 | * @returns The fields of the enum type. 27 | */ 28 | fields(namesValue?: string | number): ODataEnumTypeFieldParser[] { 29 | return this.parser.fields(namesValue); 30 | } 31 | 32 | /** 33 | * Find a field by name or value. 34 | * @param enu The name or value of the field 35 | * @returns The field with the given name or value 36 | */ 37 | field(nameValue: string | number) { 38 | return this.parser.field(nameValue); 39 | } 40 | 41 | /** 42 | * Map the fields of the enum type. 43 | * @param mapper Function that maps the value to the new value 44 | * @returns The fields mapped by the mapper 45 | */ 46 | mapFields(mapper: (field: ODataEnumTypeFieldParser) => T) { 47 | return this.parser.mapFields(mapper); 48 | } 49 | 50 | /** 51 | * Deseialize the given value from the enum type. 52 | * @param value Value to deserialize 53 | * @param options Options for deserialization 54 | * @returns Deserialized value 55 | */ 56 | deserialize(value: any, options?: ParserOptions): E { 57 | return this.parser.deserialize(value, options); 58 | } 59 | 60 | /** 61 | * Serialize the given value for the enum type. 62 | * @param value Value to serialize 63 | * @param options Options for serialization 64 | * @returns Serialized value 65 | */ 66 | serialize(value: number, options?: ParserOptions): any { 67 | return this.parser.serialize(value, options); 68 | } 69 | 70 | /** 71 | * Encode the given value for the enum type. 72 | * @param value Value to encode 73 | * @param options Options for encoding 74 | * @returns Encoded value 75 | */ 76 | encode(value: number, options?: ParserOptions): any { 77 | return this.parser.encode(value, options); 78 | } 79 | 80 | unpack(value: string | number) { 81 | return this.parser.unpack(value); 82 | } 83 | 84 | pack(value: string | number | number[]) { 85 | return this.parser.pack(value); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/index.ts: -------------------------------------------------------------------------------- 1 | export * from './schema'; 2 | export * from './entity-container'; 3 | export * from './callable'; 4 | export * from './enum-type'; 5 | export * from './structured-type'; 6 | export * from './entity-set'; 7 | export * from './singleton'; 8 | export * from './parsers'; 9 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/parsers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './edm'; 2 | export * from './enum-type'; 3 | export * from './structured-type'; 4 | export * from './callable'; 5 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/schema.ts: -------------------------------------------------------------------------------- 1 | import { ODataApi } from '../api'; 2 | import { 3 | ParserOptions, 4 | ODataSchemaConfig, 5 | ODataStructuredTypeConfig, 6 | } from '../types'; 7 | import { OData } from '../utils/odata'; 8 | import { ODataAnnotatable } from './annotation'; 9 | import { ODataCallable } from './callable'; 10 | import { ODataEntityContainer } from './entity-container'; 11 | import { ODataEntitySet } from './entity-set'; 12 | import { ODataEnumType } from './enum-type'; 13 | import { ODataSingleton } from './singleton'; 14 | import { ODataStructuredType } from './structured-type'; 15 | 16 | export class ODataSchema extends ODataAnnotatable { 17 | api: ODataApi; 18 | namespace: string; 19 | alias?: string; 20 | enums: ODataEnumType[]; 21 | entities: ODataStructuredType[]; 22 | callables: ODataCallable[]; 23 | containers: ODataEntityContainer[]; 24 | 25 | constructor(config: ODataSchemaConfig, api: ODataApi) { 26 | super(config); 27 | this.api = api; 28 | this.namespace = config.namespace; 29 | this.alias = config.alias; 30 | this.enums = (config.enums ?? []).map( 31 | (config) => new ODataEnumType(config, this), 32 | ); 33 | this.entities = (config.entities ?? []).map( 34 | (config) => new ODataStructuredType(config, this), 35 | ); 36 | this.callables = OData.mergeCallableParameters(config.callables ?? []).map( 37 | (config) => new ODataCallable(config, this), 38 | ); 39 | this.containers = (config.containers ?? []).map( 40 | (config) => new ODataEntityContainer(config, this), 41 | ); 42 | } 43 | 44 | isNamespaceOf(type: string) { 45 | return ( 46 | type.startsWith(this.namespace) ?? 47 | (this.alias && type.startsWith(this.alias)) 48 | ); 49 | } 50 | 51 | get entitySets() { 52 | return this.containers.reduce( 53 | (acc, container) => [...acc, ...container.entitySets], 54 | [] as ODataEntitySet[], 55 | ); 56 | } 57 | 58 | get singletons() { 59 | return this.containers.reduce( 60 | (acc, container) => [...acc, ...container.singletons], 61 | [] as ODataSingleton[], 62 | ); 63 | } 64 | 65 | //#region Find for Type 66 | public createStructuredType(config: ODataStructuredTypeConfig) { 67 | const entity = new ODataStructuredType(config, this); 68 | entity.configure({ options: this.api.options.parserOptions }); 69 | this.entities.push(entity); 70 | return entity; 71 | } 72 | //#endregion 73 | 74 | configure({ options }: { options: ParserOptions }) { 75 | // Configure Enums 76 | this.enums.forEach((enu) => enu.configure({ options })); 77 | // Configure Entities 78 | this.entities.forEach((structured) => structured.configure({ options })); 79 | // Configure callables 80 | this.callables.forEach((callable) => callable.configure({ options })); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/singleton.ts: -------------------------------------------------------------------------------- 1 | import { ODataSingletonConfig } from '../types'; 2 | import { ODataSchemaElement } from './element'; 3 | import { ODataSchema } from './schema'; 4 | 5 | export class ODataSingleton extends ODataSchemaElement { 6 | singletonType: string; 7 | service: { new (...params: any[]): any }; 8 | constructor(config: ODataSingletonConfig, schema: ODataSchema) { 9 | super(config, schema); 10 | this.singletonType = config.type; 11 | this.service = config.service; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/services/entity.ts: -------------------------------------------------------------------------------- 1 | import { ODataModel } from '../models/model'; 2 | import { EntityKey, ODataResource } from '../resources'; 3 | import { ODataBaseService } from './base'; 4 | 5 | export abstract class ODataEntityService extends ODataBaseService { 6 | public abstract entity(key?: EntityKey): ODataResource; 7 | public abstract attach>(value: M): void; 8 | 9 | /** 10 | * The schema for the structured type. 11 | */ 12 | get structuredTypeSchema() { 13 | return this.apiNameOrEntityType !== undefined 14 | ? this.api.findStructuredType(this.apiNameOrEntityType) 15 | : undefined; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/services/factory.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ODataClient } from '../client'; 3 | import { ODataEntitySetService } from './entity-set'; 4 | import { ODataSingletonService } from './singleton'; 5 | import { ODataCollection, ODataModel } from '../models'; 6 | 7 | @Injectable() 8 | export class ODataServiceFactory { 9 | constructor(protected client: ODataClient) {} 10 | 11 | /** 12 | * Factory method to create an entity set service. 13 | * @param entitySetName Name of the entity set. 14 | * @param apiNameOrEntityType Name of the API or the type of the entity. 15 | */ 16 | entitySet( 17 | entitySetName: string, 18 | apiNameOrEntityType?: string, 19 | options: { 20 | Model?: { new (...params: any[]): ODataModel }; 21 | Collection?: { 22 | new (...params: any[]): ODataCollection>; 23 | }; 24 | } = {}, 25 | ): ODataEntitySetService { 26 | const Service = class extends ODataEntitySetService { 27 | Model = options?.Model; 28 | Collection = options?.Collection; 29 | }; 30 | return new Service(this.client, entitySetName, apiNameOrEntityType); 31 | } 32 | 33 | /** Factory method to create a singleton service. 34 | * @param singletonName Name of the singleton. 35 | * @param apiNameOrEntityType Name of the API or the type of the entity. 36 | */ 37 | singleton( 38 | singletonName: string, 39 | apiNameOrEntityType?: string, 40 | options: { Model?: { new (...params: any[]): ODataModel } } = {}, 41 | ): ODataSingletonService { 42 | const Service = class extends ODataSingletonService { 43 | Model = options?.Model; 44 | }; 45 | return new Service(this.client, singletonName, apiNameOrEntityType); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './base'; 2 | export * from './entity-set'; 3 | export * from './singleton'; 4 | export * from './factory'; 5 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/services/singleton.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { ODataModel } from '../models/model'; 3 | import { 4 | ODataEntity, 5 | ODataOptions, 6 | ODataSingletonResource, 7 | } from '../resources'; 8 | import { ODataEntityService } from './entity'; 9 | 10 | /** 11 | * OData Singleton Service 12 | * www.odata.org/getting-started/advanced-tutorial/#singleton 13 | */ 14 | export class ODataSingletonService extends ODataEntityService { 15 | static Model?: typeof ODataModel; 16 | model(entity?: Partial) { 17 | const Service = this.constructor as typeof ODataSingletonService; 18 | return this.entity().asModel((entity ?? {}) as Partial, { 19 | ModelType: Service.Model, 20 | }); 21 | } 22 | /** 23 | * Get the entity resource for this service. 24 | * @param key The entity key. 25 | */ 26 | public entity(): ODataSingletonResource { 27 | return this.client.singleton(this.name, this.apiNameOrEntityType); 28 | } 29 | 30 | /** 31 | * Attach an existing model to this service. 32 | * @param model The model to attach. 33 | */ 34 | public attach>(model: M) { 35 | model.attach(this.entity()); 36 | } 37 | 38 | /** 39 | * The schema for the singleton. 40 | */ 41 | get singletonSchema() { 42 | return this.api.findEntitySet(this.name); 43 | } 44 | 45 | /** 46 | * Update the singleton entity 47 | * @param attrs The attributes for the entity. 48 | * @param etag The etag for the entity. 49 | * @param options The options for the request. 50 | */ 51 | public update( 52 | attrs: Partial, 53 | options?: ODataOptions & { etag?: string }, 54 | ): Observable> { 55 | const res = this.entity(); 56 | return res.update(attrs, options); 57 | } 58 | 59 | /** 60 | * Patch the singleton entity 61 | * @param attrs The attributes for the entity. 62 | * @param etag The etag for the entity. 63 | * @param options The options for the request. 64 | */ 65 | public patch( 66 | attrs: Partial, 67 | options?: ODataOptions & { etag?: string }, 68 | ): Observable> { 69 | const res = this.entity(); 70 | return res.modify(attrs, options); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/arraybuffers.ts: -------------------------------------------------------------------------------- 1 | //https://github.com/niklasvh/base64-arraybuffer 2 | const chars = 3 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 4 | 5 | // Use a lookup table to find the index. 6 | const lookup = new Uint8Array(256); 7 | for (var i = 0; i < chars.length; i++) { 8 | lookup[chars.charCodeAt(i)] = i; 9 | } 10 | 11 | export const ArrayBuffers = { 12 | toArrayBuffer(v: string): ArrayBuffer { 13 | var bufferLength = v.length * 0.75, 14 | len = v.length, 15 | i, 16 | p = 0, 17 | encoded1, 18 | encoded2, 19 | encoded3, 20 | encoded4; 21 | 22 | if (v[v.length - 1] === '=') { 23 | bufferLength--; 24 | if (v[v.length - 2] === '=') { 25 | bufferLength--; 26 | } 27 | } 28 | 29 | var arraybuffer = new ArrayBuffer(bufferLength), 30 | bytes = new Uint8Array(arraybuffer); 31 | 32 | for (i = 0; i < len; i += 4) { 33 | encoded1 = lookup[v.charCodeAt(i)]; 34 | encoded2 = lookup[v.charCodeAt(i + 1)]; 35 | encoded3 = lookup[v.charCodeAt(i + 2)]; 36 | encoded4 = lookup[v.charCodeAt(i + 3)]; 37 | 38 | bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); 39 | bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); 40 | bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); 41 | } 42 | 43 | return arraybuffer; 44 | }, 45 | toString(v: ArrayBuffer): string { 46 | var bytes = new Uint8Array(v), 47 | i, 48 | len = bytes.length, 49 | base64 = ''; 50 | 51 | for (i = 0; i < len; i += 3) { 52 | base64 += chars[bytes[i] >> 2]; 53 | base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; 54 | base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; 55 | base64 += chars[bytes[i + 2] & 63]; 56 | } 57 | 58 | if (len % 3 === 2) { 59 | base64 = base64.substring(0, base64.length - 1) + '='; 60 | } else if (len % 3 === 1) { 61 | base64 = base64.substring(0, base64.length - 2) + '=='; 62 | } 63 | return base64; 64 | }, 65 | }; 66 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/arrays.spec.ts: -------------------------------------------------------------------------------- 1 | import { Arrays } from './arrays'; 2 | 3 | describe('Arrays', () => { 4 | it('should convert to title case', () => { 5 | expect( 6 | Arrays.zip([1, 2, 3, 4, 5, 6], ['a', 'b', 'c', 'd', 'e', 'f']), 7 | ).toEqual([ 8 | [1, 'a'], 9 | [2, 'b'], 10 | [3, 'c'], 11 | [4, 'd'], 12 | [5, 'e'], 13 | [6, 'f'], 14 | ]); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/arrays.ts: -------------------------------------------------------------------------------- 1 | export const Arrays = { 2 | // Zip arrays 3 | // Example 4 | // Arrays.zip([1, 2, 3, 4, 5, 6], ['a', 'b', 'c', 'd', 'e', 'f']) 5 | // => [[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e'], [6, 'f']] 6 | zip: (...arrays: any[][]) => { 7 | return arrays[0].map((_: any, i: number) => 8 | arrays.map((array: any[]) => array[i]), 9 | ); 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/dates.ts: -------------------------------------------------------------------------------- 1 | const ISO_REGEX = 2 | /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/; 3 | 4 | export const Dates = { 5 | isoStringToDate(value: any): any { 6 | if (typeof value === 'string' && value.search(ISO_REGEX) === 0) { 7 | return new Date(value); 8 | } else if (Array.isArray(value)) { 9 | return value.map((v) => this.isoStringToDate(v)); 10 | } else if (value && value.constructor === Object) { 11 | return Object.keys(value) 12 | .map((key) => [key, this.isoStringToDate(value[key])]) 13 | .reduce((acc, v) => Object.assign(acc, { [v[0]]: v[1] }), {}); 14 | } 15 | return value; 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/durations.ts: -------------------------------------------------------------------------------- 1 | const DURATION_REGEX = 2 | /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; 3 | 4 | //https://en.wikipedia.org/wiki/ISO_8601#Durations 5 | export type Duration = { 6 | sign?: 1 | -1; 7 | years?: number; 8 | months?: number; 9 | weeks?: number; 10 | days?: number; 11 | hours?: number; 12 | minutes?: number; 13 | seconds?: number; 14 | }; 15 | 16 | export const Durations = { 17 | toDuration(v: string): Duration { 18 | const matches = DURATION_REGEX.exec(v); 19 | if (!matches || v.length < 3) { 20 | throw new TypeError( 21 | `duration invalid: "${v}". Must be a ISO 8601 duration. See https://en.wikipedia.org/wiki/ISO_8601#Durations`, 22 | ); 23 | } 24 | let duration: Duration = {}; 25 | duration.sign = matches[1] === '-' ? -1 : 1; 26 | return [ 27 | 'years', 28 | 'months', 29 | 'weeks', 30 | 'days', 31 | 'hours', 32 | 'minutes', 33 | 'seconds', 34 | ].reduce((acc: any, name, index) => { 35 | const v = parseFloat(matches[index + 2]); 36 | if (!Number.isNaN(v)) acc[name] = v; 37 | return acc; 38 | }, duration) as Duration; 39 | }, 40 | toString(v: Duration): string { 41 | return [ 42 | v.sign === -1 ? '-' : '', 43 | 'P', 44 | v.years ? v.years + 'Y' : '', 45 | v.months ? v.months + 'M' : '', 46 | v.weeks ? v.weeks + 'W' : '', 47 | v.days ? v.days + 'D' : '', 48 | 'T', 49 | v.hours ? v.hours + 'H' : '', 50 | v.minutes ? v.minutes + 'M' : '', 51 | v.seconds ? v.seconds + 'S' : '', 52 | ].join(''); 53 | }, 54 | }; 55 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/enums.ts: -------------------------------------------------------------------------------- 1 | export const Enums = { 2 | names(enums: E): string[] { 3 | return Object.values(enums).filter((v) => typeof v === 'string'); 4 | }, 5 | 6 | values(enums: E): number[] { 7 | return Object.values(enums).filter((v) => typeof v === 'number'); 8 | }, 9 | 10 | toValue( 11 | enums: E, 12 | value: any, 13 | ): number | undefined { 14 | if (value in enums) return typeof value === 'string' ? enums[value] : value; 15 | return undefined; 16 | }, 17 | 18 | toValues(enums: E, value: any): number[] { 19 | if (typeof value === 'number') { 20 | return this.values(enums).filter((v) => (value & v) === v); 21 | } 22 | if (typeof value === 'string') { 23 | value = value.split(',').map((o) => o.trim()); 24 | } 25 | if (Array.isArray(value) && value.every((v) => v in enums)) { 26 | return value.map((o) => this.toValue(enums, o) as number); 27 | } 28 | return []; 29 | }, 30 | 31 | toName( 32 | enums: E, 33 | value: any, 34 | ): string | undefined { 35 | if (value in enums) return typeof value === 'number' ? enums[value] : value; 36 | return undefined; 37 | }, 38 | 39 | toNames(enums: E, value: any): string[] { 40 | if (typeof value === 'number') { 41 | return this.values(enums) 42 | .filter((v) => (value & v) === v) 43 | .map((v) => this.toName(enums, v) as string); 44 | } 45 | if (typeof value === 'string') { 46 | value = value.split(',').map((o) => o.trim()); 47 | } 48 | if (Array.isArray(value) && value.every((v) => v in enums)) { 49 | return value.map((o) => this.toName(enums, o) as string); 50 | } 51 | return []; 52 | }, 53 | 54 | toFlags(enums: E, value: any): string[] { 55 | if (typeof value === 'number') { 56 | return this.values(enums) 57 | .filter((v) => v !== 0 && (value & v) === v) 58 | .map((v) => this.toName(enums, v) as string); 59 | } 60 | if (typeof value === 'string') { 61 | value = value.split(',').map((o) => o.trim()); 62 | } 63 | if (Array.isArray(value) && value.every((v) => v in enums)) { 64 | return value 65 | .filter((v) => enums[v]) 66 | .map((v) => this.toName(enums, v) as string); 67 | } 68 | return []; 69 | }, 70 | }; 71 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/http.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { Http } from './http'; 3 | 4 | describe('Http', () => { 5 | it('should merge headers', () => { 6 | const headers = Http.mergeHttpHeaders( 7 | { 8 | 'Content-Type': 'application/json', 9 | }, 10 | { 11 | Authorization: 'Bearer token', 12 | 'Content-Type': '*/*', 13 | }, 14 | ); 15 | expect(headers.get('Authorization')).toEqual('Bearer token'); 16 | expect(headers.getAll('Content-Type')).toEqual(['application/json', '*/*']); 17 | }); 18 | 19 | it('should merge params', () => { 20 | const params = Http.mergeHttpParams( 21 | { 22 | param1: 'value1', 23 | param2: 'value2', 24 | params: ['value1'], 25 | }, 26 | { 27 | param3: 'value3', 28 | params: ['value2', 'value3', 'value4'], 29 | }, 30 | { 31 | params: ['value5', 'value6'], 32 | param4: 'value4', 33 | }, 34 | ); 35 | expect(params.toString()).toEqual( 36 | 'param1=value1¶m2=value2¶ms=value1¶ms=value2¶ms=value3¶ms=value4¶ms=value5¶ms=value6¶m3=value3¶m4=value4', 37 | ); 38 | }); 39 | 40 | it('should split params', () => { 41 | const params = Http.mergeHttpParams( 42 | { 43 | param1: 'value1', 44 | param2: 'value2', 45 | params: ['value1'], 46 | }, 47 | { 48 | param3: 'value3', 49 | params: ['value2', 'value3', 'value4'], 50 | }, 51 | { 52 | params: ['value5', 'value6'], 53 | param4: 'value4', 54 | }, 55 | ); 56 | let [param1, param2] = Http.splitHttpParams(params, ['param1', 'param2']); 57 | expect(param1.toString()).toEqual( 58 | 'params=value1¶ms=value2¶ms=value3¶ms=value4¶ms=value5¶ms=value6¶m3=value3¶m4=value4', 59 | ); 60 | expect(param2.toString()).toEqual('param1=value1¶m2=value2'); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dates'; 2 | export * from './durations'; 3 | export * from './enums'; 4 | export * from './http'; 5 | export * from './objects'; 6 | export * from './odata'; 7 | export * from './strings'; 8 | export * from './types'; 9 | export * from './urls'; 10 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/odata.ts: -------------------------------------------------------------------------------- 1 | import { CALLABLE_BINDING_PARAMETER } from '../constants'; 2 | import { ODataCallableConfig } from '../types'; 3 | import { Objects } from './objects'; 4 | 5 | export const OData = { 6 | // Merge callables parameters 7 | mergeCallableParameters( 8 | callables: ODataCallableConfig[], 9 | ): ODataCallableConfig[] { 10 | const areEqual = (a: ODataCallableConfig, b: ODataCallableConfig) => 11 | a.name === b.name && 12 | Objects.equal( 13 | (a.parameters || {})[CALLABLE_BINDING_PARAMETER] || {}, 14 | (b.parameters || {})[CALLABLE_BINDING_PARAMETER] || {}, 15 | ); 16 | return callables.reduce((acc: ODataCallableConfig[], config) => { 17 | if (acc.every((c) => !areEqual(c, config))) { 18 | config = callables 19 | .filter((c) => areEqual(c, config)) 20 | .reduce((acc, c) => { 21 | acc.parameters = Object.assign( 22 | acc.parameters || {}, 23 | c.parameters || {}, 24 | ); 25 | return acc; 26 | }, config); 27 | return [...acc, config]; 28 | } 29 | return acc; 30 | }, [] as ODataCallableConfig[]); 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/strings.spec.ts: -------------------------------------------------------------------------------- 1 | import { Strings } from './strings'; 2 | 3 | describe('Strings', () => { 4 | it('should convert to title case', () => { 5 | expect(Strings.titleCase('hiWorld')).toEqual('Hi World'); 6 | expect(Strings.titleCase('world')).toEqual('World'); 7 | expect(Strings.titleCase('anitaLavaLaTina')).toEqual('Anita Lava La Tina'); 8 | expect(Strings.titleCase('PascalCase')).toEqual('Pascal Case'); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/strings.ts: -------------------------------------------------------------------------------- 1 | // From https://github.com/adamhalasz/uniqid 2 | var glast: number; 3 | function now() { 4 | let time = Date.now(); 5 | let last = glast || time; 6 | return (glast = time > last ? time : last + 1); 7 | } 8 | export const Strings = { 9 | uniqueId({ 10 | prefix, 11 | suffix, 12 | }: { prefix?: string; suffix?: string } = {}): string { 13 | return (prefix ? prefix : '') + now().toString(36) + (suffix ? suffix : ''); 14 | }, 15 | 16 | titleCase(text: string): string { 17 | const result = text.replace(/([a-z])([A-Z])/g, '$1 $2'); 18 | return result 19 | .split(' ') 20 | .map((p) => p.charAt(0).toUpperCase() + p.slice(1)) 21 | .join(' '); 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/urls.ts: -------------------------------------------------------------------------------- 1 | import { PARAM_SEPARATOR, VALUE_SEPARATOR } from '../constants'; 2 | 3 | export const Urls = { 4 | parseQueryString(query: string) { 5 | return query.split(PARAM_SEPARATOR).reduce((acc, param: string) => { 6 | let index = param.indexOf(VALUE_SEPARATOR); 7 | if (index !== -1) 8 | Object.assign(acc, { 9 | [param.substring(0, index)]: param.substring(index + 1), 10 | }); 11 | return acc; 12 | }, {}); 13 | }, 14 | escapeIllegalChars(string: string) { 15 | string = string.replace(/%/g, '%25'); 16 | string = string.replace(/\+/g, '%2B'); 17 | string = string.replace(/\//g, '%2F'); 18 | string = string.replace(/\?/g, '%3F'); 19 | string = string.replace(/#/g, '%23'); 20 | string = string.replace(/&/g, '%26'); 21 | string = string.replace(/'/g, "''"); 22 | return string; 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /projects/angular-odata/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of angular-odata 3 | */ 4 | 5 | export * from './lib'; 6 | -------------------------------------------------------------------------------- /projects/angular-odata/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/lib", 7 | "declaration": true, 8 | "declarationMap": true, 9 | "inlineSources": true, 10 | "types": [] 11 | }, 12 | "exclude": [ 13 | "**/*.spec.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /projects/angular-odata/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "./tsconfig.lib.json", 5 | "compilerOptions": { 6 | "declarationMap": false 7 | }, 8 | "angularCompilerOptions": { 9 | "compilationMode": "partial" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/angular-odata/tsconfig.schematics.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "lib": [ 5 | "es2018", 6 | "dom" 7 | ], 8 | "declaration": true, 9 | "module": "commonjs", 10 | "moduleResolution": "node", 11 | "noEmitOnError": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "noImplicitAny": true, 14 | "noImplicitThis": true, 15 | "noUnusedParameters": false, 16 | "noUnusedLocals": false, 17 | "rootDir": "schematics", 18 | "outDir": "../../dist/angular-odata/schematics", 19 | "skipDefaultLibCheck": true, 20 | "skipLibCheck": true, 21 | "sourceMap": true, 22 | "strictNullChecks": true, 23 | "target": "es6", 24 | "types": [ 25 | "jasmine", 26 | "node" 27 | ] 28 | }, 29 | "include": [ 30 | "schematics/**/*" 31 | ], 32 | "exclude": [ 33 | "schematics/*/files/**/*" 34 | ] 35 | } -------------------------------------------------------------------------------- /projects/angular-odata/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/spec", 7 | "types": [ 8 | "jasmine" 9 | ] 10 | }, 11 | "include": [ 12 | "**/*.spec.ts", 13 | "**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "compileOnSave": false, 5 | "compilerOptions": { 6 | "paths": { 7 | "angular-odata": [ 8 | "./dist/angular-odata" 9 | ] 10 | }, 11 | "outDir": "./dist/out-tsc", 12 | "strict": true, 13 | "noImplicitOverride": true, 14 | "noPropertyAccessFromIndexSignature": true, 15 | "noImplicitReturns": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "skipLibCheck": true, 18 | "isolatedModules": true, 19 | "esModuleInterop": true, 20 | "experimentalDecorators": true, 21 | "moduleResolution": "bundler", 22 | "importHelpers": true, 23 | "target": "ES2022", 24 | "module": "ES2022" 25 | }, 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | --------------------------------------------------------------------------------