├── SECURITY.md ├── CONTRIBUTING.md ├── projects └── angular-odata │ ├── schematics │ ├── apigen │ │ ├── files │ │ │ ├── metadata │ │ │ │ └── metadata.json │ │ │ ├── index │ │ │ │ └── __fileName__.ts │ │ │ ├── enum │ │ │ │ └── __fileName__.ts │ │ │ ├── module │ │ │ │ └── __fileName__.ts │ │ │ ├── entity │ │ │ │ └── __fileName__.ts │ │ │ ├── entitycontainer-service │ │ │ │ └── __fileName__.ts │ │ │ ├── singleton-service │ │ │ │ └── __fileName__.ts │ │ │ ├── collection │ │ │ │ └── __fileName__.ts │ │ │ ├── api-config │ │ │ │ └── __fileName__.ts │ │ │ ├── entityset-service │ │ │ │ └── __fileName__.ts │ │ │ └── model │ │ │ │ └── __fileName__.ts │ │ ├── metadata │ │ │ ├── index.ts │ │ │ ├── csdl │ │ │ │ ├── csdl-singleton.ts │ │ │ │ ├── csdl-type-definition.ts │ │ │ │ ├── csdl-navigation-property-binding.ts │ │ │ │ ├── csdl-enum-type.ts │ │ │ │ ├── csdl-entity-set.ts │ │ │ │ ├── csdl-reference.ts │ │ │ │ ├── csdl-entity-container.ts │ │ │ │ └── csdl-schema.ts │ │ │ └── metadata.ts │ │ ├── schema.ts │ │ ├── angular │ │ │ ├── import.ts │ │ │ ├── module.ts │ │ │ ├── api-config.ts │ │ │ ├── enum.ts │ │ │ ├── collection.ts │ │ │ └── service.ts │ │ ├── schema.json │ │ └── index.ts │ ├── ng-add │ │ └── index.ts │ └── collection.json │ ├── src │ ├── lib │ │ ├── metadata │ │ │ ├── index.ts │ │ │ ├── csdl │ │ │ │ ├── csdl-navigation-property-binding.ts │ │ │ │ ├── csdl-singleton.ts │ │ │ │ ├── csdl-type-definition.ts │ │ │ │ ├── csdl-entity-set.ts │ │ │ │ ├── csdl-enum-type.ts │ │ │ │ ├── csdl-entity-container.ts │ │ │ │ └── csdl-reference.ts │ │ │ └── metadata.ts │ │ ├── resources │ │ │ ├── path │ │ │ │ ├── index.ts │ │ │ │ └── handlers.ts │ │ │ ├── query │ │ │ │ ├── index.ts │ │ │ │ └── expressions │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── compute.spec.ts │ │ │ │ │ ├── count.spec.ts │ │ │ │ │ ├── search.spec.ts │ │ │ │ │ ├── select.spec.ts │ │ │ │ │ ├── base.ts │ │ │ │ │ ├── orderby.spec.ts │ │ │ │ │ ├── expand.spec.ts │ │ │ │ │ ├── select.ts │ │ │ │ │ └── compute.ts │ │ │ ├── index.ts │ │ │ ├── types │ │ │ │ ├── index.ts │ │ │ │ ├── options.ts │ │ │ │ ├── metadata.ts │ │ │ │ ├── count.ts │ │ │ │ ├── value.ts │ │ │ │ └── media.ts │ │ │ └── options.ts │ │ ├── cache │ │ │ ├── index.ts │ │ │ ├── memory.ts │ │ │ └── storage.ts │ │ ├── models │ │ │ └── index.ts │ │ ├── services │ │ │ ├── index.ts │ │ │ ├── entity.ts │ │ │ ├── factory.ts │ │ │ └── singleton.ts │ │ ├── schema │ │ │ ├── parsers │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── singleton.ts │ │ │ ├── entity-set.ts │ │ │ ├── entity-container.ts │ │ │ ├── annotation.ts │ │ │ ├── callable.ts │ │ │ ├── enum-type.ts │ │ │ ├── schema.ts │ │ │ └── element.ts │ │ ├── utils │ │ │ ├── index.ts │ │ │ ├── arrays.ts │ │ │ ├── arrays.spec.ts │ │ │ ├── strings.spec.ts │ │ │ ├── dates.ts │ │ │ ├── urls.ts │ │ │ ├── strings.ts │ │ │ ├── odata.ts │ │ │ ├── durations.ts │ │ │ ├── http.spec.ts │ │ │ ├── arraybuffers.ts │ │ │ └── enums.ts │ │ ├── index.ts │ │ ├── annotations.spec.ts │ │ ├── loaders.ts │ │ ├── module.ts │ │ └── constants.ts │ └── public-api.ts │ ├── ng-package.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.spec.json │ ├── tsconfig.lib.json │ ├── tsconfig.schematics.json │ ├── README.md │ └── package.json ├── docs └── api │ ├── js │ ├── routes │ │ └── routes_index.js │ ├── compodoc.js │ ├── libs │ │ ├── tablesort.number.min.js │ │ ├── EventDispatcher.js │ │ ├── custom-elements-es5-adapter.js │ │ ├── innersvg.js │ │ ├── promise.min.js │ │ └── tablesort.min.js │ ├── tabs.js │ ├── lazy-load-graphs.js │ ├── search │ │ └── search-lunr.js │ ├── svg-pan-zoom.controls.js │ └── sourceCode.js │ ├── fonts │ ├── ionicons.eot │ ├── ionicons.ttf │ ├── ionicons.woff │ ├── ionicons.woff2 │ ├── roboto-v15-latin-300.eot │ ├── roboto-v15-latin-300.ttf │ ├── roboto-v15-latin-300.woff │ ├── roboto-v15-latin-700.eot │ ├── roboto-v15-latin-700.ttf │ ├── roboto-v15-latin-700.woff │ ├── roboto-v15-latin-300.woff2 │ ├── roboto-v15-latin-700.woff2 │ ├── roboto-v15-latin-italic.eot │ ├── roboto-v15-latin-italic.ttf │ ├── roboto-v15-latin-italic.woff │ ├── roboto-v15-latin-italic.woff2 │ ├── roboto-v15-latin-regular.eot │ ├── roboto-v15-latin-regular.ttf │ ├── roboto-v15-latin-regular.woff │ └── roboto-v15-latin-regular.woff2 │ ├── images │ ├── favicon.ico │ ├── compodoc-vectorise.png │ └── compodoc-vectorise-inverted.png │ ├── styles │ ├── style.css │ ├── tablesort.css │ ├── original.css │ ├── reset.css │ ├── stripe.css │ ├── readthedocs.css │ ├── laravel.css │ ├── dark.css │ ├── vagrant.css │ └── material.css │ └── template-playground │ ├── main.ts │ ├── template-playground.module.ts │ └── zip-export.service.ts ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── node.js.yml │ └── codeql-analysis.yml ├── .gitignore ├── angular.json ├── README.md ├── LICENSE ├── tsconfig.json ├── CHANGELOG.md └── package.json /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/metadata/metadata.json: -------------------------------------------------------------------------------- 1 | <%= content %> -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/index.ts: -------------------------------------------------------------------------------- 1 | export * from './metadata'; 2 | -------------------------------------------------------------------------------- /docs/api/js/routes/routes_index.js: -------------------------------------------------------------------------------- 1 | var ROUTES_INDEX = {"name":"","kind":"module","children":[]} 2 | -------------------------------------------------------------------------------- /docs/api/fonts/ionicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/ionicons.eot -------------------------------------------------------------------------------- /docs/api/fonts/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/ionicons.ttf -------------------------------------------------------------------------------- /docs/api/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/images/favicon.ico -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/index.ts: -------------------------------------------------------------------------------- 1 | export * from './metadata'; 2 | export * from './parser'; 3 | -------------------------------------------------------------------------------- /docs/api/fonts/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/ionicons.woff -------------------------------------------------------------------------------- /docs/api/fonts/ionicons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/ionicons.woff2 -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/path/index.ts: -------------------------------------------------------------------------------- 1 | export * from './segments'; 2 | export * from './handlers'; 3 | -------------------------------------------------------------------------------- /projects/angular-odata/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of angular-odata 3 | */ 4 | 5 | export * from './lib'; 6 | -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-300.eot -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-300.ttf -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-300.woff -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-700.eot -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-700.ttf -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-700.woff -------------------------------------------------------------------------------- /docs/api/images/compodoc-vectorise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/images/compodoc-vectorise.png -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/cache/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cache'; 2 | export * from './memory'; 3 | export * from './storage'; 4 | -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-300.woff2 -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-700.woff2 -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-italic.eot -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-italic.ttf -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './options'; 2 | export * from './model'; 3 | export * from './collection'; 4 | -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-italic.woff -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-italic.woff2 -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-regular.eot -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-regular.ttf -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-regular.woff -------------------------------------------------------------------------------- /docs/api/fonts/roboto-v15-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/fonts/roboto-v15-latin-regular.woff2 -------------------------------------------------------------------------------- /docs/api/images/compodoc-vectorise-inverted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diegomvh/angular-odata/HEAD/docs/api/images/compodoc-vectorise-inverted.png -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/index/__fileName__.ts: -------------------------------------------------------------------------------- 1 | <% for (let imp of imports) { %>export * from '<%= imp.path() %>'; 2 | <% } %> 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/schema.ts: -------------------------------------------------------------------------------- 1 | export interface Schema { 2 | name: string; 3 | project: string; 4 | metadata: string; 5 | path: string; 6 | models: boolean; 7 | serviceRootUrl?: string; 8 | version?: string; 9 | creation?: Date; 10 | } 11 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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) => arrays.map((array: any[]) => array[i])); 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /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(Arrays.zip([1, 2, 3, 4, 5, 6], ['a', 'b', 'c', 'd', 'e', 'f'])).toEqual([ 6 | [1, 'a'], 7 | [2, 'b'], 8 | [3, 'c'], 9 | [4, 'd'], 10 | [5, 'e'], 11 | [6, 'f'], 12 | ]); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /docs/api/template-playground/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | import { TemplatePlaygroundModule } from './template-playground.module'; 3 | 4 | // Bootstrap the Angular application when the DOM is ready 5 | document.addEventListener('DOMContentLoaded', () => { 6 | platformBrowserDynamic() 7 | .bootstrapModule(TemplatePlaygroundModule) 8 | .catch(err => console.error('Error starting template playground:', err)); 9 | }); 10 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /docs/api/js/libs/tablesort.number.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * tablesort v5.6.0 (2025-04-20) 3 | * http://tristen.ca/tablesort/demo/ 4 | * Copyright (c) 2025 ; Licensed MIT 5 | */ 6 | (()=>{function a(t){return t.replace(/[^\-?0-9.]/g,"")}Tablesort.extend("number",function(t){return t.match(/^[-+]?[£\x24Û¢´€]?\d+\s*([,\.]\d{0,2})/)||t.match(/^[-+]?\d+\s*([,\.]\d{0,2})?[£\x24Û¢´€]/)||t.match(/^[-+]?(\d)*-?([,\.]){0,1}-?(\d)+([E,e][\-+][\d]+)?%?$/)},function(t,e){return t=a(t),e=a(e),e=e,t=t,e=parseFloat(e),t=parseFloat(t),(e=isNaN(e)?0:e)-(t=isNaN(t)?0:t)})})(); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | "vitest/globals" 9 | ] 10 | }, 11 | "include": [ 12 | "src/**/*.d.ts", 13 | "src/**/*.spec.ts" 14 | , "src/lib/trippin.config.ts" ] 15 | } 16 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/entity/__fileName__.ts: -------------------------------------------------------------------------------- 1 | import { Duration } from 'angular-odata'; 2 | <% if (hasGeoProperties) { %>import { <% for (let p of geoProperties) { %><%= p.type() %>,<% } %> } from 'geojson';<% } %><% for (let imp of imports) { %> 3 | import { <%= imp.names.join(", ") %> } from '<%= imp.path() %>';<% } %> 4 | 5 | export const <%= type %> = '<%= fullName %>'; 6 | export interface <%= classify(name) %><% if (baseType) { %> extends <%= toTypescriptType(baseType) %><% } %> {<% for(let prop of properties) { %> 7 | <%= prop.name() %>: <%= prop.type() %>;<% } %> 8 | } 9 | -------------------------------------------------------------------------------- /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 | "include": [ 13 | "src/**/*.ts" 14 | ], 15 | "exclude": [ 16 | "**/*.spec.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /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/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/src/lib/services/entity.ts: -------------------------------------------------------------------------------- 1 | import type { ODataModel } from '../models/model'; 2 | import type { 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/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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((config) => new ODataEntitySet(config, schema)); 14 | this.singletons = (config.singletons ?? []).map((config) => new ODataSingleton(config, schema)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /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 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/schematics/apigen/files/collection/__fileName__.ts: -------------------------------------------------------------------------------- 1 | import { HttpHeaders, HttpParams } from '@angular/common/http'; 2 | import { Observable } from 'rxjs'; 3 | import { map } from 'rxjs/operators'; 4 | 5 | import { 6 | ODataModel, 7 | ODataCollection, 8 | ODataOptions, 9 | ODataQueryArgumentsOptions, 10 | ODataFunctionOptions, 11 | ODataActionOptions 12 | } from 'angular-odata';<% for (let imp of imports) { %> 13 | import { <%= imp.names.join(", ") %> } from '<%= imp.path() %>';<% } %> 14 | 15 | export class <%= classify(name) %>, M extends <%= model.name() %>> extends <% if (baseType) { %><%= toTypescriptType(baseType) %><% } else { %>ODataCollection<% } %> { 16 | <% for (let cal of callables) { %> 17 | // <%= cal.name() %> 18 | <%= cal.callableMethod() %> 19 | <% } %> 20 | } 21 | -------------------------------------------------------------------------------- /.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: [20.x, 22.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 | -------------------------------------------------------------------------------- /docs/api/js/tabs.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | var tabs = document.getElementsByClassName('nav-tabs'), 3 | updateAddress = function(e) { 4 | if(history.pushState && e.target.dataset.link) { 5 | history.pushState(null, null, '#' + e.target.dataset.link); 6 | } 7 | }; 8 | if (tabs.length > 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 | -------------------------------------------------------------------------------- /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/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/schematics/apigen/files/api-config/__fileName__.ts: -------------------------------------------------------------------------------- 1 | import { ODataApiConfig, EDM_PARSERS, ODataMetadata, ODataVersion } from 'angular-odata'; 2 | import * as json from './metadata.json';<% for (let imp of imports) { %> 3 | import { <%= imp.names.join(", ") %> } from '<%= imp.path() %>';<% } %> 4 | 5 | export const <%= classify(name) %> = ODataMetadata.fromJson(json).toConfig({ 6 | serviceRootUrl: '<%= serviceRootUrl %>', 7 | metadataUrl: '<%= metadataUrl %>', 8 | name: '<%= apiConfigName %>', 9 | version: '<%= version %>' as ODataVersion, 10 | creation: new Date('<%= creation.toISOString() %>'), 11 | parsers: EDM_PARSERS, 12 | models: {<% for(const model of models) { %> 13 | '<%= model.entityType() %>': <%= model.name() %>, <% } %> 14 | }, 15 | collections: {<% for(const col of collections) { %> 16 | '<%= col.entityType() %>': <%= col.name() %>, <% } %> 17 | } 18 | }) as ODataApiConfig; 19 | -------------------------------------------------------------------------------- /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({ prefix, suffix }: { prefix?: string; suffix?: string } = {}): string { 10 | return (prefix ? prefix : '') + now().toString(36) + (suffix ? suffix : ''); 11 | }, 12 | 13 | titleCase(text: string): string { 14 | const result = text.replace(/([a-z])([A-Z])/g, '$1 $2'); 15 | return result 16 | .split(' ') 17 | .map((p) => p.charAt(0).toUpperCase() + p.slice(1)) 18 | .join(' '); 19 | }, 20 | 21 | hashCode(text: string): number { 22 | if (!text || text.length === 0) { 23 | return 0; 24 | } 25 | let hash = 0; 26 | for (let i = 0; i < text.length; i++) { 27 | hash = Math.imul(hash, 31) + text.charCodeAt(i); 28 | } 29 | return hash; 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /docs/api/js/libs/custom-elements-es5-adapter.js: -------------------------------------------------------------------------------- 1 | /** 2 | @license @nocompile 3 | Copyright (c) 2018 The Polymer Project Authors. All rights reserved. 4 | This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5 | The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6 | The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7 | Code distributed by Google as part of the polymer project is also 8 | subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9 | */ 10 | (function () { 11 | 'use strict'; 12 | 13 | (function(){if(void 0===window.Reflect||void 0===window.customElements||window.customElements.hasOwnProperty('polyfillWrapFlushCallback'))return;const a=HTMLElement;window.HTMLElement=function(){return Reflect.construct(a,[],this.constructor)},HTMLElement.prototype=a.prototype,HTMLElement.prototype.constructor=HTMLElement,Object.setPrototypeOf(HTMLElement,a);})(); 14 | 15 | }()); 16 | -------------------------------------------------------------------------------- /docs/api/template-playground/template-playground.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { CommonModule } from '@angular/common'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { HttpClientModule } from '@angular/common/http'; 6 | 7 | import { TemplatePlaygroundComponent } from './template-playground.component'; 8 | import { TemplateEditorService } from './template-editor.service'; 9 | import { ZipExportService } from './zip-export.service'; 10 | import { HbsRenderService } from './hbs-render.service'; 11 | 12 | @NgModule({ 13 | declarations: [ 14 | TemplatePlaygroundComponent 15 | ], 16 | imports: [ 17 | BrowserModule, 18 | CommonModule, 19 | FormsModule, 20 | HttpClientModule 21 | ], 22 | providers: [ 23 | TemplateEditorService, 24 | ZipExportService, 25 | HbsRenderService 26 | ], 27 | bootstrap: [TemplatePlaygroundComponent] 28 | }) 29 | export class TemplatePlaygroundModule { } 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "cli": { 5 | "packageManager": "npm" 6 | }, 7 | "newProjectRoot": "projects", 8 | "projects": { 9 | "angular-odata": { 10 | "projectType": "library", 11 | "root": "projects/angular-odata", 12 | "sourceRoot": "projects/angular-odata/src", 13 | "prefix": "lib", 14 | "architect": { 15 | "build": { 16 | "builder": "@angular/build:ng-packagr", 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/build:unit-test", 29 | "options": { 30 | "tsConfig": "projects/angular-odata/tsconfig.spec.json" 31 | } 32 | } 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 = 'RFNwdAoAAQAAAAAAAAAAFAAAAHoO-P3xtNOT90O-DRY2LSZF_AFWAAAAAQIAAAA'; 27 | expect(actual).toBe(expected); 28 | }); 29 | }); 30 | }); 31 | 32 | interface AirPort {} 33 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/odata.ts: -------------------------------------------------------------------------------- 1 | import { CALLABLE_BINDING_PARAMETER } from '../constants'; 2 | import type { ODataCallableConfig } from '../types'; 3 | import { Objects } from './objects'; 4 | 5 | export const OData = { 6 | // Merge callables parameters 7 | mergeCallableParameters(callables: ODataCallableConfig[]): ODataCallableConfig[] { 8 | const areEqual = (a: ODataCallableConfig, b: ODataCallableConfig) => 9 | a.name === b.name && 10 | Objects.equal( 11 | (a.parameters || {})[CALLABLE_BINDING_PARAMETER] || {}, 12 | (b.parameters || {})[CALLABLE_BINDING_PARAMETER] || {}, 13 | ); 14 | return callables.reduce((acc: ODataCallableConfig[], config) => { 15 | if (acc.every((c) => !areEqual(c, config))) { 16 | config = callables 17 | .filter((c) => areEqual(c, config)) 18 | .reduce((acc, c) => { 19 | acc.parameters = Object.assign(acc.parameters || {}, c.parameters || {}); 20 | return acc; 21 | }, config); 22 | return [...acc, config]; 23 | } 24 | return acc; 25 | }, [] as ODataCallableConfig[]); 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /projects/angular-odata/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 | -------------------------------------------------------------------------------- /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 | "models": { 22 | "type": "boolean", 23 | "description": "Generate models for the entities and complex types.", 24 | "default": true 25 | }, 26 | "project": { 27 | "type": "string", 28 | "description": "The name of the project.", 29 | "$default": { 30 | "$source": "projectName" 31 | } 32 | }, 33 | "serviceRootUrl": { 34 | "description": "The serviceRootUrl of the api.", 35 | "type": "string" 36 | } 37 | }, 38 | "required": [ 39 | "name", 40 | "metadata" 41 | ] 42 | } -------------------------------------------------------------------------------- /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 | "strict": true, 12 | "noImplicitOverride": true, 13 | "noPropertyAccessFromIndexSignature": true, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "skipLibCheck": true, 17 | "isolatedModules": true, 18 | "experimentalDecorators": true, 19 | "importHelpers": true, 20 | "target": "ES2022", 21 | "module": "preserve" 22 | }, 23 | "angularCompilerOptions": { 24 | "enableI18nLegacyMessageIdFormat": false, 25 | "strictInjectionParameters": true, 26 | "strictInputAccessModifiers": true, 27 | "strictTemplates": true 28 | }, 29 | "files": [], 30 | "references": [ 31 | { 32 | "path": "./projects/angular-odata/tsconfig.lib.json" 33 | }, 34 | { 35 | "path": "./projects/angular-odata/tsconfig.spec.json" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /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]: string | number | boolean | ReadonlyArray; 13 | }; 14 | reportProgress?: boolean; 15 | withCredentials?: boolean; 16 | fetchPolicy?: FetchPolicy; 17 | parserOptions?: ParserOptions; 18 | }; 19 | 20 | export type ODataEntityOptions = ODataOptions & { responseType?: 'entity' }; 21 | export type ODataEntitiesOptions = ODataOptions & { 22 | responseType?: 'entities'; 23 | withCount?: boolean; 24 | }; 25 | export type ODataPropertyOptions = ODataOptions & { responseType?: 'property' }; 26 | export type ODataQueryArgumentsOptions = ODataOptions & ODataQueryArguments; 27 | export type ODataActionOptions = ODataQueryArgumentsOptions; 28 | export type ODataFunctionOptions = ODataQueryArgumentsOptions & { 29 | alias?: boolean; 30 | }; 31 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/files/model/__fileName__.ts: -------------------------------------------------------------------------------- 1 | import { HttpHeaders, HttpParams } from '@angular/common/http'; 2 | import { Observable } from 'rxjs'; 3 | import { map } from 'rxjs/operators'; 4 | 5 | import { 6 | Model, 7 | ModelField, 8 | ODataModel, 9 | ODataCollection, 10 | ODataOptions, 11 | ODataQueryArgumentsOptions, 12 | ODataFunctionOptions, 13 | ODataActionOptions, 14 | Duration, 15 | } from 'angular-odata'; 16 | <% if (hasGeoFields) { %>import { <% for (let f of geoFields) { %><%= f.type() %>,<% } %> } from 'geojson';<% } %><% for (let imp of imports) { %> 17 | import { <%= imp.names.join(", ") %> } from '<%= imp.path() %>';<% } %> 18 | 19 | @Model() 20 | export class <%= classify(name) %>> extends <% if (baseType) { %><%= toTypescriptType(baseType) %><% } else { %>ODataModel<% } %> { 21 | <% for (let field of fields) { %>@ModelField() 22 | declare <%= field.name() %>: <%= field.type() %>; 23 | <%= field.resource() %><%= field.getter() %><%= field.setter() %><%= field.fetch() %> 24 | <% } %> 25 | <% for (let cal of callables) { %> 26 | // <%= cal.name() %> 27 | <%= cal.callableMethod() %> 28 | <% } %> 29 | <% for (let nav of navigations) { %><%= nav %> 30 | <% } %> 31 | } -------------------------------------------------------------------------------- /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 | import { Package } from './package'; 7 | 8 | export class Module extends Base { 9 | services: Service[] = []; 10 | constructor( 11 | protected pkg: Package, 12 | options: ApiGenSchema, 13 | ) { 14 | super(pkg, options); 15 | } 16 | public override template(): Source { 17 | return url('./files/module'); 18 | } 19 | public override variables(): { [name: string]: any } { 20 | return { 21 | services: this.services, 22 | }; 23 | } 24 | public addService(service: Service) { 25 | this.services.push(service); 26 | this.addDependency(service); 27 | } 28 | public override name() { 29 | return strings.classify(this.options.name) + 'Module'; 30 | } 31 | public override fileName() { 32 | return strings.dasherize(this.options.name) + '.module'; 33 | } 34 | public override directory() { 35 | return ''; 36 | } 37 | public override fullName() { 38 | return this.name(); 39 | } 40 | public override importTypes(): string[] { 41 | return []; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/api/js/libs/innersvg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * innerHTML property for SVGElement 3 | * Copyright(c) 2010, Jeff Schiller 4 | * 5 | * Licensed under the Apache License, Version 2 6 | * 7 | * Minor modifications by Chris Price to only polyfill when required. 8 | */ 9 | !function(e){if(e&&!("innerHTML"in e.prototype)){var t=function(e,r){var i=e.nodeType;if(3==i)r.push(e.textContent.replace(/&/,"&").replace(/",">"));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); -------------------------------------------------------------------------------- /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 }) => e().field(t.Pets)); 37 | 38 | expect(compare1.render()).toBe('Pets/$count'); 39 | }); 40 | 41 | it('count filter', () => { 42 | const compare1 = CountExpression.factory(({ e, t }) => 43 | e().field(t.Pets, ({ f }) => f.filter(({ e, t }) => e().gt(t.Age, 3))), 44 | ); 45 | 46 | expect(compare1.render()).toBe('Pets/$count($filter=Age gt 3)'); 47 | }); 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /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 }) => e().term('John')); 37 | 38 | expect(compare1.render()).toBe('John'); 39 | }); 40 | }); 41 | 42 | describe('combination e().and(...).or(...)', () => { 43 | it('and,or', () => { 44 | const compare = SearchExpression.factory(({ e }) => 45 | e().term('John').and(e().term('Lennon')).or(e().term('Beatles')), 46 | ); 47 | 48 | expect(compare.render()).toBe('(John AND Lennon) OR Beatles'); 49 | }); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /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/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 | toString() { 56 | return this.render({ 57 | aliases: [], 58 | escape: true, 59 | prefix: '', 60 | parser: undefined, 61 | options: {}, 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /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 | import { Package } from './package'; 6 | 7 | export class ApiConfig extends Base { 8 | constructor(pkg: Package, options: ApiGenSchema) { 9 | super(pkg, options); 10 | } 11 | public override template(): Source { 12 | return url('./files/api-config'); 13 | } 14 | public override variables(): { [name: string]: any } { 15 | return { 16 | serviceRootUrl: this.options.serviceRootUrl, 17 | metadataUrl: this.options.metadata, 18 | apiConfigName: this.options.name, 19 | version: this.options.version, 20 | creation: this.options.creation, 21 | models: this.pkg.models, 22 | collections: this.pkg.collections, 23 | }; 24 | } 25 | public override name() { 26 | return strings.classify(this.options.name) + 'Config'; 27 | } 28 | public override fileName() { 29 | return strings.dasherize(this.options.name) + '.config'; 30 | } 31 | public override directory() { 32 | return ''; 33 | } 34 | public override fullName() { 35 | return this.name(); 36 | } 37 | public override importTypes(): string[] { 38 | const imports = [ 39 | ...this.pkg.models.map((m) => m.fullName()), 40 | ...this.pkg.collections.map((c) => c.fullName()), 41 | ]; 42 | return imports; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /projects/angular-odata/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-odata", 3 | "version": "0.141.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": "^21.0.0", 36 | "@angular/core": "^21.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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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(map((body: any) => new ODataMetadataParser(body).metadata())); 42 | } 43 | //#endregion 44 | } 45 | -------------------------------------------------------------------------------- /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((annot) => new ODataAnnotation(annot)); 21 | } 22 | 23 | /** 24 | * Find an annotation inside the annotatable. 25 | * @param predicate Function that returns true if the annotation match. 26 | * @returns The annotation that matches the predicate. 27 | */ 28 | findAnnotation(predicate: (annot: ODataAnnotation) => boolean) { 29 | return this.annotations.find(predicate); 30 | } 31 | 32 | /** 33 | * Find an annotation inside the annotatable and return its value. 34 | * @param term The term of the annotation to find. 35 | * @returns The value of the annotation. 36 | */ 37 | annotatedValue(term: string | RegExp): T | undefined { 38 | const reg = term instanceof RegExp ? term : new RegExp(`^${term}$`); 39 | const annot = this.findAnnotation((a) => reg.test(a.term)); 40 | if (!annot) { 41 | return undefined; 42 | } 43 | return (annot.string || 44 | annot.bool || 45 | annot.int || 46 | annot.permissions || 47 | annot.properties) as any; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /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((n) => n.toJson()); 43 | } 44 | return json; 45 | } 46 | 47 | name() { 48 | return `${this.Name}`; 49 | } 50 | 51 | namespace() { 52 | return `${this.container.namespace()}`; 53 | } 54 | 55 | fullName() { 56 | return `${this.container.namespace()}.${this.Name}`; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /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 ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'].reduce( 27 | (acc: any, name, index) => { 28 | const v = parseFloat(matches[index + 2]); 29 | if (!Number.isNaN(v)) acc[name] = v; 30 | return acc; 31 | }, 32 | duration, 33 | ) as Duration; 34 | }, 35 | toString(v: Duration): string { 36 | return [ 37 | v.sign === -1 ? '-' : '', 38 | 'P', 39 | v.years ? v.years + 'Y' : '', 40 | v.months ? v.months + 'M' : '', 41 | v.weeks ? v.weeks + 'W' : '', 42 | v.days ? v.days + 'D' : '', 43 | 'T', 44 | v.hours ? v.hours + 'H' : '', 45 | v.minutes ? v.minutes + 'M' : '', 46 | v.seconds ? v.seconds + 'S' : '', 47 | ].join(''); 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /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(this, 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((n) => n.toJson()); 43 | } 44 | return json; 45 | } 46 | 47 | name() { 48 | return `${this.Name}`; 49 | } 50 | 51 | namespace() { 52 | return `${this.container.namespace()}`; 53 | } 54 | 55 | fullName() { 56 | return `${this.container.namespace()}.${this.Name}`; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /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/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 | import { Package } from './package'; 8 | 9 | export class EnumValue { 10 | constructor(private edmType: CsdlMember) {} 11 | public name() { 12 | return this.edmType.Name; 13 | } 14 | public value() { 15 | return this.edmType.Value; 16 | } 17 | } 18 | export class Enum extends Base { 19 | constructor( 20 | pkg: Package, 21 | options: ApiGenSchema, 22 | protected edmType: CsdlEnumType, 23 | ) { 24 | super(pkg, options); 25 | } 26 | public override template(): Source { 27 | return url('./files/enum'); 28 | } 29 | public override variables(): { [name: string]: any } { 30 | return { 31 | type: this.name() + 'EnumType', 32 | values: (this.edmType.Member ?? []).map((m) => new EnumValue(m)), 33 | }; 34 | } 35 | public override name() { 36 | return strings.classify(this.edmType.name()); 37 | } 38 | public override fileName() { 39 | return strings.dasherize(this.edmType.name()) + '.enum'; 40 | } 41 | public override directory() { 42 | return this.edmType.namespace().replace(/\./g, '/'); 43 | } 44 | public override fullName() { 45 | return this.edmType.fullName(); 46 | } 47 | public members() { 48 | return this.edmType.Member.map((m) => `${m.Name} = ${m.Value}`); 49 | } 50 | public flags() { 51 | return this.edmType.IsFlags; 52 | } 53 | public override importTypes(): string[] { 54 | return []; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 type { 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/metadata/metadata.ts: -------------------------------------------------------------------------------- 1 | import type { CsdlAction, CsdlFunction } from './csdl/csdl-function-action'; 2 | import { CsdlReference } from './csdl/csdl-reference'; 3 | import { CsdlSchema } from './csdl/csdl-schema'; 4 | import type { 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/loaders.ts: -------------------------------------------------------------------------------- 1 | import { Observable, forkJoin, map, of } from 'rxjs'; 2 | import type { 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(private readonly passedConfigs: ODataApiConfig | ODataApiConfig[]) {} 11 | 12 | loadConfigs(): Observable { 13 | return Array.isArray(this.passedConfigs) ? of(this.passedConfigs) : of([this.passedConfigs]); 14 | } 15 | } 16 | 17 | export class ODataConfigAsyncLoader implements ODataConfigLoader { 18 | constructor( 19 | private readonly configs$: 20 | | Observable[] 21 | | Observable, 22 | ) {} 23 | 24 | loadConfigs(): Observable { 25 | return Array.isArray(this.configs$) 26 | ? forkJoin(this.configs$) 27 | : (this.configs$ as Observable).pipe( 28 | map((value) => 29 | Array.isArray(value) ? (value as ODataApiConfig[]) : ([value] as ODataApiConfig[]), 30 | ), 31 | ); 32 | } 33 | } 34 | 35 | export class ODataMetadataLoader implements ODataConfigLoader { 36 | constructor( 37 | private readonly sources$: Observable, 38 | private readonly baseConfigs: ODataApiConfig | ODataApiConfig[], 39 | ) {} 40 | 41 | loadConfigs(): Observable { 42 | const configs = Array.isArray(this.baseConfigs) ? this.baseConfigs : [this.baseConfigs]; 43 | return this.sources$.pipe( 44 | map((source) => 45 | (Array.isArray(source) ? source : [source]).map((m, i) => 46 | new ODataMetadataParser(m).metadata().toConfig(configs[i] ?? {}), 47 | ), 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /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/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/csdl/csdl-navigation-property-binding.ts: -------------------------------------------------------------------------------- 1 | import { find } from 'rxjs'; 2 | import { CsdlEntitySet } from './csdl-entity-set'; 3 | import { CsdlSingleton } from './csdl-singleton'; 4 | import { CsdlEntityType } from './csdl-structured-type'; 5 | 6 | export class CsdlNavigationPropertyBinding { 7 | Path: string; 8 | Target: string; 9 | 10 | constructor( 11 | protected entitySet: CsdlEntitySet | CsdlSingleton, 12 | { Path, Target }: { Path: string; Target: string }, 13 | ) { 14 | this.Path = Path; 15 | this.Target = Target; 16 | } 17 | 18 | toJson() { 19 | return { 20 | Path: this.Path, 21 | Target: this.Target, 22 | }; 23 | } 24 | 25 | entityType() { 26 | return this.entitySet instanceof CsdlEntitySet 27 | ? this.entitySet.EntityType 28 | : this.entitySet.Type; 29 | } 30 | 31 | resolvePropertyName() { 32 | return this.Path.split('/').pop(); 33 | } 34 | 35 | resolvePropertyType(findEntityType: (fullName: string) => CsdlEntityType | undefined) { 36 | const parts = this.Path.split('/'); 37 | let entityType = findEntityType(this.entityType()); 38 | if (parts.length > 1) { 39 | for (let i = 0; i < parts.length - 1; i++) { 40 | const part = parts[i]; 41 | const baseEntity = findEntityType(part); 42 | if (baseEntity) { 43 | entityType = baseEntity; 44 | } else { 45 | const navProp = entityType?.findNavigationPropertyType(part, findEntityType); 46 | entityType = navProp ? findEntityType(navProp.Type) : undefined; 47 | } 48 | } 49 | } 50 | return entityType; 51 | } 52 | 53 | resolveNavigationPropertyType(findEntityType: (fullName: string) => CsdlEntityType | undefined) { 54 | const name = this.Path.split('/').pop(); 55 | let entity = this.resolvePropertyType(findEntityType); 56 | if (entity && name) { 57 | return entity.findNavigationPropertyType(name, findEntityType); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/api/js/search/search-lunr.js: -------------------------------------------------------------------------------- 1 | (function (compodoc) { 2 | function LunrSearchEngine() { 3 | this.index = undefined; 4 | this.store = {}; 5 | this.name = 'LunrSearchEngine'; 6 | } 7 | 8 | LunrSearchEngine.prototype.init = function () { 9 | var that = this, 10 | d = new promise.Promise(); 11 | 12 | that.index = lunr.Index.load(COMPODOC_SEARCH_INDEX.index); 13 | that.store = COMPODOC_SEARCH_INDEX.store; 14 | d.done(); 15 | 16 | return d; 17 | }; 18 | 19 | LunrSearchEngine.prototype.search = function (q, offset, length) { 20 | var that = this, 21 | results = [], 22 | d = new promise.Promise(); 23 | 24 | if (this.index) { 25 | results = this.index.search('*' + q + '*').map(function (result) { 26 | var doc = that.store[result.ref]; 27 | 28 | return { 29 | title: doc.title, 30 | url: doc.url, 31 | body: doc.summary || doc.body 32 | }; 33 | }); 34 | } 35 | 36 | d.done({ 37 | query: q, 38 | results: length === 0 ? results : results.slice(0, length), 39 | count: results.length 40 | }); 41 | 42 | return d; 43 | }; 44 | 45 | compodoc.addEventListener(compodoc.EVENTS.READY, function (event) { 46 | var engine = new LunrSearchEngine(), 47 | initialized = false; 48 | 49 | function query(q, offset, length) { 50 | if (!initialized) throw new Error('Search has not been initialized'); 51 | return engine.search(q, offset, length); 52 | } 53 | 54 | compodoc.search = { 55 | query: query 56 | }; 57 | 58 | engine.init().then(function () { 59 | initialized = true; 60 | compodoc.dispatchEvent({ 61 | type: compodoc.EVENTS.SEARCH_READY 62 | }); 63 | }); 64 | }); 65 | })(compodoc); 66 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/utils/arraybuffers.ts: -------------------------------------------------------------------------------- 1 | //https://github.com/niklasvh/base64-arraybuffer 2 | const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 3 | 4 | // Use a lookup table to find the index. 5 | const lookup = new Uint8Array(256); 6 | for (var i = 0; i < chars.length; i++) { 7 | lookup[chars.charCodeAt(i)] = i; 8 | } 9 | 10 | export const ArrayBuffers = { 11 | toArrayBuffer(v: string): ArrayBuffer { 12 | var bufferLength = v.length * 0.75, 13 | len = v.length, 14 | i, 15 | p = 0, 16 | encoded1, 17 | encoded2, 18 | encoded3, 19 | encoded4; 20 | 21 | if (v[v.length - 1] === '=') { 22 | bufferLength--; 23 | if (v[v.length - 2] === '=') { 24 | bufferLength--; 25 | } 26 | } 27 | 28 | var arraybuffer = new ArrayBuffer(bufferLength), 29 | bytes = new Uint8Array(arraybuffer); 30 | 31 | for (i = 0; i < len; i += 4) { 32 | encoded1 = lookup[v.charCodeAt(i)]; 33 | encoded2 = lookup[v.charCodeAt(i + 1)]; 34 | encoded3 = lookup[v.charCodeAt(i + 2)]; 35 | encoded4 = lookup[v.charCodeAt(i + 3)]; 36 | 37 | bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); 38 | bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); 39 | bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); 40 | } 41 | 42 | return arraybuffer; 43 | }, 44 | toString(v: ArrayBuffer): string { 45 | var bytes = new Uint8Array(v), 46 | i, 47 | len = bytes.length, 48 | base64 = ''; 49 | 50 | for (i = 0; i < len; i += 3) { 51 | base64 += chars[bytes[i] >> 2]; 52 | base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; 53 | base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; 54 | base64 += chars[bytes[i + 2] & 63]; 55 | } 56 | 57 | if (len % 3 === 2) { 58 | base64 = base64.substring(0, base64.length - 1) + '='; 59 | } else if (len % 3 === 1) { 60 | base64 = base64.substring(0, base64.length - 2) + '=='; 61 | } 62 | return base64; 63 | }, 64 | }; 65 | -------------------------------------------------------------------------------- /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({ Name, Value, Annotation }: { Name: string; Value?: number; Annotation?: any[] }) { 64 | super({ Annotation }); 65 | this.Name = Name; 66 | this.Value = Value; 67 | } 68 | 69 | override toJson() { 70 | const json: { [key: string]: any } = { ...super.toJson(), Name: this.Name }; 71 | if (this.Value !== undefined) { 72 | json['Value'] = this.Value; 73 | } 74 | return json; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /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(this, 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((n) => n.toJson()); 48 | } 49 | if (this.IncludeInServiceDocument !== undefined) { 50 | json['IncludeInServiceDocument'] = this.IncludeInServiceDocument; 51 | } 52 | return json; 53 | } 54 | 55 | name() { 56 | return `${this.Name}`; 57 | } 58 | 59 | namespace() { 60 | return `${this.container.namespace()}`; 61 | } 62 | 63 | fullName() { 64 | return `${this.container.namespace()}.${this.Name}`; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { 3 | EnvironmentProviders, 4 | InjectionToken, 5 | ModuleWithProviders, 6 | NgModule, 7 | Provider, 8 | makeEnvironmentProviders, 9 | } from '@angular/core'; 10 | import { ODataClient } from './client'; 11 | import { ODataConfigLoader, ODataConfigSyncLoader } from './loaders'; 12 | import { ODataServiceFactory } from './services/index'; 13 | import type { ODataApiConfig } from './types'; 14 | 15 | export interface PassedInitialConfig { 16 | config?: ODataApiConfig | ODataApiConfig[]; 17 | loader?: Provider; 18 | } 19 | 20 | export const ODATA_CONFIG = new InjectionToken('odata.config'); 21 | 22 | export function createSyncLoader(passedConfig: PassedInitialConfig) { 23 | return new ODataConfigSyncLoader(passedConfig.config!); 24 | } 25 | 26 | // Standalone version 27 | export function provideODataClient(passedConfig: PassedInitialConfig): EnvironmentProviders { 28 | return makeEnvironmentProviders([ 29 | { provide: ODATA_CONFIG, useValue: passedConfig }, 30 | passedConfig?.loader ?? { 31 | provide: ODataConfigLoader, 32 | useFactory: createSyncLoader, 33 | deps: [ODATA_CONFIG], 34 | }, 35 | ODataClient, 36 | ODataServiceFactory, 37 | ]); 38 | } 39 | 40 | // Module version 41 | @NgModule({ 42 | imports: [CommonModule], 43 | providers: [ODataClient, ODataServiceFactory], 44 | }) 45 | export class ODataModule { 46 | static forRoot(passedConfig: PassedInitialConfig): ModuleWithProviders { 47 | return { 48 | ngModule: ODataModule, 49 | providers: [ 50 | // Make the ODATA_CONFIG available through injection 51 | { provide: ODATA_CONFIG, useValue: passedConfig }, 52 | 53 | // Create the loader: Either the one getting passed or a sync one 54 | passedConfig?.loader ?? { 55 | provide: ODataConfigLoader, 56 | useFactory: createSyncLoader, 57 | deps: [ODATA_CONFIG], 58 | }, 59 | ODataClient, 60 | ODataServiceFactory, 61 | ], 62 | }; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 ? resource.api.structuredType(baseType) : undefined; 29 | const value = ODataValueResource.factory(resource.api, { 30 | segments: resource.cloneSegments(), 31 | }); 32 | 33 | // Switch entitySet to binding type if available 34 | if (baseSchema !== undefined && baseSchema.type() !== baseType) { 35 | let entitySet = resource.api.findEntitySet(baseSchema.type()); 36 | if (entitySet !== undefined) { 37 | value.segment((s) => s.entitySet().path(entitySet!.name)); 38 | } 39 | } 40 | 41 | return value; 42 | } 43 | override clone(): ODataValueResource { 44 | return super.clone() as ODataValueResource; 45 | } 46 | //#endregion 47 | 48 | //#region Requests 49 | protected override get(options: ODataOptions = {}): Observable { 50 | return super.get({ responseType: 'value', ...options }); 51 | } 52 | //#endregion 53 | 54 | //#region Shortcuts 55 | 56 | /** 57 | * Fetch the value of the resource. 58 | * @param options OData options. 59 | * @returns Observable of the value. 60 | */ 61 | fetch(options?: ODataOptions): Observable { 62 | return this.get(options); 63 | } 64 | 65 | //#endregion 66 | } 67 | -------------------------------------------------------------------------------- /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 }) => e().ascending(t.Age)); 37 | 38 | expect(compare1.render()).toBe('Age asc'); 39 | }); 40 | 41 | it('desc', () => { 42 | const compare1 = OrderByExpression.factory(({ e, t }) => e().descending(t.Age)); 43 | 44 | expect(compare1.render()).toBe('Age desc'); 45 | }); 46 | }); 47 | 48 | describe('combination e().ascending(...).descending(...)', () => { 49 | it('asc,desc', () => { 50 | const compare = OrderByExpression.factory(({ e, t }) => 51 | e().ascending(t.Age).descending(t.CreatedOn), 52 | ); 53 | 54 | expect(compare.render()).toBe('Age asc,CreatedOn desc'); 55 | }); 56 | }); 57 | 58 | describe('navigate main', () => { 59 | it('navigate', () => { 60 | const compare1 = OrderByExpression.factory(({ e, t }) => 61 | e().ascending(t.Car!.Year), 62 | ); 63 | expect(compare1.render()).toBe('Car/Year asc'); 64 | }); 65 | 66 | it('combination navigate', () => { 67 | const compare1 = OrderByExpression.factory(({ e, t }) => 68 | e().ascending(t.Car!.Year).descending(t.Car!.Model!.Name), 69 | ); 70 | expect(compare1.render()).toBe('Car/Year asc,Car/Model/Name desc'); 71 | }); 72 | }); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/cache/storage.ts: -------------------------------------------------------------------------------- 1 | import { ODataRequest, ODataResponse, ODataResponseJson } from '../resources'; 2 | import { ODataBaseCache, ODataCacheEntry } from './cache'; 3 | 4 | export class ODataInStorageCache extends ODataBaseCache { 5 | name: string; 6 | storage: Storage; 7 | 8 | constructor({ 9 | name, 10 | storage = sessionStorage, 11 | timeout, 12 | }: { 13 | timeout?: number; 14 | name: string; 15 | storage?: Storage; 16 | }) { 17 | super({ timeout }); 18 | this.name = name; 19 | this.storage = storage; 20 | this.restore(); 21 | window.addEventListener('beforeunload', () => this.store()); 22 | } 23 | 24 | /** 25 | * Store the cache in the storage 26 | */ 27 | store() { 28 | this.storage.setItem(this.name, JSON.stringify(Array.from(this.entries.entries()))); 29 | } 30 | 31 | /** 32 | * Restore the cache from the storage 33 | */ 34 | restore() { 35 | this.entries = new Map>( 36 | JSON.parse(this.storage.getItem(this.name) || '[]'), 37 | ); 38 | } 39 | 40 | /** 41 | * Flush the cache and clean the storage 42 | */ 43 | override flush() { 44 | super.flush(); 45 | this.store(); 46 | } 47 | 48 | /** 49 | * Store the response in the cache 50 | * @param req The request with the resource to store the response 51 | * @param res The response to store in the cache 52 | */ 53 | putResponse(req: ODataRequest, res: ODataResponse) { 54 | const scope = this.scope(req); 55 | const tags = this.tags(res); 56 | this.put>(req.cacheKey, res.toJson(), { 57 | timeout: res.options.maxAge, 58 | scope, 59 | tags, 60 | }); 61 | } 62 | 63 | /** 64 | * Restore the response from the cache 65 | * @param req The request with the resource to get the response 66 | * @returns The response from the cache 67 | */ 68 | getResponse(req: ODataRequest): ODataResponse | undefined { 69 | const scope = this.scope(req); 70 | const data = this.get>(req.cacheKey, { scope }); 71 | 72 | return data !== undefined ? ODataResponse.fromJson(req, data) : undefined; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/services/singleton.ts: -------------------------------------------------------------------------------- 1 | import type { Observable } from 'rxjs'; 2 | import type { ODataModel } from '../models/model'; 3 | import type { ODataEntity, ODataOptions, ODataSingletonResource } from '../resources'; 4 | import { ODataEntityService } from './entity'; 5 | 6 | /** 7 | * OData Singleton Service 8 | * www.odata.org/getting-started/advanced-tutorial/#singleton 9 | */ 10 | export class ODataSingletonService extends ODataEntityService { 11 | static Model?: typeof ODataModel; 12 | model(entity?: Partial) { 13 | const Service = this.constructor as typeof ODataSingletonService; 14 | return this.entity().asModel((entity ?? {}) as Partial, { 15 | ModelType: Service.Model, 16 | }); 17 | } 18 | /** 19 | * Get the entity resource for this service. 20 | * @param key The entity key. 21 | */ 22 | public entity(): ODataSingletonResource { 23 | return this.client.singleton(this.name, this.apiNameOrEntityType); 24 | } 25 | 26 | /** 27 | * Attach an existing model to this service. 28 | * @param model The model to attach. 29 | */ 30 | public attach>(model: M) { 31 | model.attach(this.entity()); 32 | } 33 | 34 | /** 35 | * The schema for the singleton. 36 | */ 37 | get singletonSchema() { 38 | return this.api.findEntitySet(this.name); 39 | } 40 | 41 | /** 42 | * Update the singleton entity 43 | * @param attrs The attributes for the entity. 44 | * @param etag The etag for the entity. 45 | * @param options The options for the request. 46 | */ 47 | public update( 48 | attrs: Partial, 49 | options?: ODataOptions & { etag?: string }, 50 | ): Observable> { 51 | const res = this.entity(); 52 | return res.update(attrs, options); 53 | } 54 | 55 | /** 56 | * Patch the singleton entity 57 | * @param attrs The attributes for the entity. 58 | * @param etag The etag for the entity. 59 | * @param options The options for the request. 60 | */ 61 | public patch( 62 | attrs: Partial, 63 | options?: ODataOptions & { etag?: string }, 64 | ): Observable> { 65 | const res = this.entity(); 66 | return res.modify(attrs, options); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /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 }) => e().field(t.Car)); 37 | 38 | expect(compare1.render()).toBe('Car'); 39 | }); 40 | 41 | it('navigation', () => { 42 | const compare1 = ExpandExpression.factory(({ e, t }) => e().field(t.Car?.Model)); 43 | 44 | expect(compare1.render()).toBe('Car/Model'); 45 | }); 46 | }); 47 | }); 48 | 49 | describe('nested condition', () => { 50 | describe('as factory function', () => { 51 | it('field', () => { 52 | const compare1 = ExpandExpression.factory(({ e, t }) => 53 | e().field(t.Car, (f) => { 54 | f.expand(({ e, t }) => e().field(t.Model)); 55 | f.skip(1); 56 | f.filter(({ e, t }) => e().eq(t.Year, 2000)); 57 | }), 58 | ); 59 | 60 | expect(compare1.render()).toBe('Car($expand=Model;$filter=Year eq 2000;$skip=1)'); 61 | }); 62 | 63 | it('navigation', () => { 64 | const compare1 = ExpandExpression.factory(({ e, t }) => 65 | e().field(t.Car.Model!, (f) => { 66 | f.filter(({ e, t }) => e().in(t.Name, ['BMW', 'Audi'])); 67 | f.skip(1); 68 | f.top(1); 69 | }), 70 | ); 71 | 72 | expect(compare1.render()).toBe("Car/Model($filter=Name in ('BMW','Audi');$skip=1;$top=1)"); 73 | }); 74 | }); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /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((i) => new CsdlIncludeAnnotations(i)); 22 | } 23 | 24 | override toJson() { 25 | const json: { [key: string]: any } = { ...super.toJson(), Uri: this.Uri }; 26 | if (Array.isArray(this.Include) && this.Include.length > 0) { 27 | json['Include'] = this.Include.map((i) => i.toJson()); 28 | } 29 | if (Array.isArray(this.IncludeAnnotations) && this.IncludeAnnotations.length > 0) { 30 | json['IncludeAnnotations'] = this.IncludeAnnotations.map((i) => i.toJson()); 31 | } 32 | return json; 33 | } 34 | } 35 | 36 | export class CsdlInclude { 37 | Namespace: string; 38 | Alias?: string; 39 | constructor({ Namespace, Alias }: { Namespace: string; Alias?: string }) { 40 | this.Namespace = Namespace; 41 | this.Alias = Alias; 42 | } 43 | 44 | toJson() { 45 | return { 46 | Namespace: this.Namespace, 47 | Alias: this.Alias, 48 | }; 49 | } 50 | } 51 | 52 | export class CsdlIncludeAnnotations { 53 | TermNamespace: string; 54 | Qualifier?: string; 55 | TargetNamespace?: string; 56 | constructor({ 57 | TermNamespace, 58 | Qualifier, 59 | TargetNamespace, 60 | }: { 61 | TermNamespace: string; 62 | Qualifier?: string; 63 | TargetNamespace?: string; 64 | }) { 65 | this.TermNamespace = TermNamespace; 66 | this.Qualifier = Qualifier; 67 | this.TargetNamespace = TargetNamespace; 68 | } 69 | 70 | toJson() { 71 | return { 72 | TermNamespace: this.TermNamespace, 73 | Qualifier: this.Qualifier, 74 | TargetNamespace: this.TargetNamespace, 75 | }; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /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); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/api/js/svg-pan-zoom.controls.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | if (document.getElementById('module-graph-svg')) { 3 | panZoom = svgPanZoom(document.getElementById('module-graph-svg').querySelector('svg'), { 4 | zoomEnabled: true, 5 | minZoom: 1, 6 | maxZoom: 5 7 | }); 8 | 9 | document.getElementById('zoom-in').addEventListener('click', function(ev) { 10 | ev.preventDefault(); 11 | panZoom.zoomIn(); 12 | }); 13 | 14 | document.getElementById('zoom-out').addEventListener('click', function(ev) { 15 | ev.preventDefault(); 16 | panZoom.zoomOut(); 17 | }); 18 | 19 | document.getElementById('reset').addEventListener('click', function(ev) { 20 | ev.preventDefault(); 21 | panZoom.resetZoom(); 22 | panZoom.resetPan(); 23 | }); 24 | 25 | var overviewFullscreen = false, 26 | originalOverviewHeight; 27 | 28 | document.getElementById('fullscreen').addEventListener('click', function(ev) { 29 | if (overviewFullscreen) { 30 | document.getElementById('module-graph-svg').style.height = originalOverviewHeight; 31 | overviewFullscreen = false; 32 | if (ev.target) { 33 | ev.target.classList.remove('ion-md-close'); 34 | ev.target.classList.add('ion-ios-resize'); 35 | } 36 | } else { 37 | originalOverviewHeight = document.getElementById('module-graph-svg').style.height; 38 | document.getElementById('module-graph-svg').style.height = '85vh'; 39 | overviewFullscreen = true; 40 | if (ev.target) { 41 | ev.target.classList.remove('ion-ios-resize'); 42 | ev.target.classList.add('ion-md-close'); 43 | } 44 | } 45 | document.getElementById('module-graph-svg').querySelector('svg').style.height = document.getElementById('module-graph-svg').clientHeight; 46 | setTimeout(function() { 47 | panZoom.resize(); 48 | panZoom.fit(); 49 | panZoom.center(); 50 | }, 0) 51 | }); 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/csdl/csdl-entity-set.ts: -------------------------------------------------------------------------------- 1 | import type { 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((n) => n.toJson()); 49 | } 50 | if (this.IncludeInServiceDocument !== undefined) { 51 | json['IncludeInServiceDocument'] = this.IncludeInServiceDocument; 52 | } 53 | return json; 54 | } 55 | 56 | name() { 57 | return `${this.Name}`; 58 | } 59 | 60 | namespace() { 61 | return `${this.container.namespace()}`; 62 | } 63 | 64 | fullName() { 65 | return `${this.container.namespace()}.${this.Name}`; 66 | } 67 | 68 | override toConfig(): ODataEntitySetConfig { 69 | return { 70 | ...super.toConfig(), 71 | name: this.Name, 72 | entityType: this.EntityType, 73 | service: {}, 74 | } as ODataEntitySetConfig; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /docs/api/js/sourceCode.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | var $tabSource = document.querySelector('#source-tab'), 3 | $tabInfo = document.querySelector('#info-tab'), 4 | $tabReadme = document.querySelector('#readme-tab'), 5 | $tabTemplate = document.querySelector('#templateData-tab'), 6 | $tabTree = document.querySelector('#tree-tab'), 7 | $tabExample = document.querySelector('#example-tab'), 8 | $prismPre = document.querySelector('pre.compodoc-sourcecode'); 9 | if ($tabSource && $prismPre) { 10 | $prismCode = $prismPre.querySelector('code'), 11 | $content = document.querySelector('.content'), 12 | prismLinks = document.querySelectorAll('.link-to-prism') 13 | 14 | for (var i = 0; i < prismLinks.length; i++) { 15 | prismLinks[i].addEventListener('click', linkToPrism, false); 16 | } 17 | 18 | function linkToPrism(event) { 19 | var targetLine = event.target.getAttribute('data-line'); 20 | event.preventDefault(); 21 | 22 | $prismPre.setAttribute('data-line', targetLine); 23 | Prism.highlightElement($prismCode, function() {}); 24 | 25 | $tabSource.click(); 26 | 27 | setTimeout(function() { 28 | var $prismHighlightLine = document.querySelector('.line-highlight'), 29 | top = parseInt(getComputedStyle($prismHighlightLine)['top']); 30 | $content.scrollTop = top; 31 | }, 500); 32 | }; 33 | 34 | window.onhashchange = function(event) { 35 | switch (window.location.hash) { 36 | case '': 37 | case '#info': 38 | $tabInfo.click(); 39 | break; 40 | case '#readme': 41 | $tabReadme.click(); 42 | break; 43 | case '#source': 44 | $tabSource.click(); 45 | break; 46 | case '#template': 47 | $tabTemplate.click(); 48 | break; 49 | case '#dom-tree': 50 | $tabTree.click(); 51 | break; 52 | case '#example': 53 | $tabExample.click(); 54 | break; 55 | } 56 | } 57 | } 58 | }); 59 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/resources/options.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | CacheCacheability, 3 | ODataMetadataType, 4 | ODataVersion, 5 | ParserOptions, 6 | ResponseOptions, 7 | } from '../types'; 8 | import { DEFAULT_VERSION, MAX_AGE, VERSION_2_0, VERSION_3_0, VERSION_4_0 } from '../constants'; 9 | 10 | import { ODataHelper } from '../helper'; 11 | 12 | export class ODataResponseOptions implements ResponseOptions { 13 | version: ODataVersion; 14 | streaming?: boolean; 15 | // OData 16 | metadata?: ODataMetadataType; 17 | ieee754Compatible?: boolean; 18 | // Location 19 | location?: string; 20 | // Cache 21 | cacheability?: 'public' | 'private' | 'no-cache' | 'no-store'; 22 | maxAge?: number; 23 | 24 | constructor(config: ParserOptions) { 25 | this.version = config.version || DEFAULT_VERSION; 26 | } 27 | 28 | get helper() { 29 | return ODataHelper[this.version]; 30 | } 31 | 32 | clone() { 33 | return new ODataResponseOptions(this); 34 | } 35 | 36 | setFeatures(features: string) { 37 | features.split(';').forEach((o) => { 38 | let [k, v] = o.split('='); 39 | switch (k.trim()) { 40 | case 'odata.metadata': 41 | this.metadata = v as ODataMetadataType; 42 | break; 43 | case 'odata.streaming': 44 | this.streaming = v == 'true'; 45 | break; 46 | case 'IEEE754Compatible': 47 | this.ieee754Compatible = v == 'true'; 48 | break; 49 | } 50 | }); 51 | } 52 | 53 | setVersion(version: string) { 54 | const value = version.replace(/\;/g, '').trim(); 55 | if ([VERSION_2_0, VERSION_3_0, VERSION_4_0].indexOf(value) !== -1) 56 | this.version = value as ODataVersion; 57 | } 58 | 59 | setLocation(location: string) { 60 | // TODO: resolve location? 61 | this.location = location; 62 | } 63 | 64 | setPreferenceApplied(preference: string) { 65 | preference.split(',').forEach((prefer) => { 66 | // TODO: resolve preference 67 | }); 68 | } 69 | 70 | setCache(cacheControl: string) { 71 | cacheControl.split(',').forEach((directive) => { 72 | if (directive.startsWith(MAX_AGE)) { 73 | let maxAge = Number(directive.split('=')[1]); 74 | if (!Number.isNaN(maxAge)) this.maxAge = maxAge; 75 | } 76 | if (['public', 'private', 'no-cache', 'no-store'].indexOf(directive) !== -1) { 77 | this.cacheability = directive as CacheCacheability; 78 | } 79 | }); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /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(enums: E, value: any): number | undefined { 11 | if (value in enums) return typeof value === 'string' ? enums[value] : value; 12 | return undefined; 13 | }, 14 | 15 | toValues(enums: E, value: any): number[] { 16 | if (typeof value === 'number') { 17 | return this.values(enums).filter((v) => (value & v) === v); 18 | } 19 | if (typeof value === 'string') { 20 | value = value.split(',').map((o) => o.trim()); 21 | } 22 | if (Array.isArray(value) && value.every((v) => v in enums)) { 23 | return value.map((o) => this.toValue(enums, o) as number); 24 | } 25 | return []; 26 | }, 27 | 28 | toName(enums: E, value: any): string | undefined { 29 | if (value in enums) return typeof value === 'number' ? enums[value] : value; 30 | return undefined; 31 | }, 32 | 33 | toNames(enums: E, value: any): string[] { 34 | if (typeof value === 'number') { 35 | return this.values(enums) 36 | .filter((v) => (value & v) === v) 37 | .map((v) => this.toName(enums, v) as string); 38 | } 39 | if (typeof value === 'string') { 40 | value = value.split(',').map((o) => o.trim()); 41 | } 42 | if (Array.isArray(value) && value.every((v) => v in enums)) { 43 | return value.map((o) => this.toName(enums, o) as string); 44 | } 45 | return []; 46 | }, 47 | 48 | toFlags(enums: E, value: any): string[] { 49 | if (typeof value === 'number') { 50 | return this.values(enums) 51 | .filter((v) => v !== 0 && (value & v) === v) 52 | .map((v) => this.toName(enums, v) as string); 53 | } 54 | if (typeof value === 'string') { 55 | value = value.split(',').map((o) => o.trim()); 56 | } 57 | if (Array.isArray(value) && value.every((v) => v in enums)) { 58 | return value.filter((v) => enums[v]).map((v) => this.toName(enums, v) as string); 59 | } 60 | return []; 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /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 | entitySetPath?: string; 8 | bound?: boolean; 9 | composable?: boolean; 10 | 11 | constructor(config: ODataCallableConfig, schema: ODataSchema) { 12 | super(config, schema, new ODataCallableParser(config, schema.namespace, schema.alias)); 13 | this.entitySetPath = config.entitySetPath; 14 | this.bound = config.bound; 15 | this.composable = config.composable; 16 | } 17 | 18 | path() { 19 | let path: string; 20 | if (this.entitySetPath) path = this.entitySetPath; 21 | else if (this.bound) path = `${this.schema.namespace}.${this.name}`; 22 | else 23 | path = this.parser.return 24 | ? this.api.findEntitySet(this.parser.return.type)?.name || this.name 25 | : this.name; 26 | return path; 27 | } 28 | 29 | configure({ options }: { options: ParserOptions }) { 30 | this.parser.configure({ 31 | options, 32 | parserForType: (t: string) => this.api.parserForType(t), 33 | }); 34 | } 35 | 36 | /** 37 | * Deseialize the given value from the callable. 38 | * @param value Value to deserialize 39 | * @param options Options for deserialization 40 | * @returns Deserialized value 41 | */ 42 | deserialize(value: any, options?: ParserOptions): any { 43 | return this.parser.deserialize(value, options); 44 | } 45 | 46 | /** 47 | * Serialize the given value for the callable. 48 | * @param value Value to serialize 49 | * @param options Options for serialization 50 | * @returns Serialized value 51 | */ 52 | serialize(value: any, options?: ParserOptions): any { 53 | return this.parser.serialize(value, options); 54 | } 55 | 56 | /** 57 | * Encode the given value for the callable. 58 | * @param value Value to encode 59 | * @param options Options for encoding 60 | * @returns Encoded value 61 | */ 62 | encode(value: any, options?: ParserOptions): any { 63 | return this.parser.encode(value, options); 64 | } 65 | 66 | /** 67 | * Returns the binding parameter of the callable. 68 | * @returns The binding parameter of the callable. 69 | */ 70 | binding() { 71 | return this.parser.binding(); 72 | } 73 | 74 | returnType() { 75 | return this.parser.returnType(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /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' ? n : n.render({ aliases, escape, prefix, parser, options }), 65 | ) 66 | .join(','); 67 | } 68 | 69 | clone() { 70 | return new SelectExpression({ 71 | children: this._children.map((c) => c.clone()), 72 | }); 73 | } 74 | 75 | private _add(node: Renderable): SelectExpression { 76 | this._children.push(node); 77 | return this; 78 | } 79 | 80 | field(field: any): SelectExpression { 81 | return this._add(field); 82 | } 83 | 84 | fields(...fields: any[]): SelectExpression { 85 | fields.forEach((f) => this._add(f)); 86 | return this; 87 | } 88 | 89 | combine(expression: SelectExpression): SelectExpression { 90 | return this._add(expression); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /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(data: ArrayBuffer | Blob, options: ODataOptions = {}): Observable { 40 | return super.put(data, options); 41 | } 42 | //#endregion 43 | 44 | //#region Shortcuts 45 | fetch(options: { responseType: 'arraybuffer' } & ODataOptions): Observable; 46 | fetch(options: { responseType: 'blob' } & ODataOptions): Observable; 47 | fetch(options: { responseType: any } & ODataOptions): Observable { 48 | return this.get(options); 49 | } 50 | 51 | fetchArraybuffer(options: ODataOptions = {}): Observable { 52 | return this.fetch({ responseType: 'arraybuffer', ...options }); 53 | } 54 | 55 | fetchBlob(options: ODataOptions = {}): Observable { 56 | return this.fetch({ responseType: 'blob', ...options }); 57 | } 58 | 59 | upload(data: ArrayBuffer | Blob, options: ODataOptions = {}): Observable { 60 | return this.put(data, options); 61 | } 62 | 63 | uploadArrayBuffer( 64 | data: ArrayBuffer, 65 | contentType: string, 66 | options: ODataOptions = {}, 67 | ): Observable { 68 | options.headers = Http.mergeHttpHeaders(options.headers || {}, { 69 | 'Content-Type': contentType, 70 | }); 71 | return this.upload(data, options); 72 | } 73 | 74 | uploadBlob(data: Blob, options: ODataOptions = {}): Observable { 75 | return this.upload(data, options); 76 | } 77 | //#endregion 78 | } 79 | -------------------------------------------------------------------------------- /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((f) => new CsdlFunctionImport(this, f)); 42 | this.ActionImport = ActionImport?.map((a) => new CsdlActionImport(this, a)); 43 | } 44 | 45 | override toJson() { 46 | const json: { [key: string]: any } = { ...super.toJson() }; 47 | if (this.Extend !== undefined) { 48 | json['Extend'] = this.Extend; 49 | } 50 | if (Array.isArray(this.EntitySet) && this.EntitySet.length > 0) { 51 | json['EntitySet'] = this.EntitySet.map((a) => a.toJson()); 52 | } 53 | if (Array.isArray(this.Singleton) && this.Singleton.length > 0) { 54 | json['Singleton'] = this.Singleton.map((a) => a.toJson()); 55 | } 56 | if (Array.isArray(this.FunctionImport) && this.FunctionImport.length > 0) { 57 | json['FunctionImport'] = this.FunctionImport.map((a) => a.toJson()); 58 | } 59 | if (Array.isArray(this.ActionImport) && this.ActionImport.length > 0) { 60 | json['ActionImport'] = this.ActionImport.map((a) => a.toJson()); 61 | } 62 | return json; 63 | } 64 | 65 | name() { 66 | return `${this.Name}`; 67 | } 68 | 69 | namespace() { 70 | return `${this.schema.Namespace}`; 71 | } 72 | 73 | fullName() { 74 | return `${this.schema.Namespace}.${this.Name}`; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/api/template-playground/zip-export.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | declare const JSZip: any; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class ZipExportService { 9 | 10 | exportTemplates(files: any[]) { 11 | const zip = new JSZip(); 12 | 13 | // Add all template files to the ZIP 14 | files.forEach(file => { 15 | zip.file(file.path, file.content); 16 | }); 17 | 18 | // Add a README with instructions 19 | const readme = this.generateReadme(); 20 | zip.file('README.md', readme); 21 | 22 | // Generate and download the ZIP file 23 | zip.generateAsync({ type: 'blob' }) 24 | .then((content: Blob) => { 25 | this.downloadBlob(content, 'compodoc-templates.zip'); 26 | }); 27 | } 28 | 29 | private generateReadme(): string { 30 | return `# Compodoc Custom Templates 31 | 32 | This ZIP file contains customized templates for Compodoc documentation generation. 33 | 34 | ## Contents 35 | 36 | - **Templates** (\`.hbs\` files): Handlebars templates for generating documentation pages 37 | - **Styles** (\`.css\` files): Stylesheets for customizing the appearance 38 | - **Scripts** (\`.js\` files): JavaScript files for additional functionality 39 | 40 | ## Usage 41 | 42 | 1. Extract this ZIP file to a directory on your system 43 | 2. Use the \`--templates\` flag when running Compodoc to specify the path to your custom templates: 44 | 45 | \`\`\`bash 46 | compodoc -p tsconfig.json --templates ./path/to/custom/templates/ 47 | \`\`\` 48 | 49 | ## Template Structure 50 | 51 | - \`page.hbs\` - Main page template 52 | - \`partials/\` - Directory containing partial templates 53 | - \`styles/\` - Directory containing CSS files 54 | - \`js/\` - Directory containing JavaScript files 55 | 56 | ## Customization Tips 57 | 58 | 1. **Templates**: Use Handlebars syntax to customize the HTML structure 59 | 2. **Styles**: Modify CSS to change colors, fonts, layout, etc. 60 | 3. **Scripts**: Add custom JavaScript functionality 61 | 62 | ## Backup 63 | 64 | Always keep a backup of your original templates before making changes. 65 | 66 | ## Documentation 67 | 68 | For more information about customizing Compodoc templates, visit: 69 | https://compodoc.app/guides/template-customization.html 70 | 71 | Generated by Compodoc Template Playground 72 | `; 73 | } 74 | 75 | private downloadBlob(blob: Blob, filename: string) { 76 | const url = window.URL.createObjectURL(blob); 77 | const a = document.createElement('a'); 78 | a.href = url; 79 | a.download = filename; 80 | a.style.display = 'none'; 81 | document.body.appendChild(a); 82 | a.click(); 83 | document.body.removeChild(a); 84 | window.URL.revokeObjectURL(url); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-odata", 3 | "version": "0.141.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": true, 27 | "scripts": { 28 | "ng": "ng", 29 | "start": "ng serve", 30 | "build": "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 --watch=false", 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.141.0 --allow-same-version && cd projects/angular-odata && npm version 0.141.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 | "trippin": "ng generate angular-odata:apigen --name=TripPin --metadata='https://services.odata.org/V4/TripPinServiceRW/$metadata'", 40 | "prettier": "prettier --write \"projects/angular-odata/**/*.ts\"" 41 | }, 42 | "prettier": { 43 | "printWidth": 100, 44 | "singleQuote": true, 45 | "overrides": [ 46 | { 47 | "files": "*.html", 48 | "options": { 49 | "parser": "angular" 50 | } 51 | } 52 | ] 53 | }, 54 | "packageManager": "npm@11.6.2", 55 | "dependencies": { 56 | "@angular/common": "^21.0.0", 57 | "@angular/compiler": "^21.0.0", 58 | "@angular/core": "^21.0.0", 59 | "@angular/forms": "^21.0.0", 60 | "@angular/platform-browser": "^21.0.0", 61 | "@angular/router": "^21.0.0", 62 | "@schematics/angular": "^21.0.3", 63 | "rxjs": "~7.8.0", 64 | "tslib": "^2.3.0" 65 | }, 66 | "devDependencies": { 67 | "@angular/build": "^21.0.3", 68 | "@angular/cli": "^21.0.3", 69 | "@angular/compiler-cli": "^21.0.0", 70 | "@compodoc/compodoc": "^1.1.32", 71 | "jsdom": "^27.1.0", 72 | "ng-packagr": "^21.0.0", 73 | "prettier": "^3.7.4", 74 | "typescript": "~5.9.2", 75 | "vitest": "^4.0.15" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /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 | members: { [name: string]: number } | { [value: number]: string }; 8 | constructor(config: ODataEnumTypeConfig, schema: ODataSchema) { 9 | super(config, schema, new ODataEnumTypeParser(config, schema.namespace, schema.alias)); 10 | this.members = config.members; 11 | } 12 | 13 | configure({ options }: { options: ParserOptions }) { 14 | this.parser.configure({ options }); 15 | } 16 | 17 | /** 18 | * Returns the fields of the enum type. 19 | * @returns The fields of the enum type. 20 | */ 21 | fields(namesValue?: string | number): ODataEnumTypeFieldParser[] { 22 | return this.parser.fields(namesValue); 23 | } 24 | 25 | /** 26 | * Find a field by name or value. 27 | * @param enu The name or value of the field 28 | * @returns The field with the given name or value 29 | */ 30 | field(nameValue: string | number) { 31 | return this.parser.field(nameValue); 32 | } 33 | 34 | /** 35 | * Map the fields of the enum type. 36 | * @param mapper Function that maps the value to the new value 37 | * @returns The fields mapped by the mapper 38 | */ 39 | mapFields(mapper: (field: ODataEnumTypeFieldParser) => T) { 40 | return this.parser.mapFields(mapper); 41 | } 42 | 43 | /** 44 | * Deseialize the given value from the enum type. 45 | * @param value Value to deserialize 46 | * @param options Options for deserialization 47 | * @returns Deserialized value 48 | */ 49 | deserialize(value: any, options?: ParserOptions): E { 50 | return this.parser.deserialize(value, options); 51 | } 52 | 53 | /** 54 | * Serialize the given value for the enum type. 55 | * @param value Value to serialize 56 | * @param options Options for serialization 57 | * @returns Serialized value 58 | */ 59 | serialize(value: E, options?: ParserOptions): any { 60 | return this.parser.serialize(value, options); 61 | } 62 | 63 | /** 64 | * Encode the given value for the enum type. 65 | * @param value Value to encode 66 | * @param options Options for encoding 67 | * @returns Encoded value 68 | */ 69 | encode(value: E, options?: ParserOptions): any { 70 | return this.parser.encode(value, options); 71 | } 72 | 73 | unpack(value: string | number) { 74 | return this.parser.unpack(value); 75 | } 76 | 77 | pack(value: string | number | number[]) { 78 | return this.parser.pack(value); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/schema/schema.ts: -------------------------------------------------------------------------------- 1 | import { ODataApi } from '../api'; 2 | import { ParserOptions, ODataSchemaConfig, ODataStructuredTypeConfig } from '../types'; 3 | import { OData } from '../utils/odata'; 4 | import { ODataAnnotatable } from './annotation'; 5 | import { ODataCallable } from './callable'; 6 | import { ODataEntityContainer } from './entity-container'; 7 | import { ODataEntitySet } from './entity-set'; 8 | import { ODataEnumType } from './enum-type'; 9 | import { ODataSingleton } from './singleton'; 10 | import { ODataStructuredType } from './structured-type'; 11 | 12 | export class ODataSchema extends ODataAnnotatable { 13 | api: ODataApi; 14 | namespace: string; 15 | alias?: string; 16 | enums: ODataEnumType[]; 17 | entities: ODataStructuredType[]; 18 | callables: ODataCallable[]; 19 | containers: ODataEntityContainer[]; 20 | 21 | constructor(config: ODataSchemaConfig, api: ODataApi) { 22 | super(config); 23 | this.api = api; 24 | this.namespace = config.namespace; 25 | this.alias = config.alias; 26 | this.enums = (config.enums ?? []).map((config) => new ODataEnumType(config, this)); 27 | this.entities = (config.entities ?? []).map((config) => new ODataStructuredType(config, this)); 28 | this.callables = OData.mergeCallableParameters(config.callables ?? []).map( 29 | (config) => new ODataCallable(config, this), 30 | ); 31 | this.containers = (config.containers ?? []).map( 32 | (config) => new ODataEntityContainer(config, this), 33 | ); 34 | } 35 | 36 | isNamespaceOf(type: string) { 37 | return type.startsWith(this.namespace) ?? (this.alias && type.startsWith(this.alias)); 38 | } 39 | 40 | get entitySets() { 41 | return this.containers.reduce( 42 | (acc, container) => [...acc, ...container.entitySets], 43 | [] as ODataEntitySet[], 44 | ); 45 | } 46 | 47 | get singletons() { 48 | return this.containers.reduce( 49 | (acc, container) => [...acc, ...container.singletons], 50 | [] as ODataSingleton[], 51 | ); 52 | } 53 | 54 | //#region Find for Type 55 | public createStructuredType(config: ODataStructuredTypeConfig) { 56 | const entity = new ODataStructuredType(config, this); 57 | entity.configure({ options: this.api.options.parserOptions }); 58 | this.entities.push(entity); 59 | return entity; 60 | } 61 | //#endregion 62 | 63 | configure({ options }: { options: ParserOptions }) { 64 | // Configure Enums 65 | this.enums.forEach((enu) => enu.configure({ options })); 66 | // Configure Entities 67 | this.entities.forEach((structured) => structured.configure({ options })); 68 | // Configure callables 69 | this.callables.forEach((callable) => callable.configure({ options })); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/angular/collection.ts: -------------------------------------------------------------------------------- 1 | import { strings } from '@angular-devkit/core'; 2 | import { Base } from './base'; 3 | import { CsdlComplexType, CsdlEntityType } from '../metadata/csdl/csdl-structured-type'; 4 | import { url, Source } from '@angular-devkit/schematics'; 5 | import { Schema as ApiGenSchema } from '../schema'; 6 | import { Model } from './model'; 7 | import { Entity } from './entity'; 8 | import { Package } from './package'; 9 | 10 | export class Collection extends Base { 11 | constructor( 12 | pkg: Package, 13 | options: ApiGenSchema, 14 | protected edmType: CsdlEntityType | CsdlComplexType, 15 | protected entity: Entity, 16 | protected model: Model, 17 | ) { 18 | super(pkg, options); 19 | } 20 | 21 | public entityType() { 22 | return this.edmType.fullName(); 23 | } 24 | 25 | public override template(): Source { 26 | return url('./files/collection'); 27 | } 28 | public override variables(): { [name: string]: any } { 29 | return { 30 | type: this.name() + 'Collection', 31 | baseType: this.edmType.BaseType ? this.edmType.BaseType + 'Collection' : null, 32 | entity: this.entity, 33 | model: this.model, 34 | callables: this.callables ?? [], 35 | functions: [], 36 | }; 37 | } 38 | public override name() { 39 | return strings.classify(this.edmType.name()) + 'Collection'; 40 | } 41 | public override fileName() { 42 | return strings.dasherize(this.edmType.name()) + '.collection'; 43 | } 44 | public override directory() { 45 | return this.edmType.namespace().replace(/\./g, '/'); 46 | } 47 | public override fullName() { 48 | return this.edmType.fullName() + 'Collection'; 49 | } 50 | public override importTypes(): string[] { 51 | const imports = [this.entity.fullName(), this.model.fullName()]; 52 | if (this.edmType.BaseType) { 53 | imports.push(this.edmType.BaseType); 54 | imports.push(this.edmType.BaseType + 'Collection'); 55 | imports.push(this.edmType.BaseType + 'Model'); 56 | } 57 | for (let prop of this.edmType?.Property ?? []) { 58 | if (!prop.Type.startsWith('Edm.')) { 59 | imports.push(prop.Type); 60 | imports.push(prop.Type + 'Model'); 61 | if (prop.Collection) { 62 | imports.push(prop.Type + 'Collection'); 63 | } 64 | } 65 | } 66 | for (let prop of this.edmType?.NavigationProperty ?? []) { 67 | if (!prop.Type.startsWith('Edm.')) { 68 | imports.push(prop.Type); 69 | imports.push(prop.Type + 'Model'); 70 | if (prop.Collection) { 71 | imports.push(prop.Type + 'Collection'); 72 | } 73 | } 74 | } 75 | for (let callable of this.callables ?? []) { 76 | imports.push(...callable.importTypes()); 77 | } 78 | return imports; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | import { Package } from './package'; 9 | 10 | export class Service extends Base { 11 | constructor( 12 | pkg: Package, 13 | options: ApiGenSchema, 14 | protected edmElement: CsdlEntitySet | CsdlEntityContainer | CsdlSingleton, 15 | ) { 16 | super(pkg, options); 17 | } 18 | public override template(): Source { 19 | return this.edmElement instanceof CsdlEntitySet 20 | ? url('./files/entityset-service') 21 | : this.edmElement instanceof CsdlSingleton 22 | ? url('./files/singleton-service') 23 | : url('./files/entitycontainer-service'); 24 | } 25 | public override variables(): { [name: string]: any } { 26 | return { 27 | path: this.edmElement.name(), 28 | type: 29 | this.edmElement instanceof CsdlEntitySet 30 | ? this.edmElement.EntityType 31 | : this.edmElement instanceof CsdlSingleton 32 | ? this.edmElement.Type 33 | : this.options.name, 34 | callables: this.callables ?? [], 35 | }; 36 | } 37 | public entityType() { 38 | return this.edmElement instanceof CsdlEntitySet 39 | ? this.edmElement.EntityType 40 | : this.edmElement instanceof CsdlSingleton 41 | ? this.edmElement.Type 42 | : ''; 43 | } 44 | 45 | public override name() { 46 | return strings.classify(this.edmElement.name()) + 'Service'; 47 | } 48 | public override fileName() { 49 | return strings.dasherize(this.edmElement.name()) + '.service'; 50 | } 51 | public override directory() { 52 | return this.edmElement.namespace().replace(/\./g, '/'); 53 | } 54 | public override fullName() { 55 | return this.edmElement.fullName(); 56 | } 57 | public override importTypes(): string[] { 58 | const imports = []; 59 | if (this.edmElement instanceof CsdlEntitySet) { 60 | imports.push(this.edmElement.EntityType); 61 | } else if (this.edmElement instanceof CsdlSingleton) { 62 | imports.push(this.edmElement.Type); 63 | } 64 | for (var call of this.callables ?? []) { 65 | const ret = call.returnType(); 66 | if (ret !== undefined && !ret.Type.startsWith('Edm.')) { 67 | imports.push(ret.Type); 68 | } 69 | const { binding, required, optional } = call.parameters(); 70 | for (let param of [...required, ...optional]) { 71 | if (!param.Type.startsWith('Edm.')) { 72 | imports.push(param.Type); 73 | } 74 | } 75 | } 76 | return imports; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/csdl/csdl-enum-type.ts: -------------------------------------------------------------------------------- 1 | import type { 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({ Name, Value, Annotation }: { Name: string; Value?: number; Annotation?: any[] }) { 80 | super({ Annotation }); 81 | this.Name = Name; 82 | this.Value = Value; 83 | } 84 | 85 | override toJson() { 86 | const json: { [key: string]: any } = { ...super.toJson(), Name: this.Name }; 87 | if (this.Value !== undefined) { 88 | json['Value'] = this.Value; 89 | } 90 | return json; 91 | } 92 | 93 | override toConfig(base?: Partial): ODataEnumTypeFieldConfig { 94 | const config: { [key: string]: any } = { 95 | ...super.toConfig(), 96 | value: this.Value, 97 | }; 98 | return config as ODataEnumTypeFieldConfig; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /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 type { 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((f) => new CsdlFunctionImport(this, f)); 43 | this.ActionImport = ActionImport?.map((a) => new CsdlActionImport(this, a)); 44 | } 45 | 46 | override toJson() { 47 | const json: { [key: string]: any } = { ...super.toJson() }; 48 | if (this.Extend !== undefined) { 49 | json['Extend'] = this.Extend; 50 | } 51 | if (Array.isArray(this.EntitySet) && this.EntitySet.length > 0) { 52 | json['EntitySet'] = this.EntitySet.map((a) => a.toJson()); 53 | } 54 | if (Array.isArray(this.Singleton) && this.Singleton.length > 0) { 55 | json['Singleton'] = this.Singleton.map((a) => a.toJson()); 56 | } 57 | if (Array.isArray(this.FunctionImport) && this.FunctionImport.length > 0) { 58 | json['FunctionImport'] = this.FunctionImport.map((a) => a.toJson()); 59 | } 60 | if (Array.isArray(this.ActionImport) && this.ActionImport.length > 0) { 61 | json['ActionImport'] = this.ActionImport.map((a) => a.toJson()); 62 | } 63 | return json; 64 | } 65 | 66 | name() { 67 | return `${this.Name}`; 68 | } 69 | 70 | namespace() { 71 | return `${this.schema.Namespace}`; 72 | } 73 | 74 | fullName() { 75 | return `${this.schema.Namespace}.${this.Name}`; 76 | } 77 | 78 | override toConfig(base?: Partial): ODataEntityContainerConfig { 79 | return { 80 | ...super.toConfig(), 81 | name: this.Name, 82 | entitySets: this.EntitySet?.map((t) => t.toConfig()), 83 | }; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /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> extends ODataSchemaElement { 75 | parser: P; 76 | constructor( 77 | config: { annotations?: ODataAnnotationConfig[]; name: string }, 78 | schema: ODataSchema, 79 | parser: P, 80 | ) { 81 | super(config, schema); 82 | this.parser = parser; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /projects/angular-odata/schematics/apigen/metadata/metadata.ts: -------------------------------------------------------------------------------- 1 | import { CsdlEntityContainer } from './csdl/csdl-entity-container'; 2 | import { CsdlEntitySet } from './csdl/csdl-entity-set'; 3 | import { CsdlEnumType } from './csdl/csdl-enum-type'; 4 | import { CsdlAction, CsdlFunction } from './csdl/csdl-function-action'; 5 | import { CsdlReference } from './csdl/csdl-reference'; 6 | import { CsdlSchema } from './csdl/csdl-schema'; 7 | import { CsdlComplexType, CsdlEntityType } from './csdl/csdl-structured-type'; 8 | 9 | export class ODataMetadata { 10 | Version: string; 11 | References: CsdlReference[]; 12 | Schemas: CsdlSchema[]; 13 | constructor(Version: string, References: any[], Schemas: any[]) { 14 | this.Version = Version; 15 | this.References = References?.map((r) => new CsdlReference(r)); 16 | this.Schemas = Schemas?.map((s) => new CsdlSchema(s)); 17 | } 18 | 19 | toJson() { 20 | return { 21 | Version: this.Version, 22 | References: this.References.map((r) => r.toJson()), 23 | Schemas: this.Schemas.map((s) => s.toJson()), 24 | }; 25 | } 26 | 27 | static fromJson(json: any): ODataMetadata { 28 | return new ODataMetadata(json.Version, json.References, json.Schemas); 29 | } 30 | 31 | functions() { 32 | return this.Schemas.reduce((acc, s) => { 33 | return [...acc, ...(s.Function ?? [])]; 34 | }, [] as CsdlFunction[]); 35 | } 36 | 37 | actions() { 38 | return this.Schemas.reduce((acc, s) => { 39 | return [...acc, ...(s.Action ?? [])]; 40 | }, [] as CsdlAction[]); 41 | } 42 | 43 | enumTypes(): CsdlEnumType[] { 44 | return this.Schemas.reduce((acc, s) => { 45 | return [...acc, ...(s.EnumType ?? [])]; 46 | }, [] as CsdlEnumType[]); 47 | } 48 | 49 | entityTypes(): CsdlEntityType[] { 50 | return this.Schemas.reduce((acc, s) => { 51 | return [...acc, ...(s.EntityType ?? [])]; 52 | }, [] as CsdlEntityType[]); 53 | } 54 | 55 | complexTypes(): CsdlComplexType[] { 56 | return this.Schemas.reduce((acc, s) => { 57 | return [...acc, ...(s.ComplexType ?? [])]; 58 | }, [] as CsdlComplexType[]); 59 | } 60 | 61 | entitySets(): CsdlEntitySet[] { 62 | return this.Schemas.reduce((acc, s) => { 63 | return [...acc, ...(s.EntityContainer ?? [])]; 64 | }, [] as CsdlEntityContainer[]).reduce((acc, ec) => { 65 | return [...acc, ...(ec.EntitySet ?? [])]; 66 | }, [] as CsdlEntitySet[]); 67 | } 68 | 69 | findEnumType(fullName: string): CsdlEnumType | undefined { 70 | return this.enumTypes().find((et) => et.fullName() === fullName); 71 | } 72 | 73 | findEntityType(fullName: string): CsdlEntityType | undefined { 74 | return this.entityTypes()?.find((et) => et.fullName() === fullName); 75 | } 76 | 77 | findComplexType(fullName: string): CsdlComplexType | undefined { 78 | return this.complexTypes()?.find((ct) => ct.fullName() === fullName); 79 | } 80 | 81 | findEntitySet(fullName: string): CsdlEntitySet | undefined { 82 | return this.entitySets()?.find((ct) => ct.EntityType === fullName); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /projects/angular-odata/src/lib/metadata/csdl/csdl-reference.ts: -------------------------------------------------------------------------------- 1 | import type { 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((i) => new CsdlIncludeAnnotations(i)); 23 | } 24 | 25 | override toJson() { 26 | const json: { [key: string]: any } = { ...super.toJson(), Uri: this.Uri }; 27 | if (Array.isArray(this.Include) && this.Include.length > 0) { 28 | json['Include'] = this.Include.map((i) => i.toJson()); 29 | } 30 | if (Array.isArray(this.IncludeAnnotations) && this.IncludeAnnotations.length > 0) { 31 | json['IncludeAnnotations'] = this.IncludeAnnotations.map((i) => i.toJson()); 32 | } 33 | return json; 34 | } 35 | 36 | override toConfig(base?: Partial) { 37 | return { 38 | ...super.toConfig(), 39 | uri: this.Uri, 40 | includes: this.Include?.map((i) => i.toConfig()), 41 | includeAnnotations: this.IncludeAnnotations?.map((i) => i.toConfig()), 42 | } as ODataReferenceConfig; 43 | } 44 | } 45 | 46 | export class CsdlInclude { 47 | Namespace: string; 48 | Alias?: string; 49 | constructor({ Namespace, Alias }: { Namespace: string; Alias?: string }) { 50 | this.Namespace = Namespace; 51 | this.Alias = Alias; 52 | } 53 | 54 | toJson() { 55 | return { 56 | Namespace: this.Namespace, 57 | Alias: this.Alias, 58 | }; 59 | } 60 | 61 | toConfig() { 62 | return { 63 | namespace: this.Namespace, 64 | alias: this.Alias, 65 | }; 66 | } 67 | } 68 | 69 | export class CsdlIncludeAnnotations { 70 | TermNamespace: string; 71 | Qualifier?: string; 72 | TargetNamespace?: string; 73 | constructor({ 74 | TermNamespace, 75 | Qualifier, 76 | TargetNamespace, 77 | }: { 78 | TermNamespace: string; 79 | Qualifier?: string; 80 | TargetNamespace?: string; 81 | }) { 82 | this.TermNamespace = TermNamespace; 83 | this.Qualifier = Qualifier; 84 | this.TargetNamespace = TargetNamespace; 85 | } 86 | 87 | toJson() { 88 | return { 89 | TermNamespace: this.TermNamespace, 90 | Qualifier: this.Qualifier, 91 | TargetNamespace: this.TargetNamespace, 92 | }; 93 | } 94 | 95 | toConfig() { 96 | return { 97 | termNamespace: this.TermNamespace, 98 | qualifier: this.Qualifier, 99 | targetNamespace: this.TargetNamespace, 100 | }; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /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.map((name, index) => `${children[index]} as ${name}`).join(','); 82 | } 83 | 84 | clone() { 85 | return new ComputeExpression({ 86 | children: this._children.map((c) => c.clone()), 87 | names: [...this.names], 88 | }); 89 | } 90 | 91 | private _add(name: string, node: Renderable): ComputeExpression { 92 | this.names.push(name); 93 | this._children.push(node); 94 | return this; 95 | } 96 | 97 | field( 98 | name: string, 99 | opts: (e: { o: ODataOperators; f: ODataFunctions }) => Renderable, 100 | ): ComputeExpression { 101 | const node = opts({ 102 | o: operators as ODataOperators, 103 | f: functions as ODataFunctions, 104 | }); 105 | return this._add(name, node); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /docs/api/js/libs/tablesort.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * tablesort v5.6.0 (2025-04-20) 3 | * http://tristen.ca/tablesort/demo/ 4 | * Copyright (c) 2025 ; Licensed MIT 5 | */ 6 | (()=>{function r(t,e){if(!(this instanceof r))return new r(t,e);if(!t||"TABLE"!==t.tagName)throw new Error("Element must be a table");this.init(t,e||{})}function v(t){var e;return window.CustomEvent&&"function"==typeof window.CustomEvent?e=new CustomEvent(t):(e=document.createEvent("CustomEvent")).initCustomEvent(t,!1,!1,void 0),e}function p(t,e){return t.getAttribute(e.sortAttribute||"data-sort")||t.textContent||t.innerText||""}function A(t,e){return(t=t.trim().toLowerCase())===(e=e.trim().toLowerCase())?0:t { 37 | const workspace = await getWorkspace(tree); 38 | if (!options.project) { 39 | options.project = workspace.projects.keys().next().value ?? ''; 40 | } 41 | const project = workspace.projects.get(options.project); 42 | if (!project) { 43 | throw new SchematicsException(`Invalid project name: ${options.project}`); 44 | } 45 | 46 | if (options.path === undefined) { 47 | options.path = await createDefaultPath(tree, options.project as string); 48 | } 49 | 50 | const parsedPath = parseName(options.path, options.name); 51 | options.name = parsedPath.name; 52 | options.path = parsedPath.path; 53 | 54 | const modulePath = options.path + '/' + strings.dasherize(options.name); 55 | 56 | return fetch(options.metadata) 57 | .then((resp) => resp.text()) 58 | .then((data) => new ODataMetadataParser(data).metadata()) 59 | .then((meta) => { 60 | options.creation = new Date(); 61 | options.serviceRootUrl = options.metadata.substring(0, options.metadata.length - 9); 62 | options.version = meta.Version; 63 | const pkg = new Package(options, meta); 64 | pkg.resolveImports(); 65 | return chain( 66 | pkg 67 | .sources() 68 | .map((s) => 69 | apply(s.template(), [ 70 | template({ 71 | ...{ 72 | name: s.name(), 73 | fileName: s.fileName(), 74 | fullName: s.fullName(), 75 | imports: s.imports(), 76 | }, 77 | ...s.variables(), 78 | ...strings, 79 | ...utils, 80 | }), 81 | move(normalize(`${modulePath}/${s.directory()}`)), 82 | ]), 83 | ) 84 | .reduce((rules, s) => [...rules, mergeWith(s, MergeStrategy.Overwrite)], [] as Rule[]), 85 | ); 86 | }); 87 | }; 88 | } 89 | -------------------------------------------------------------------------------- /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((e) => new CsdlEntityContainer(this, e)); 53 | this.TypeDefinition = TypeDefinition?.map((t) => new CsdlTypeDefinition(this, t)); 54 | this.Term = Term?.map((t) => new CsdlTerm(this, t)); 55 | this.Annotations = Annotations?.map((a) => new CsdlAnnotations(this, a)); 56 | } 57 | 58 | toJson() { 59 | const json: { [key: string]: any } = { 60 | Namespace: this.Namespace, 61 | }; 62 | if (this.Alias !== undefined) { 63 | json['Alias'] = this.Alias; 64 | } 65 | if (Array.isArray(this.EntityContainer) && this.EntityContainer.length > 0) { 66 | json['EntityContainer'] = this.EntityContainer.map((a) => a.toJson()); 67 | } 68 | if (Array.isArray(this.EntityType) && this.EntityType.length > 0) { 69 | json['EntityType'] = this.EntityType.map((a) => a.toJson()); 70 | } 71 | if (Array.isArray(this.ComplexType) && this.ComplexType.length > 0) { 72 | json['ComplexType'] = this.ComplexType.map((a) => a.toJson()); 73 | } 74 | if (Array.isArray(this.EnumType) && this.EnumType.length > 0) { 75 | json['EnumType'] = this.EnumType.map((a) => a.toJson()); 76 | } 77 | if (Array.isArray(this.TypeDefinition) && this.TypeDefinition.length > 0) { 78 | json['TypeDefinition'] = this.TypeDefinition.map((a) => a.toJson()); 79 | } 80 | if (Array.isArray(this.Term) && this.Term.length > 0) { 81 | json['Term'] = this.Term.map((a) => a.toJson()); 82 | } 83 | if (Array.isArray(this.Annotations) && this.Annotations.length > 0) { 84 | json['Annotations'] = this.Annotations.map((a) => a.toJson()); 85 | } 86 | if (Array.isArray(this.Action) && this.Action.length > 0) { 87 | json['Action'] = this.Action.map((a) => a.toJson()); 88 | } 89 | if (Array.isArray(this.Function) && this.Function.length > 0) { 90 | json['Function'] = this.Function.map((a) => a.toJson()); 91 | } 92 | return json; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /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 = [CACHE_CONTROL, CACHE_CONTROL.toLowerCase()]; 22 | export const ODATA_VERSION = 'OData-Version'; 23 | export const ODATA_VERSION_HEADERS = [ 24 | ODATA_VERSION, 25 | ODATA_VERSION.toLowerCase(), 26 | 'dataserviceversion', 27 | ]; 28 | export const LOCATION_HEADER = 'Location'; 29 | export const LOCATION_HEADERS = [LOCATION_HEADER, LOCATION_HEADER.toLowerCase()]; 30 | export const ODATA_ENTITYID = 'OData-EntityId'; 31 | export const ODATA_ENTITYID_HEADERS = [ODATA_ENTITYID, ODATA_ENTITYID.toLowerCase()]; 32 | export const PREFERENCE_APPLIED = 'Preference-Applied'; 33 | export const PREFERENCE_APPLIED_HEADERS = [PREFERENCE_APPLIED, PREFERENCE_APPLIED.toLowerCase()]; 34 | export const ETAG_HEADER = 'ETag'; 35 | export const ETAG_HEADERS = [ETAG_HEADER, ETAG_HEADER.toLowerCase()]; 36 | 37 | export const RETRY_AFTER = 'Retry-After'; 38 | export const RETRY_AFTER_HEADERS = [RETRY_AFTER, RETRY_AFTER.toLowerCase()]; 39 | 40 | // HTTP HEADER VALUES 41 | export const APPLICATION_JSON = 'application/json'; 42 | export const APPLICATION_HTTP = 'application/http'; 43 | export const APPLICATION_XHTML = 'application/xhtml+xml'; 44 | export const APPLICATION_XML = 'application/xml'; 45 | export const TEXT_PLAIN = 'text/plain'; 46 | export const CONTENT_TYPE_ANY = '*/*'; 47 | export const MULTIPART_MIXED = 'multipart/mixed'; 48 | export const MULTIPART_MIXED_BOUNDARY = 'multipart/mixed;boundary='; 49 | export const CONTENT_TRANSFER_ENCODING = 'Content-Transfer-Encoding'; 50 | export const CONTENT_ID = 'Content-ID'; 51 | export const MAX_AGE = 'max-age'; 52 | 53 | // VERSIONS 54 | export const VERSION_4_0 = '4.0'; 55 | export const VERSION_3_0 = '3.0'; 56 | export const VERSION_2_0 = '2.0'; 57 | export const DEFAULT_VERSION = VERSION_4_0; 58 | 59 | export const BINARY = 'binary'; 60 | export const BOUNDARY_PREFIX_SUFFIX = '--'; 61 | export const BATCH_PREFIX = 'batch_'; 62 | export const CHANGESET_PREFIX = 'changeset_'; 63 | export const DEFAULT_METADATA = 'minimal'; 64 | export const DEFAULT_STRIP_METADATA = 'full'; 65 | export const DEFAULT_FETCH_POLICY = 'network-only'; 66 | export const DEFAULT_TIMEOUT = 60; // Time in seconds 67 | export const CALLABLE_BINDING_PARAMETER = 'bindingParameter'; 68 | export const XSSI_PREFIX = /^\)\]\}',?\n/; 69 | 70 | // URL PARTS 71 | export const QUERY_SEPARATOR = '?'; 72 | export const PARAM_SEPARATOR = '&'; 73 | export const VALUE_SEPARATOR = '='; 74 | export const PATH_SEPARATOR = '/'; 75 | export const ODATA_PARAM_PREFIX = '$'; 76 | export const ODATA_ALIAS_PREFIX = '@'; 77 | 78 | export const NEWLINE = '\r\n'; 79 | export const NEWLINE_REGEXP = /\r?\n/; 80 | export const CACHE_KEY_SEPARATOR = ':'; 81 | 82 | // Models 83 | export const CID_FIELD_NAME = '_cid'; 84 | export const EVENT_SPLITTER = /\s+/; 85 | 86 | // Standard vocabularies for annotating OData services 87 | // https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Core.V1.md 88 | 89 | export const COMPUTED = /.*Computed$/; 90 | export const OPTIMISTIC_CONCURRENCY = /.*OptimisticConcurrency$/; 91 | export const DESCRIPTION = /.*Description$/; 92 | export const LONG_DESCRIPTION = /.*LongDescription$/; 93 | export const OPTIONARL_PARAMETER = /.*OptionalParameter$/; 94 | --------------------------------------------------------------------------------