Skip to content

Commit 73b82e3

Browse files
author
Diego van Haaster
committed
Refactor: Add async initialization with metadata population and reference support
1 parent 10b614b commit 73b82e3

6 files changed

Lines changed: 88 additions & 26 deletions

File tree

projects/angular-odata/src/lib/api.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { HttpEvent, HttpEventType } from '@angular/common/http';
2-
import { NEVER, Observable, of, throwError } from 'rxjs';
2+
import { firstValueFrom, NEVER, Observable, of, throwError } from 'rxjs';
33
import { catchError, map, startWith, tap } from 'rxjs/operators';
44
import { DEFAULT_VERSION } from './constants';
55
import {
@@ -54,6 +54,7 @@ import {
5454
} from './types';
5555
import type { ODataMetadata } from './metadata/metadata';
5656
import { ODataEntityAnnotations } from './annotations';
57+
import { ODataReference } from './schema/reference';
5758

5859
const RESERVED_FIELD_NAMES = Object.getOwnPropertyNames(ODataModel.prototype);
5960

@@ -76,8 +77,12 @@ export class ODataApi {
7677
errorHandler?: (error: any, caught: Observable<any>) => Observable<never>;
7778
// Base Parsers
7879
parsers: Map<string, Parser<any>>;
80+
// Populate from Metadata
81+
populateFromMetadata: boolean;
7982
// Schemas
8083
schemas: ODataSchema[];
84+
// References
85+
references: ODataReference[];
8186
// Models
8287
models: { [type: string]: typeof ODataModel<any> } = {};
8388
// Collections
@@ -104,19 +109,17 @@ export class ODataApi {
104109
this.errorHandler = config.errorHandler;
105110
this.parsers = new Map(Object.entries(config.parsers ?? EDM_PARSERS));
106111

112+
this.populateFromMetadata = config.populateFromMetadata ?? false;
107113
this.schemas = (config.schemas ?? []).map((schema) => new ODataSchema(schema, this));
114+
this.references = (config.references ?? []).map((reference) => new ODataReference(reference, this));
108115
this.models = (config.models ?? {}) as { [type: string]: typeof ODataModel<any> };
109116
this.collections = (config.collections ?? {}) as {
110117
[type: string]: typeof ODataCollection<any, ODataModel<any>>;
111118
};
112119
}
113120

114-
configure(
115-
settings: {
116-
requester?: (request: ODataRequest<any>) => Observable<any>;
117-
} = {},
118-
) {
119-
this.requester = settings.requester;
121+
initialize(requester: (request: ODataRequest<any>) => Observable<any>) {
122+
this.requester = requester;
120123
this.schemas.forEach((schema) => {
121124
schema.configure({
122125
options: this.options.parserOptions,
@@ -132,6 +135,9 @@ export class ODataApi {
132135
}
133136
}
134137
});
138+
return (this.populateFromMetadata) ?
139+
firstValueFrom(this.metadata().fetch().pipe(map(metadata => this.populate(metadata)))) :
140+
Promise.resolve(true);
135141
}
136142

137143
populate(metadata: ODataMetadata) {
@@ -144,6 +150,7 @@ export class ODataApi {
144150
options: this.options.parserOptions,
145151
});
146152
});
153+
return true;
147154
}
148155

149156
fromJson<P, R>(json: {

projects/angular-odata/src/lib/client.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ export class ODataClient {
4747
constructor(private loader: ODataLoader) {}
4848

4949
initialize() {
50-
return this.loader.load().then(({configs, requester}) => {
50+
return this.loader.load()
51+
.then(({configs, requester}) => {
5152
this.settings = new ODataSettings(configs);
52-
this.settings.configure({requester});
53-
return true;
54-
});
53+
return this.settings.initialize(requester);
54+
})
55+
.then(results => results.every(v => v));
5556
}
5657

5758
//#region Resolve Building Blocks

projects/angular-odata/src/lib/module.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ export function provideODataClient(passedConfig: PassedInitialConfig): Environme
4646
// Make the ODATA_CONFIG available through injection
4747
{ provide: ODATA_CONFIG, useValue: passedConfig.config ?? [] },
4848
// Register the startup task
49-
provideAppInitializer(() => {
50-
return inject(ODataClient).initialize();
51-
}),
49+
provideAppInitializer(() => inject(ODataClient).initialize()),
5250
ODataClient,
5351
ODataServiceFactory,
5452
];
@@ -76,9 +74,7 @@ export class ODataModule {
7674
// Make the ODATA_CONFIG available through injection
7775
{ provide: ODATA_CONFIG, useValue: passedConfig.config ?? []},
7876
// Register the startup task
79-
provideAppInitializer(() => {
80-
return inject(ODataClient).initialize();
81-
}),
77+
provideAppInitializer(() => inject(ODataClient).initialize()),
8278
ODataClient,
8379
ODataServiceFactory,
8480
];
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { ODataApi } from '../api';
2+
import { ParserOptions, ODataReferenceConfig } from '../types';
3+
import { ODataAnnotatable } from './annotation';
4+
5+
export class ODataInclude {
6+
namespace: string;
7+
alias?: string;
8+
reference: ODataReference;
9+
constructor(config: {namespace: string, alias?: string}, reference: ODataReference) {
10+
this.namespace = config.namespace;
11+
this.alias = config.alias;
12+
this.reference = reference;
13+
}
14+
15+
configure({ options }: { options: ParserOptions }) {
16+
}
17+
}
18+
19+
export class ODataIncludeAnnotation {
20+
termNamespace: string;
21+
qualifier?: string;
22+
targetNamespace?: string;
23+
reference: ODataReference;
24+
constructor(config: {termNamespace: string, qualifier?: string, targetNamespace?: string}, reference: ODataReference) {
25+
this.termNamespace = config.termNamespace;
26+
this.qualifier = config.qualifier;
27+
this.targetNamespace = config.targetNamespace;
28+
this.reference = reference;
29+
}
30+
31+
configure({ options }: { options: ParserOptions }) {
32+
}
33+
}
34+
35+
export class ODataReference extends ODataAnnotatable {
36+
api: ODataApi;
37+
uri: string;
38+
includes: ODataInclude[]
39+
includeAnnotations: ODataIncludeAnnotation[]
40+
41+
constructor(config: ODataReferenceConfig, api: ODataApi) {
42+
super(config);
43+
this.api = api;
44+
this.uri = config.uri;
45+
this.includes = (config.includes ?? []).map((config) => new ODataInclude(config, this));
46+
this.includeAnnotations = (config.includeAnnotations ?? []).map((config) => new ODataIncludeAnnotation(config, this));
47+
}
48+
49+
configure({ options }: { options: ParserOptions }) {
50+
// Configure Includes
51+
this.includes.forEach((include) => include.configure({ options }));
52+
// Configure IncludeAnnotations
53+
this.includeAnnotations.forEach((includeAnnotation) => includeAnnotation.configure({ options }));
54+
}
55+
}

projects/angular-odata/src/lib/settings.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ export class ODataSettings {
1919
if (this.apis.every((c) => !c.default)) this.apis[0].default = true;
2020
}
2121

22-
configure(settings: { requester?: (request: ODataRequest<any>) => Observable<any> }) {
23-
this.apis.forEach((api) => api.configure(settings));
22+
initialize(requester: (request: ODataRequest<any>) => Observable<any>) {
23+
return Promise.all(
24+
this.apis.map((api) => api.initialize(requester))
25+
);
2426
}
2527

2628
public defaultApi() {

projects/angular-odata/src/lib/types.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { JSONSchema7 } from 'json-schema';
22
import type { Observable } from 'rxjs';
3-
import type { ODataModel, ODataModelOptions } from './models';
4-
import type { EntityKey, ODataRequest, ODataResponse } from './resources';
3+
import type { ODataRequest, ODataResponse } from './resources';
54

65
export type ODataVersion = '2.0' | '3.0' | '4.0';
76
export type FetchPolicy =
@@ -242,6 +241,7 @@ export type ODataApiConfig = {
242241
errorHandler?: (error: any, caught: Observable<any>) => Observable<never>;
243242
options?: ODataApiConfigOptions;
244243
parsers?: { [type: string]: Parser<any> };
244+
populateFromMetadata?: boolean;
245245
schemas?: ODataSchemaConfig[];
246246
references?: ODataReferenceConfig[];
247247
models?: { [type: string]: { new (...params: any[]): any } };
@@ -257,12 +257,13 @@ export type ODataAnnotationConfig = {
257257
};
258258
export type ODataReferenceConfig = {
259259
uri: string;
260-
includes?: string;
261260
annotations?: ODataAnnotationConfig[];
262-
enums?: ODataEnumTypeConfig[];
263-
entities?: ODataStructuredTypeConfig[];
264-
callables?: ODataCallableConfig[];
265-
containers?: ODataEntityContainerConfig[];
261+
includes?: { namespace: string; alias?: string; }[];
262+
includeAnnotations?: {
263+
termNamespace: string;
264+
qualifier?: string;
265+
targetNamespace?: string;
266+
}[]
266267
};
267268
export type ODataSchemaConfig = {
268269
namespace: string;

0 commit comments

Comments
 (0)