breeze-client 2.2.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +83 -63
- package/adapters/abstract-data-service-adapter.d.ts +120 -0
- package/adapters/abstract-data-service-adapter.js +395 -0
- package/adapters/abstract-data-service-adapter.js.map +1 -0
- package/adapters/adapter-ajax-fetch.d.ts +12 -0
- package/adapters/adapter-ajax-fetch.js +13 -0
- package/adapters/adapter-ajax-fetch.js.map +1 -0
- package/{adapter-ajax-post → adapters}/adapter-ajax-post.d.ts +40 -36
- package/{fesm2022/breeze-client-adapter-ajax-post.mjs → adapters/adapter-ajax-post.js} +80 -82
- package/adapters/adapter-ajax-post.js.map +1 -0
- package/{adapter-ajax-fetch → adapters}/adapter-core.d.ts +4 -4
- package/{fesm2022/breeze-client-adapter-uri-builder-json.mjs → adapters/adapter-core.js} +48 -91
- package/adapters/adapter-core.js.map +1 -0
- package/adapters/adapter-data-service-webapi.d.ts +9 -0
- package/{fesm2022/breeze-client-adapter-data-service-webapi.mjs → adapters/adapter-data-service-webapi.js} +105 -107
- package/adapters/adapter-data-service-webapi.js.map +1 -0
- package/adapters/adapter-model-library-backing-store.d.ts +32 -0
- package/adapters/adapter-model-library-backing-store.js +321 -0
- package/adapters/adapter-model-library-backing-store.js.map +1 -0
- package/adapters/adapter-uri-builder-json.d.ts +11 -0
- package/adapters/adapter-uri-builder-json.js +40 -0
- package/adapters/adapter-uri-builder-json.js.map +1 -0
- package/adapters/http.d.ts +52 -0
- package/adapters/http.js +202 -0
- package/adapters/http.js.map +1 -0
- package/angular/adapter-angular-httpclient.d.ts +21 -0
- package/angular/adapter-angular-httpclient.js +70 -0
- package/angular/adapter-angular-httpclient.js.map +1 -0
- package/breeze.d.ts +215 -0
- package/breeze.js +141 -0
- package/breeze.js.map +1 -0
- package/config/config.d.ts +212 -0
- package/config/config.js +392 -0
- package/config/config.js.map +1 -0
- package/config/configure.d.ts +66 -0
- package/config/configure.js +46 -0
- package/config/configure.js.map +1 -0
- package/config/default-adapters.d.ts +1 -0
- package/config/default-adapters.js +40 -0
- package/config/default-adapters.js.map +1 -0
- package/config/interface-registry.d.ts +191 -0
- package/config/interface-registry.js +5 -0
- package/config/interface-registry.js.map +1 -0
- package/{src → core}/assert-param.d.ts +52 -60
- package/core/assert-param.js +390 -0
- package/core/assert-param.js.map +1 -0
- package/core/core.d.ts +285 -0
- package/core/core.js +772 -0
- package/core/core.js.map +1 -0
- package/core/enum.d.ts +116 -0
- package/core/enum.js +167 -0
- package/core/enum.js.map +1 -0
- package/core/event.d.ts +169 -0
- package/core/event.js +338 -0
- package/core/event.js.map +1 -0
- package/entity/array.d.ts +3 -0
- package/entity/array.js +4 -0
- package/entity/array.js.map +1 -0
- package/entity/complex-array.d.ts +17 -0
- package/entity/complex-array.js +104 -0
- package/entity/complex-array.js.map +1 -0
- package/entity/default-property-interceptor.d.ts +1 -0
- package/entity/default-property-interceptor.js +496 -0
- package/entity/default-property-interceptor.js.map +1 -0
- package/{src → entity}/entity-action.d.ts +36 -42
- package/entity/entity-action.js +45 -0
- package/entity/entity-action.js.map +1 -0
- package/entity/entity-aspect.d.ts +457 -0
- package/entity/entity-aspect.js +1262 -0
- package/entity/entity-aspect.js.map +1 -0
- package/entity/entity-base.d.ts +55 -0
- package/entity/entity-base.js +41 -0
- package/entity/entity-base.js.map +1 -0
- package/entity/entity-group.d.ts +1 -0
- package/entity/entity-group.js +224 -0
- package/entity/entity-group.js.map +1 -0
- package/entity/entity-key.d.ts +95 -0
- package/entity/entity-key.js +189 -0
- package/entity/entity-key.js.map +1 -0
- package/{src → entity}/entity-state.d.ts +112 -84
- package/entity/entity-state.js +135 -0
- package/entity/entity-state.js.map +1 -0
- package/entity/key-generator.d.ts +57 -0
- package/entity/key-generator.js +155 -0
- package/entity/key-generator.js.map +1 -0
- package/entity/observable-array.d.ts +25 -0
- package/entity/observable-array.js +230 -0
- package/entity/observable-array.js.map +1 -0
- package/entity/primitive-array.d.ts +12 -0
- package/entity/primitive-array.js +71 -0
- package/entity/primitive-array.js.map +1 -0
- package/entity/relation-array.d.ts +24 -0
- package/entity/relation-array.js +176 -0
- package/entity/relation-array.js.map +1 -0
- package/entity/unattached-children-map.d.ts +1 -0
- package/entity/unattached-children-map.js +105 -0
- package/entity/unattached-children-map.js.map +1 -0
- package/generate-entity-classes.js +1176 -0
- package/manager/entity-manager.d.ts +801 -0
- package/manager/entity-manager.js +2395 -0
- package/manager/entity-manager.js.map +1 -0
- package/{src → manager}/save-options.d.ts +48 -42
- package/manager/save-options.js +51 -0
- package/manager/save-options.js.map +1 -0
- package/{src → metadata}/data-service.d.ts +229 -184
- package/metadata/data-service.js +246 -0
- package/metadata/data-service.js.map +1 -0
- package/metadata/data-type.d.ts +122 -0
- package/metadata/data-type.js +465 -0
- package/metadata/data-type.js.map +1 -0
- package/metadata/entity-metadata.d.ts +1193 -0
- package/metadata/entity-metadata.js +2502 -0
- package/metadata/entity-metadata.js.map +1 -0
- package/{src → metadata}/local-query-comparison-options.d.ts +65 -59
- package/metadata/local-query-comparison-options.js +69 -0
- package/metadata/local-query-comparison-options.js.map +1 -0
- package/{src → metadata}/naming-convention.d.ts +75 -72
- package/metadata/naming-convention.js +97 -0
- package/metadata/naming-convention.js.map +1 -0
- package/mixins/mixin-get-entity-graph.d.ts +63 -0
- package/{fesm2022/breeze-client-mixin-get-entity-graph.mjs → mixins/mixin-get-entity-graph.js} +331 -253
- package/mixins/mixin-get-entity-graph.js.map +1 -0
- package/mixins/mixin-save-queuing.d.ts +98 -0
- package/{fesm2022/breeze-client-mixin-save-queuing.mjs → mixins/mixin-save-queuing.js} +493 -365
- package/mixins/mixin-save-queuing.js.map +1 -0
- package/package.json +63 -94
- package/query/entity-query.d.ts +521 -0
- package/query/entity-query.js +1149 -0
- package/query/entity-query.js.map +1 -0
- package/{src → query}/mapping-context.d.ts +44 -46
- package/query/mapping-context.js +412 -0
- package/query/mapping-context.js.map +1 -0
- package/query/predicate.d.ts +394 -0
- package/query/predicate.js +1294 -0
- package/query/predicate.js.map +1 -0
- package/query/property-path.d.ts +252 -0
- package/query/property-path.js +2 -0
- package/query/property-path.js.map +1 -0
- package/{src → query}/query-options.d.ts +143 -119
- package/query/query-options.js +180 -0
- package/query/query-options.js.map +1 -0
- package/rxjs/breeze-rxjs.d.ts +72 -0
- package/rxjs/breeze-rxjs.js +117 -0
- package/rxjs/breeze-rxjs.js.map +1 -0
- package/validation/validate.d.ts +583 -0
- package/validation/validate.js +961 -0
- package/validation/validate.js.map +1 -0
- package/{src → validation}/validation-options.d.ts +67 -63
- package/validation/validation-options.js +70 -0
- package/validation/validation-options.js.map +1 -0
- package/adapter-ajax-angularjs/adapter-ajax-angularjs.d.ts +0 -17
- package/adapter-ajax-angularjs/adapter-core.d.ts +0 -4
- package/adapter-ajax-angularjs/index.d.ts +0 -5
- package/adapter-ajax-fetch/adapter-ajax-fetch.d.ts +0 -18
- package/adapter-ajax-fetch/index.d.ts +0 -5
- package/adapter-ajax-httpclient/adapter-ajax-httpclient.d.ts +0 -13
- package/adapter-ajax-httpclient/adapter-core.d.ts +0 -4
- package/adapter-ajax-httpclient/index.d.ts +0 -5
- package/adapter-ajax-jquery/adapter-ajax-jquery.d.ts +0 -15
- package/adapter-ajax-jquery/index.d.ts +0 -5
- package/adapter-ajax-post/index.d.ts +0 -5
- package/adapter-data-service-odata/adapter-core.d.ts +0 -4
- package/adapter-data-service-odata/adapter-data-service-odata.d.ts +0 -17
- package/adapter-data-service-odata/index.d.ts +0 -5
- package/adapter-data-service-webapi/adapter-data-service-webapi.d.ts +0 -15
- package/adapter-data-service-webapi/index.d.ts +0 -5
- package/adapter-model-library-backing-store/adapter-model-library-backing-store.d.ts +0 -10
- package/adapter-model-library-backing-store/index.d.ts +0 -5
- package/adapter-model-library-ko/adapter-model-library-ko.d.ts +0 -10
- package/adapter-model-library-ko/index.d.ts +0 -5
- package/adapter-uri-builder-json/adapter-core.d.ts +0 -4
- package/adapter-uri-builder-json/adapter-uri-builder-json.d.ts +0 -8
- package/adapter-uri-builder-json/index.d.ts +0 -5
- package/adapter-uri-builder-odata/adapter-core.d.ts +0 -4
- package/adapter-uri-builder-odata/adapter-uri-builder-odata.d.ts +0 -8
- package/adapter-uri-builder-odata/index.d.ts +0 -5
- package/esm2022/adapter-ajax-angularjs/adapter-ajax-angularjs.mjs +0 -135
- package/esm2022/adapter-ajax-angularjs/adapter-core.mjs +0 -49
- package/esm2022/adapter-ajax-angularjs/breeze-client-adapter-ajax-angularjs.mjs +0 -5
- package/esm2022/adapter-ajax-fetch/adapter-ajax-fetch.mjs +0 -135
- package/esm2022/adapter-ajax-fetch/adapter-core.mjs +0 -49
- package/esm2022/adapter-ajax-fetch/breeze-client-adapter-ajax-fetch.mjs +0 -5
- package/esm2022/adapter-ajax-httpclient/adapter-ajax-httpclient.mjs +0 -139
- package/esm2022/adapter-ajax-httpclient/adapter-core.mjs +0 -49
- package/esm2022/adapter-ajax-httpclient/breeze-client-adapter-ajax-httpclient.mjs +0 -5
- package/esm2022/adapter-ajax-jquery/adapter-ajax-jquery.mjs +0 -107
- package/esm2022/adapter-ajax-jquery/breeze-client-adapter-ajax-jquery.mjs +0 -5
- package/esm2022/adapter-ajax-post/adapter-ajax-post.mjs +0 -77
- package/esm2022/adapter-ajax-post/breeze-client-adapter-ajax-post.mjs +0 -5
- package/esm2022/adapter-data-service-odata/adapter-core.mjs +0 -49
- package/esm2022/adapter-data-service-odata/adapter-data-service-odata.mjs +0 -461
- package/esm2022/adapter-data-service-odata/breeze-client-adapter-data-service-odata.mjs +0 -5
- package/esm2022/adapter-data-service-webapi/adapter-data-service-webapi.mjs +0 -100
- package/esm2022/adapter-data-service-webapi/breeze-client-adapter-data-service-webapi.mjs +0 -5
- package/esm2022/adapter-model-library-backing-store/adapter-model-library-backing-store.mjs +0 -269
- package/esm2022/adapter-model-library-backing-store/breeze-client-adapter-model-library-backing-store.mjs +0 -5
- package/esm2022/adapter-model-library-ko/adapter-model-library-ko.mjs +0 -241
- package/esm2022/adapter-model-library-ko/breeze-client-adapter-model-library-ko.mjs +0 -5
- package/esm2022/adapter-uri-builder-json/adapter-core.mjs +0 -49
- package/esm2022/adapter-uri-builder-json/adapter-uri-builder-json.mjs +0 -37
- package/esm2022/adapter-uri-builder-json/breeze-client-adapter-uri-builder-json.mjs +0 -5
- package/esm2022/adapter-uri-builder-odata/adapter-core.mjs +0 -49
- package/esm2022/adapter-uri-builder-odata/adapter-uri-builder-odata.mjs +0 -191
- package/esm2022/adapter-uri-builder-odata/breeze-client-adapter-uri-builder-odata.mjs +0 -5
- package/esm2022/breeze-client.mjs +0 -5
- package/esm2022/index.mjs +0 -2
- package/esm2022/mixin-get-entity-graph/breeze-client-mixin-get-entity-graph.mjs +0 -5
- package/esm2022/mixin-get-entity-graph/mixin-get-entity-graph.mjs +0 -269
- package/esm2022/mixin-save-queuing/breeze-client-mixin-save-queuing.mjs +0 -5
- package/esm2022/mixin-save-queuing/mixin-save-queuing.mjs +0 -421
- package/esm2022/src/abstract-data-service-adapter.mjs +0 -325
- package/esm2022/src/array.mjs +0 -4
- package/esm2022/src/assert-param.mjs +0 -330
- package/esm2022/src/breeze.mjs +0 -85
- package/esm2022/src/complex-array.mjs +0 -117
- package/esm2022/src/config.mjs +0 -232
- package/esm2022/src/core.mjs +0 -712
- package/esm2022/src/csdl-metadata-parser.mjs +0 -385
- package/esm2022/src/data-service.mjs +0 -221
- package/esm2022/src/data-type.mjs +0 -510
- package/esm2022/src/default-property-interceptor.mjs +0 -460
- package/esm2022/src/entity-action.mjs +0 -45
- package/esm2022/src/entity-aspect.mjs +0 -897
- package/esm2022/src/entity-group.mjs +0 -192
- package/esm2022/src/entity-key.mjs +0 -125
- package/esm2022/src/entity-manager.mjs +0 -2163
- package/esm2022/src/entity-metadata.mjs +0 -2242
- package/esm2022/src/entity-query.mjs +0 -1024
- package/esm2022/src/entity-state.mjs +0 -107
- package/esm2022/src/enum.mjs +0 -143
- package/esm2022/src/event.mjs +0 -264
- package/esm2022/src/interface-registry.mjs +0 -39
- package/esm2022/src/key-generator.mjs +0 -110
- package/esm2022/src/local-query-comparison-options.mjs +0 -65
- package/esm2022/src/mapping-context.mjs +0 -411
- package/esm2022/src/naming-convention.mjs +0 -92
- package/esm2022/src/observable-array.mjs +0 -183
- package/esm2022/src/predicate.mjs +0 -1259
- package/esm2022/src/primitive-array.mjs +0 -85
- package/esm2022/src/query-options.mjs +0 -152
- package/esm2022/src/relation-array.mjs +0 -173
- package/esm2022/src/save-options.mjs +0 -41
- package/esm2022/src/unattached-children-map.mjs +0 -56
- package/esm2022/src/validate.mjs +0 -995
- package/esm2022/src/validation-options.mjs +0 -64
- package/fesm2022/breeze-client-adapter-ajax-angularjs.mjs +0 -190
- package/fesm2022/breeze-client-adapter-ajax-angularjs.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-ajax-fetch.mjs +0 -190
- package/fesm2022/breeze-client-adapter-ajax-fetch.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-ajax-httpclient.mjs +0 -194
- package/fesm2022/breeze-client-adapter-ajax-httpclient.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-ajax-jquery.mjs +0 -114
- package/fesm2022/breeze-client-adapter-ajax-jquery.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-ajax-post.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-data-service-odata.mjs +0 -516
- package/fesm2022/breeze-client-adapter-data-service-odata.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-data-service-webapi.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-model-library-backing-store.mjs +0 -276
- package/fesm2022/breeze-client-adapter-model-library-backing-store.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-model-library-ko.mjs +0 -248
- package/fesm2022/breeze-client-adapter-model-library-ko.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-uri-builder-json.mjs.map +0 -1
- package/fesm2022/breeze-client-adapter-uri-builder-odata.mjs +0 -246
- package/fesm2022/breeze-client-adapter-uri-builder-odata.mjs.map +0 -1
- package/fesm2022/breeze-client-mixin-get-entity-graph.mjs.map +0 -1
- package/fesm2022/breeze-client-mixin-save-queuing.mjs.map +0 -1
- package/fesm2022/breeze-client.mjs +0 -14144
- package/fesm2022/breeze-client.mjs.map +0 -1
- package/index.d.ts +0 -1
- package/mixin-get-entity-graph/index.d.ts +0 -5
- package/mixin-get-entity-graph/mixin-get-entity-graph.d.ts +0 -31
- package/mixin-save-queuing/index.d.ts +0 -5
- package/mixin-save-queuing/mixin-save-queuing.d.ts +0 -32
- package/src/abstract-data-service-adapter.d.ts +0 -84
- package/src/array.d.ts +0 -3
- package/src/breeze.d.ts +0 -139
- package/src/complex-array.d.ts +0 -13
- package/src/config.d.ts +0 -104
- package/src/core.d.ts +0 -164
- package/src/csdl-metadata-parser.d.ts +0 -7
- package/src/data-type.d.ts +0 -89
- package/src/default-property-interceptor.d.ts +0 -4
- package/src/entity-aspect.d.ts +0 -332
- package/src/entity-group.d.ts +0 -28
- package/src/entity-key.d.ts +0 -77
- package/src/entity-manager.d.ts +0 -680
- package/src/entity-metadata.d.ts +0 -988
- package/src/entity-query.d.ts +0 -444
- package/src/enum.d.ts +0 -100
- package/src/event.d.ts +0 -142
- package/src/interface-registry.d.ts +0 -150
- package/src/key-generator.d.ts +0 -12
- package/src/observable-array.d.ts +0 -52
- package/src/predicate.d.ts +0 -376
- package/src/primitive-array.d.ts +0 -7
- package/src/relation-array.d.ts +0 -18
- package/src/unattached-children-map.d.ts +0 -19
- package/src/validate.d.ts +0 -650
|
@@ -0,0 +1,2502 @@
|
|
|
1
|
+
import { core, hasOwnProperty as hasOwnProp, stringStartsWith } from '../core/core.js';
|
|
2
|
+
import { config, setDefaultAdapters } from '../config/config.js';
|
|
3
|
+
import { BreezeEvent } from '../core/event.js';
|
|
4
|
+
import { assertParam, assertConfig } from '../core/assert-param.js';
|
|
5
|
+
import { DataType } from './data-type.js';
|
|
6
|
+
import { EntityAspect, ComplexAspect } from '../entity/entity-aspect.js';
|
|
7
|
+
import { EntityKey } from '../entity/entity-key.js';
|
|
8
|
+
import { Validator } from '../validation/validate.js';
|
|
9
|
+
import { BreezeEnum } from '../core/enum.js';
|
|
10
|
+
import { DataService } from './data-service.js';
|
|
11
|
+
import { NamingConvention } from './naming-convention.js';
|
|
12
|
+
import { LocalQueryComparisonOptions } from './local-query-comparison-options.js';
|
|
13
|
+
import { defaultPropertyInterceptor } from '../entity/default-property-interceptor.js';
|
|
14
|
+
import { ModelLibraryBackingStoreAdapter } from '../adapters/adapter-model-library-backing-store.js';
|
|
15
|
+
/**
|
|
16
|
+
An instance of the MetadataStore contains all of the metadata about a collection of {@link EntityType}'s.
|
|
17
|
+
MetadataStores may be shared across {@link EntityManager}'s. If an EntityManager is created without an
|
|
18
|
+
explicit MetadataStore, the MetadataStore from the MetadataStore.defaultInstance property will be used.
|
|
19
|
+
*/
|
|
20
|
+
export class MetadataStore {
|
|
21
|
+
/** @hidden @internal */
|
|
22
|
+
static __id = 0;
|
|
23
|
+
/** @hidden @internal */
|
|
24
|
+
static ANONTYPE_PREFIX = "_IB_";
|
|
25
|
+
/** The version of any MetadataStores created by this class */
|
|
26
|
+
static metadataVersion = '1.0.5';
|
|
27
|
+
/** An optional name for this MetadataStore, set with {@link MetadataStore.setProperties}.
|
|
28
|
+
{@link MetadataStore.exportMetadata} writes it, but {@link MetadataStore.importMetadata} does not
|
|
29
|
+
restore it. When {@link EntityManager.exportEntities} leaves out the metadata it records this name,
|
|
30
|
+
and {@link EntityManager.importEntities} passes it to {@link ImportConfig.metadataVersionFn}. */
|
|
31
|
+
name;
|
|
32
|
+
/** The {@link DataService}s this store has metadata for, one per service name. Entries are added by
|
|
33
|
+
{@link MetadataStore.fetchMetadata}, {@link MetadataStore.importMetadata} and
|
|
34
|
+
{@link MetadataStore.addDataService}. __Read Only__ */
|
|
35
|
+
dataServices;
|
|
36
|
+
/** The {@link NamingConvention} associated with this MetadataStore. __Read Only__ */
|
|
37
|
+
namingConvention;
|
|
38
|
+
/** The {@link LocalQueryComparisonOptions} associated with this MetadataStore. __Read Only__ */
|
|
39
|
+
localQueryComparisonOptions;
|
|
40
|
+
/** A function that customizes how data property values are serialized, both when entities are
|
|
41
|
+
sent to the server in a save and when {@link EntityManager.exportEntities} writes them. It is called
|
|
42
|
+
as `serializerFn(property, value)`; its return value is used in place of `value`, and returning
|
|
43
|
+
`undefined` leaves the property out. Applies to every type in the store that has no
|
|
44
|
+
`serializerFn` of its own (see {@link EntityType.serializerFn}). */
|
|
45
|
+
serializerFn;
|
|
46
|
+
/**
|
|
47
|
+
An {@link BreezeEvent} that fires after a MetadataStore has completed fetching metadata from a remote service.
|
|
48
|
+
|
|
49
|
+
@eventArgs -
|
|
50
|
+
- metadataStore - The MetadataStore into which the metadata was fetched.
|
|
51
|
+
- dataService - The {@link DataService} that metadata was fetched from.
|
|
52
|
+
- rawMetadata - {Object} The raw metadata returned from the service. (It will have already been processed by this point).
|
|
53
|
+
```ts
|
|
54
|
+
const ms = myEntityManager.metadataStore;
|
|
55
|
+
ms.metadataFetched.subscribe(args => {
|
|
56
|
+
const metadataStore = args.metadataStore;
|
|
57
|
+
const dataService = args.dataService;
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
@event
|
|
61
|
+
*/
|
|
62
|
+
metadataFetched;
|
|
63
|
+
/** @hidden @internal */
|
|
64
|
+
_resourceEntityTypeMap;
|
|
65
|
+
/** @hidden @internal */
|
|
66
|
+
_entityTypeResourceMap;
|
|
67
|
+
/** @hidden @internal key is qualified structuraltype name - value is structuralType. ( structural = entityType or complexType). */
|
|
68
|
+
_structuralTypeMap;
|
|
69
|
+
/** @hidden @internal key is shortName, value is qualified name - does not need to be serialized. */
|
|
70
|
+
_shortNameMap;
|
|
71
|
+
/** @hidden @internal key is either short or qual type name - value is ctor; */
|
|
72
|
+
_ctorRegistry;
|
|
73
|
+
/** @hidden @internal key is entityTypeName; value is array of nav props */
|
|
74
|
+
_incompleteTypeMap;
|
|
75
|
+
/** @hidden @internal */
|
|
76
|
+
_incompleteComplexTypeMap;
|
|
77
|
+
/** @hidden @internal { json: any, stype: StructuralType }[] */
|
|
78
|
+
_deferredTypes;
|
|
79
|
+
/** @hidden @internal */
|
|
80
|
+
_id;
|
|
81
|
+
/** @hidden @internal Whether the comparison options were chosen on the client; they then win over imported metadata. */
|
|
82
|
+
_hasClientLqco;
|
|
83
|
+
/**
|
|
84
|
+
Constructs a new MetadataStore.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const ms = new MetadataStore();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The store can then be associated with an EntityManager
|
|
91
|
+
```ts
|
|
92
|
+
const entityManager = new EntityManager({
|
|
93
|
+
serviceName: "breeze/NorthwindIBModel",
|
|
94
|
+
metadataStore: ms
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
or for an existing EntityManager
|
|
99
|
+
```ts
|
|
100
|
+
// Assume em1 is an existing EntityManager
|
|
101
|
+
em1.setProperties({ metadataStore: ms });
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
@param config - Configuration settings .
|
|
105
|
+
- namingConvention - (default=NamingConvention.defaultInstance) NamingConvention to be used in mapping property names
|
|
106
|
+
between client and server. Uses the NamingConvention.defaultInstance if not specified.
|
|
107
|
+
- localQueryComparisonOptions - (default=LocalQueryComparisonOptions.defaultInstance) The LocalQueryComparisonOptions to be
|
|
108
|
+
used when performing "local queries" in order to match the semantics of queries against a remote service. Options passed
|
|
109
|
+
here, or made the default with setAsDefault(), win over any that imported metadata names.
|
|
110
|
+
- serializerFn - A function that is used to mediate the serialization of instances of this type.
|
|
111
|
+
*/
|
|
112
|
+
constructor(config) {
|
|
113
|
+
config = config || {};
|
|
114
|
+
assertConfig(config)
|
|
115
|
+
.whereParam("namingConvention").isOptional().isInstanceOf(NamingConvention).withDefault(NamingConvention.defaultInstance)
|
|
116
|
+
.whereParam("localQueryComparisonOptions").isOptional().isInstanceOf(LocalQueryComparisonOptions).withDefault(LocalQueryComparisonOptions.defaultInstance)
|
|
117
|
+
.whereParam("serializerFn").isOptional().isFunction()
|
|
118
|
+
.whereParam("name").isOptional().isString()
|
|
119
|
+
.applyAll(this);
|
|
120
|
+
this.dataServices = []; // array of dataServices;
|
|
121
|
+
this._resourceEntityTypeMap = {}; // key is resource name - value is qualified entityType name
|
|
122
|
+
this._structuralTypeMap = {}; // key is qualified structuraltype name - value is structuralType. ( structural = entityType or complexType).
|
|
123
|
+
this._shortNameMap = new Map(); // key is shortName, value is qualified name - does not need to be serialized.
|
|
124
|
+
this._ctorRegistry = new Map(); // key is either short or qual type name - value is ctor;
|
|
125
|
+
this._incompleteTypeMap = new Map(); // key is entityTypeName; value is array of nav props
|
|
126
|
+
this._incompleteComplexTypeMap = new Map(); // key is complexTypeName; value is array of complexType props
|
|
127
|
+
this._id = MetadataStore.__id++;
|
|
128
|
+
this.metadataFetched = new BreezeEvent("metadataFetched", this);
|
|
129
|
+
// Comparison options passed here, or made the default with setAsDefault(), are a client-side choice:
|
|
130
|
+
// importMetadata keeps them rather than adopting the ones the metadata names.
|
|
131
|
+
this._hasClientLqco = config.localQueryComparisonOptions != null ||
|
|
132
|
+
LocalQueryComparisonOptions.defaultInstance.name !== LocalQueryComparisonOptions.caseInsensitiveSQL.name;
|
|
133
|
+
}
|
|
134
|
+
/** __Dev Only__ - for use when writing a {@link DataServiceAdapter} or {@link JsonResultsAdapter}.
|
|
135
|
+
Converts a type name as a server sends it, such as the .NET `"Northwind.Models.Customer, Northwind"`,
|
|
136
|
+
into Breeze's qualified form, `"Customer:#Northwind.Models"`. Results are cached; a null or empty
|
|
137
|
+
name is returned unchanged. */
|
|
138
|
+
// needs to be made avail to dataService.xxx files
|
|
139
|
+
static normalizeTypeName = core.memoize(function (rawTypeName) {
|
|
140
|
+
return rawTypeName && MetadataStore.parseTypeName(rawTypeName).typeName;
|
|
141
|
+
});
|
|
142
|
+
// for debugging use the line below instead.
|
|
143
|
+
//ctor.normalizeTypeName = function (rawTypeName) { return parseTypeName(rawTypeName).typeName; };
|
|
144
|
+
/**
|
|
145
|
+
General purpose property set method
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
// assume em1 is an EntityManager containing a number of existing entities.
|
|
149
|
+
em1.metadataStore.setProperties({
|
|
150
|
+
name: "Northwind v6.1.3",
|
|
151
|
+
serializerFn: (prop, value) => prop.isUnmapped ? undefined : value
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
@param config - An object containing the selected properties and values to set.
|
|
155
|
+
*/
|
|
156
|
+
setProperties(config) {
|
|
157
|
+
assertConfig(config)
|
|
158
|
+
.whereParam("name").isString().isOptional()
|
|
159
|
+
.whereParam("serializerFn").isFunction().isOptional()
|
|
160
|
+
.applyAll(this);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
Adds a DataService to this MetadataStore. If a DataService with the same serviceName is already
|
|
164
|
+
in the MetadataStore an exception will be thrown.
|
|
165
|
+
@param dataService - The {@link DataService} to add
|
|
166
|
+
@param shouldOverwrite - (default=false) Permit overwrite of existing DataService rather than throw exception
|
|
167
|
+
*/
|
|
168
|
+
addDataService(dataService, shouldOverwrite) {
|
|
169
|
+
assertParam(dataService, "dataService").isInstanceOf(DataService).check();
|
|
170
|
+
assertParam(shouldOverwrite, "shouldOverwrite").isBoolean().isOptional().check();
|
|
171
|
+
let ix = this._getDataServiceIndex(dataService.serviceName);
|
|
172
|
+
if (ix >= 0) {
|
|
173
|
+
if (!!shouldOverwrite) {
|
|
174
|
+
this.dataServices[ix] = dataService;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
throw new Error("A dataService with this name '" + dataService.serviceName + "' already exists in this MetadataStore");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this.dataServices.push(dataService);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** @hidden @internal */
|
|
185
|
+
_getDataServiceIndex(serviceName) {
|
|
186
|
+
return core.arrayIndexOf(this.dataServices, function (ds) {
|
|
187
|
+
return ds.serviceName === serviceName;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
Adds an EntityType or ComplexType to this MetadataStore. Properties can still be added to the
|
|
192
|
+
type afterwards, until the first entity of that type is attached to an EntityManager - after
|
|
193
|
+
that the type is frozen.
|
|
194
|
+
@param stype - The EntityType or ComplexType to add
|
|
195
|
+
*/
|
|
196
|
+
addEntityType(stype) {
|
|
197
|
+
let structuralType;
|
|
198
|
+
if (stype instanceof EntityType || stype instanceof ComplexType) {
|
|
199
|
+
structuralType = stype;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
structuralType = stype.isComplexType ? new ComplexType(stype) : new EntityType(stype);
|
|
203
|
+
}
|
|
204
|
+
// if (!structuralType.isComplexType) { // same as below but isn't a 'type guard'
|
|
205
|
+
if (structuralType instanceof EntityType) {
|
|
206
|
+
if (structuralType.baseTypeName && !structuralType.baseEntityType) {
|
|
207
|
+
let baseEntityType = this._getStructuralType(structuralType.baseTypeName, true);
|
|
208
|
+
// safe cast because we know that baseEntityType must be an EntityType if the structuralType is an EntityType
|
|
209
|
+
structuralType._updateFromBase(baseEntityType);
|
|
210
|
+
}
|
|
211
|
+
if (structuralType.keyProperties.length === 0 && !structuralType.isAbstract) {
|
|
212
|
+
throw new Error("Unable to add " + structuralType.name +
|
|
213
|
+
" to this MetadataStore. An EntityType must have at least one property designated as a key property - See the 'DataProperty.isPartOfKey' property.");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
structuralType.metadataStore = this;
|
|
217
|
+
// don't register anon types
|
|
218
|
+
if (!structuralType.isAnonymous) {
|
|
219
|
+
if (this._structuralTypeMap[structuralType.name]) {
|
|
220
|
+
throw new Error("Type " + structuralType.name + " already exists in this MetadataStore.");
|
|
221
|
+
}
|
|
222
|
+
this._structuralTypeMap[structuralType.name] = structuralType;
|
|
223
|
+
this._shortNameMap.set(structuralType.shortName, structuralType.name);
|
|
224
|
+
}
|
|
225
|
+
structuralType.getProperties().forEach(p => {
|
|
226
|
+
structuralType._updateNames(p);
|
|
227
|
+
if (!p.isUnmapped) {
|
|
228
|
+
structuralType._mappedPropertiesCount++;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
structuralType._updateCps();
|
|
232
|
+
// 'isEntityType' is a type guard
|
|
233
|
+
if (structuralType instanceof EntityType) {
|
|
234
|
+
structuralType._updateNps();
|
|
235
|
+
// give the type it's base's resource name if it doesn't have its own.
|
|
236
|
+
let defResourceName = structuralType.defaultResourceName || (structuralType.baseEntityType && structuralType.baseEntityType.defaultResourceName);
|
|
237
|
+
if (defResourceName && !this.getEntityTypeNameForResourceName(defResourceName)) {
|
|
238
|
+
this.setEntityTypeForResourceName(defResourceName, structuralType.name);
|
|
239
|
+
}
|
|
240
|
+
structuralType.defaultResourceName = defResourceName;
|
|
241
|
+
// check if this structural type's name, short version or qualified version has a registered ctor.
|
|
242
|
+
structuralType.getCtor();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
Exports this MetadataStore to a serialized string appropriate for local storage. This operation is also called
|
|
247
|
+
internally when exporting an EntityManager.
|
|
248
|
+
```ts
|
|
249
|
+
// assume ms is a previously created MetadataStore
|
|
250
|
+
const metadataAsString = ms.exportMetadata();
|
|
251
|
+
window.localStorage.setItem("metadata", metadataAsString);
|
|
252
|
+
// and later, usually in a different session imported
|
|
253
|
+
const metadataFromStorage = window.localStorage.getItem("metadata");
|
|
254
|
+
const newMetadataStore = new MetadataStore();
|
|
255
|
+
newMetadataStore.importMetadata(metadataFromStorage);
|
|
256
|
+
```
|
|
257
|
+
@returns A serialized version of this MetadataStore that may be stored locally and later restored.
|
|
258
|
+
*/
|
|
259
|
+
exportMetadata() {
|
|
260
|
+
let result = JSON.stringify({
|
|
261
|
+
"metadataVersion": MetadataStore.metadataVersion,
|
|
262
|
+
"name": this.name,
|
|
263
|
+
"namingConvention": this.namingConvention.name,
|
|
264
|
+
"localQueryComparisonOptions": this.localQueryComparisonOptions.name,
|
|
265
|
+
"dataServices": this.dataServices,
|
|
266
|
+
"structuralTypes": core.objectMap(this._structuralTypeMap),
|
|
267
|
+
"resourceEntityTypeMap": this._resourceEntityTypeMap
|
|
268
|
+
}, null, config.stringifyPad);
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
Imports a previously exported serialized MetadataStore into this MetadataStore.
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
// assume ms is a previously created MetadataStore
|
|
276
|
+
const metadataAsString = ms.exportMetadata();
|
|
277
|
+
window.localStorage.setItem("metadata", metadataAsString);
|
|
278
|
+
// and later, usually in a different session
|
|
279
|
+
const metadataFromStorage = window.localStorage.getItem("metadata");
|
|
280
|
+
const newMetadataStore = new MetadataStore();
|
|
281
|
+
newMetadataStore.importMetadata(metadataFromStorage);
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
A type that is already in this store is left as it is unless 'allowMerge' is set, so importing the same metadata
|
|
285
|
+
twice is harmless. A type that is not in the store is created, with or without 'allowMerge', and its metadata must
|
|
286
|
+
include 'dataProperties'. The 'localQueryComparisonOptions' the metadata names are adopted only by an empty store
|
|
287
|
+
whose own options were not chosen on the client (see the constructor).
|
|
288
|
+
@param exportedMetadata - A previously exported MetadataStore.
|
|
289
|
+
@param allowMerge - Allows custom metadata to be merged into existing metadata types.
|
|
290
|
+
@returns This MetadataStore.
|
|
291
|
+
@chainable
|
|
292
|
+
*/
|
|
293
|
+
importMetadata(exportedMetadata, allowMerge = false) {
|
|
294
|
+
assertParam(allowMerge, "allowMerge").isOptional().isBoolean().check();
|
|
295
|
+
this._deferredTypes = new Map();
|
|
296
|
+
// insure that we don't mutate incoming exportedMetadata ( if its an object)
|
|
297
|
+
let metadataAsString = (typeof (exportedMetadata) === "string") ? exportedMetadata : JSON.stringify(exportedMetadata);
|
|
298
|
+
const metadataJson = JSON.parse(metadataAsString);
|
|
299
|
+
// CSDL (the OData / EDMX format) has a schema property. 2.x parsed it; v3 does not, and
|
|
300
|
+
// without this check it imported silently as nothing and failed much later.
|
|
301
|
+
if (metadataJson && metadataJson.schema && !metadataJson.structuralTypes) {
|
|
302
|
+
throw new Error("This looks like CSDL (OData / EDMX) metadata, which breeze-client 3 does not read. " +
|
|
303
|
+
"Supply Breeze native JSON metadata instead - see Migrating from 2.x in the Breeze docs.");
|
|
304
|
+
}
|
|
305
|
+
let json = metadataJson;
|
|
306
|
+
if (json.metadataVersion && json.metadataVersion !== MetadataStore.metadataVersion) {
|
|
307
|
+
let msg = core.formatString("Cannot import metadata with a different 'metadataVersion' (%1) than the current 'MetadataStore.metadataVersion' (%2) ", json.metadataVersion, MetadataStore.metadataVersion);
|
|
308
|
+
throw new Error(msg);
|
|
309
|
+
}
|
|
310
|
+
let ncName = json.namingConvention;
|
|
311
|
+
// Comparison options chosen on the client win; the metadata's apply only to a store without its own.
|
|
312
|
+
let lqcoName = this._hasClientLqco ? undefined : json.localQueryComparisonOptions;
|
|
313
|
+
if (this.isEmpty()) {
|
|
314
|
+
this.namingConvention = config._fetchObject(NamingConvention, ncName) || this.namingConvention;
|
|
315
|
+
this.localQueryComparisonOptions = (lqcoName && config._fetchObject(LocalQueryComparisonOptions, lqcoName)) || this.localQueryComparisonOptions;
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
if (ncName && this.namingConvention.name !== ncName) {
|
|
319
|
+
throw new Error("Cannot import metadata with a different 'namingConvention' from the current MetadataStore");
|
|
320
|
+
}
|
|
321
|
+
if (lqcoName && this.localQueryComparisonOptions.name !== lqcoName) {
|
|
322
|
+
throw new Error("Cannot import metadata with different 'localQueryComparisonOptions' from the current MetadataStore");
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
//noinspection JSHint
|
|
326
|
+
json.dataServices && json.dataServices.forEach((ds) => {
|
|
327
|
+
let realDs = DataService.fromJSON(ds);
|
|
328
|
+
this.addDataService(realDs, true);
|
|
329
|
+
});
|
|
330
|
+
json.structuralTypes && json.structuralTypes.forEach((stype) => {
|
|
331
|
+
structuralTypeFromJson(this, stype, allowMerge);
|
|
332
|
+
});
|
|
333
|
+
core.extend(this._resourceEntityTypeMap, json.resourceEntityTypeMap);
|
|
334
|
+
// the serialized form is a plain object; the in-memory form is a Map
|
|
335
|
+
if (json.incompleteTypeMap) {
|
|
336
|
+
Object.entries(json.incompleteTypeMap).forEach(([k, v]) => this._incompleteTypeMap.set(k, v));
|
|
337
|
+
}
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
Creates a new MetadataStore from a previously exported serialized MetadataStore
|
|
342
|
+
```ts
|
|
343
|
+
// assume ms is a previously created MetadataStore
|
|
344
|
+
const metadataAsString = ms.exportMetadata();
|
|
345
|
+
window.localStorage.setItem("metadata", metadataAsString);
|
|
346
|
+
// and later, usually in a different session
|
|
347
|
+
const metadataFromStorage = window.localStorage.getItem("metadata");
|
|
348
|
+
const newMetadataStore = MetadataStore.importMetadata(metadataFromStorage);
|
|
349
|
+
```
|
|
350
|
+
@param exportedString - A previously exported MetadataStore.
|
|
351
|
+
@returns A new MetadataStore.
|
|
352
|
+
*/
|
|
353
|
+
static importMetadata(exportedString) {
|
|
354
|
+
let ms = new MetadataStore();
|
|
355
|
+
ms.importMetadata(exportedString);
|
|
356
|
+
return ms;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
Returns whether Metadata has been retrieved for a specified service name.
|
|
360
|
+
```ts
|
|
361
|
+
// Assume em1 is an existing EntityManager.
|
|
362
|
+
if (!em1.metadataStore.hasMetadataFor("breeze/NorthwindIBModel")) {
|
|
363
|
+
// do something interesting
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
@param serviceName - The service name.
|
|
367
|
+
@returns Whether metadata has already been retrieved for the specified service name.
|
|
368
|
+
*/
|
|
369
|
+
hasMetadataFor(serviceName) {
|
|
370
|
+
return !!this.getDataService(serviceName);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
Returns the DataService for a specified service name
|
|
374
|
+
```ts
|
|
375
|
+
// Assume em1 is an existing EntityManager.
|
|
376
|
+
const ds = em1.metadataStore.getDataService("breeze/NorthwindIBModel");
|
|
377
|
+
const adapterName = ds.adapterName; // may be null
|
|
378
|
+
```
|
|
379
|
+
@param serviceName - The service name.
|
|
380
|
+
@returns The DataService with the specified name.
|
|
381
|
+
*/
|
|
382
|
+
getDataService(serviceName) {
|
|
383
|
+
assertParam(serviceName, "serviceName").isString().check();
|
|
384
|
+
serviceName = DataService._normalizeServiceName(serviceName);
|
|
385
|
+
return core.arrayFirst(this.dataServices, function (ds) {
|
|
386
|
+
return ds.serviceName === serviceName;
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
Fetches the metadata for a specified 'service'. This method is automatically called
|
|
391
|
+
internally by an EntityManager before its first query against a new service. __Async__
|
|
392
|
+
|
|
393
|
+
Usually you will not actually process the results of a fetchMetadata call directly, but will instead
|
|
394
|
+
ask for the metadata from the EntityManager after the fetchMetadata call returns.
|
|
395
|
+
```ts
|
|
396
|
+
const ms = new MetadataStore();
|
|
397
|
+
// or more commonly
|
|
398
|
+
// const ms = anEntityManager.metadataStore;
|
|
399
|
+
const rawMetadata = await ms.fetchMetadata("breeze/NorthwindIBModel");
|
|
400
|
+
// do something with the metadata
|
|
401
|
+
```
|
|
402
|
+
@param dataService - Either a DataService or just the name of the DataService to fetch metadata for.
|
|
403
|
+
@param callback - Deprecated. Function called on success.
|
|
404
|
+
@param errorCallback - Deprecated. Function called on failure.
|
|
405
|
+
@returns Promise
|
|
406
|
+
*/
|
|
407
|
+
fetchMetadata(dataService, callback, errorCallback) {
|
|
408
|
+
try {
|
|
409
|
+
assertParam(dataService, "dataService").isString().or().isInstanceOf(DataService).check();
|
|
410
|
+
assertParam(callback, "callback").isFunction().isOptional().check();
|
|
411
|
+
assertParam(errorCallback, "errorCallback").isFunction().isOptional().check();
|
|
412
|
+
if (typeof dataService === "string") {
|
|
413
|
+
// use the dataService with a matching name or create a new one.
|
|
414
|
+
dataService = this.getDataService(dataService) || new DataService({ serviceName: dataService });
|
|
415
|
+
}
|
|
416
|
+
dataService = DataService.resolve([dataService]);
|
|
417
|
+
if (this.hasMetadataFor(dataService.serviceName)) {
|
|
418
|
+
throw new Error("Metadata for a specific serviceName may only be fetched once per MetadataStore. ServiceName: " + dataService.serviceName);
|
|
419
|
+
}
|
|
420
|
+
return dataService.adapterInstance.fetchMetadata(this, dataService).then((rawMetadata) => {
|
|
421
|
+
this.metadataFetched.publish({ metadataStore: this, dataService: dataService, rawMetadata: rawMetadata });
|
|
422
|
+
if (callback)
|
|
423
|
+
callback(rawMetadata);
|
|
424
|
+
return Promise.resolve(rawMetadata);
|
|
425
|
+
}, function (error) {
|
|
426
|
+
if (errorCallback)
|
|
427
|
+
errorCallback(error);
|
|
428
|
+
return Promise.reject(error);
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
catch (e) {
|
|
432
|
+
return Promise.reject(e);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// TODO: strongly type interceptor below.
|
|
436
|
+
/**
|
|
437
|
+
Used to register a constructor for an EntityType that is not known via standard Metadata discovery;
|
|
438
|
+
i.e. an unmapped type.
|
|
439
|
+
@param entityCtor - The constructor function for the 'unmapped' type.
|
|
440
|
+
@param interceptor - An interceptor function
|
|
441
|
+
*/
|
|
442
|
+
trackUnmappedType(entityCtor, interceptor) {
|
|
443
|
+
assertParam(entityCtor, "entityCtor").isFunction().check();
|
|
444
|
+
assertParam(interceptor, "interceptor").isFunction().isOptional().check();
|
|
445
|
+
// TODO: think about adding this to the MetadataStore.
|
|
446
|
+
let entityType = new EntityType(this);
|
|
447
|
+
entityType._setCtor(entityCtor, interceptor);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
Provides a mechanism to register a 'custom' constructor to be used when creating new instances
|
|
451
|
+
of the specified entity type. If this call is not made, a default constructor is created for
|
|
452
|
+
the entity as needed.
|
|
453
|
+
This call may be made before or after the corresponding EntityType has been discovered via
|
|
454
|
+
Metadata discovery.
|
|
455
|
+
```ts
|
|
456
|
+
class Customer {
|
|
457
|
+
miscData = "asdf"; // an unmapped property
|
|
458
|
+
doFoo() {
|
|
459
|
+
// ...
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
// assume em1 is a preexisting EntityManager;
|
|
463
|
+
em1.metadataStore.registerEntityTypeCtor("Customer", Customer);
|
|
464
|
+
// any queries or createEntity calls from this point on will call the Customer constructor
|
|
465
|
+
// registered above.
|
|
466
|
+
```
|
|
467
|
+
@param structuralTypeName - The name of the EntityType or ComplexType.
|
|
468
|
+
@param aCtor - The constructor for this EntityType or ComplexType; may be null if all you want to do is set the next parameter.
|
|
469
|
+
@param initFn - A function or the name of a function on the entity that is to be executed immediately after the entity has been created
|
|
470
|
+
and populated with any initial values. Called with 'initFn(entity)'
|
|
471
|
+
@param noTrackingFn - A function that is executed immediately after a noTracking entity has been created and whose return
|
|
472
|
+
value will be used in place of the noTracking entity.
|
|
473
|
+
*/
|
|
474
|
+
registerEntityTypeCtor(structuralTypeName, aCtor, initFn, noTrackingFn) {
|
|
475
|
+
assertParam(structuralTypeName, "structuralTypeName").isString().check();
|
|
476
|
+
assertParam(aCtor, "aCtor").isFunction().isOptional().check();
|
|
477
|
+
assertParam(initFn, "initFn").isOptional().isFunction().or().isString().check();
|
|
478
|
+
assertParam(noTrackingFn, "noTrackingFn").isOptional().isFunction().check();
|
|
479
|
+
let qualifiedTypeName = getQualifiedTypeName(this, structuralTypeName, false);
|
|
480
|
+
let typeName = qualifiedTypeName || structuralTypeName;
|
|
481
|
+
if (aCtor) {
|
|
482
|
+
if (aCtor._$typeName && aCtor._$typeName !== typeName) {
|
|
483
|
+
console.warn("Registering a constructor for " + typeName + " that is already used for " + aCtor._$typeName + ".");
|
|
484
|
+
}
|
|
485
|
+
aCtor._$typeName = typeName;
|
|
486
|
+
}
|
|
487
|
+
this._ctorRegistry.set(typeName, { ctor: aCtor, initFn: initFn, noTrackingFn: noTrackingFn });
|
|
488
|
+
if (qualifiedTypeName) {
|
|
489
|
+
let stype = this._structuralTypeMap[qualifiedTypeName];
|
|
490
|
+
stype && stype.getCtor(true); // this will complete the registration if avail now.
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
Returns whether this MetadataStore contains any metadata yet.
|
|
495
|
+
```ts
|
|
496
|
+
// assume em1 is a preexisting EntityManager;
|
|
497
|
+
if (em1.metadataStore.isEmpty()) {
|
|
498
|
+
// do something interesting
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
*/
|
|
502
|
+
isEmpty() {
|
|
503
|
+
return core.isEmpty(this._structuralTypeMap);
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
Returns an {@link EntityType} given a registered class or a type name, or null if there is none
|
|
507
|
+
and `okIfNotFound` is true.
|
|
508
|
+
```ts
|
|
509
|
+
// assume em1 is a preexisting EntityManager, and OrderDetail is registered with its store
|
|
510
|
+
const odType = em1.metadataStore.getAsEntityType(OrderDetail);
|
|
511
|
+
const sameType = em1.metadataStore.getAsEntityType("OrderDetail");
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
or to throw an error if the type is not found
|
|
515
|
+
```ts
|
|
516
|
+
const badType = em1.metadataStore.getAsEntityType("Foo", false);
|
|
517
|
+
// badType will not get set and an exception will be thrown.
|
|
518
|
+
```
|
|
519
|
+
@param type - A class registered with {@link MetadataStore.registerEntityTypeCtor}, or a type
|
|
520
|
+
name: either the fully qualified name or a short name. If a short name is specified and multiple
|
|
521
|
+
types share that same short name an exception will be thrown.
|
|
522
|
+
@param okIfNotFound - (default=false) Whether to return null, rather than throw, if the EntityType is not found.
|
|
523
|
+
@returns The EntityType, or null if it is not found and `okIfNotFound` is true.
|
|
524
|
+
*/
|
|
525
|
+
getAsEntityType(type, okIfNotFound = false) {
|
|
526
|
+
// A class stands for its type name, resolved against this store: a class can be registered
|
|
527
|
+
// with more than one.
|
|
528
|
+
const typeName = typeof type === "function" ? entityTypeForCtor(type).name : type;
|
|
529
|
+
const st = this._getStructuralType(typeName, okIfNotFound);
|
|
530
|
+
if (st instanceof EntityType) {
|
|
531
|
+
return st;
|
|
532
|
+
}
|
|
533
|
+
else if (okIfNotFound) {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
let msg = core.formatString("Unable to locate an 'EntityType' by the name: '%1'. Be sure to execute a query or call fetchMetadata first.", typeName);
|
|
538
|
+
throw new Error(msg);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
Returns an {@link EntityType} or null given its name.
|
|
543
|
+
```ts
|
|
544
|
+
// assume em1 is a preexisting EntityManager
|
|
545
|
+
const locType = em1.metadataStore.getAsComplexType("Location");
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
or to throw an error if the type is not found
|
|
549
|
+
```ts
|
|
550
|
+
const badType = em1.metadataStore.getAsComplexType("Foo", false);
|
|
551
|
+
// badType will not get set and an exception will be thrown.
|
|
552
|
+
```
|
|
553
|
+
@param typeName - Either the fully qualified name or a short name may be used. If a short name is specified and multiple types share
|
|
554
|
+
that same short name an exception will be thrown.
|
|
555
|
+
@param okIfNotFound - (default=false) Whether to throw an error if the specified EntityType is not found.
|
|
556
|
+
@returns The EntityType. ComplexType or 'null' if not not found.
|
|
557
|
+
*/
|
|
558
|
+
getAsComplexType(typeName, okIfNotFound = false) {
|
|
559
|
+
const st = this._getStructuralType(typeName, okIfNotFound);
|
|
560
|
+
if (st instanceof ComplexType) {
|
|
561
|
+
return st;
|
|
562
|
+
}
|
|
563
|
+
else if (okIfNotFound) {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
let msg = core.formatString("Unable to locate an 'ComplexType' by the name: '%1'. Be sure to execute a query or call fetchMetadata first.", typeName);
|
|
568
|
+
throw new Error(msg);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
Returns an {@link EntityType} or a {@link ComplexType} given its name.
|
|
573
|
+
@deprecated Misleading name: it returns an EntityType *or* a ComplexType. Use
|
|
574
|
+
{@link MetadataStore.getAsEntityType} or {@link MetadataStore.getAsComplexType} when you know
|
|
575
|
+
which kind you expect, and {@link MetadataStore.getStructuralType} when either will do.
|
|
576
|
+
@param typeName - Either the fully qualified name or a short name may be used. If a short name is specified and multiple types share
|
|
577
|
+
that same short name an exception will be thrown.
|
|
578
|
+
@param okIfNotFound - (default=false) Whether to throw an error if the specified EntityType is not found.
|
|
579
|
+
@returns The EntityType. ComplexType or 'null' if not not found.
|
|
580
|
+
*/
|
|
581
|
+
getEntityType(typeName, okIfNotFound = false) {
|
|
582
|
+
return this._getStructuralType(typeName, okIfNotFound);
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
Returns an {@link EntityType} or a {@link ComplexType} given its name.
|
|
586
|
+
```ts
|
|
587
|
+
// assume em1 is a preexisting EntityManager
|
|
588
|
+
const odType = em1.metadataStore.getStructuralType("OrderDetail");
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
or to throw an error if the type is not found
|
|
592
|
+
```ts
|
|
593
|
+
const badType = em1.metadataStore.getStructuralType("Foo", false);
|
|
594
|
+
// badType will not get set and an exception will be thrown.
|
|
595
|
+
```
|
|
596
|
+
Prefer {@link MetadataStore.getAsEntityType} or {@link MetadataStore.getAsComplexType} when you
|
|
597
|
+
know which kind you expect. This is the lookup for when you do not.
|
|
598
|
+
@param typeName - Either the fully qualified name or a short name may be used. If a short name is specified and multiple types share
|
|
599
|
+
that same short name an exception will be thrown.
|
|
600
|
+
@param okIfNotFound - (default=false) Whether to throw an error if the specified EntityType is not found.
|
|
601
|
+
@returns The EntityType. ComplexType or 'null' if not not found.
|
|
602
|
+
*/
|
|
603
|
+
getStructuralType(typeName, okIfNotFound = false) {
|
|
604
|
+
assertParam(typeName, "typeName").isString().check();
|
|
605
|
+
assertParam(okIfNotFound, "okIfNotFound").isBoolean().isOptional().check(false);
|
|
606
|
+
return this._getStructuralType(typeName, okIfNotFound);
|
|
607
|
+
}
|
|
608
|
+
/** @hidden @internal */
|
|
609
|
+
_getStructuralType(typeName, okIfNotFound = false) {
|
|
610
|
+
let qualTypeName = getQualifiedTypeName(this, typeName, false);
|
|
611
|
+
let type = qualTypeName ? this._structuralTypeMap[qualTypeName] : undefined;
|
|
612
|
+
if (!type) {
|
|
613
|
+
if (okIfNotFound)
|
|
614
|
+
return null;
|
|
615
|
+
let msg = core.formatString("Unable to locate a 'Type' by the name: '%1'. Be sure to execute a query or call fetchMetadata first.", typeName);
|
|
616
|
+
throw new Error(msg);
|
|
617
|
+
}
|
|
618
|
+
return type;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
Returns an array containing all of the {@link EntityType}s or {@link ComplexType}s in this MetadataStore.
|
|
622
|
+
```ts
|
|
623
|
+
// assume em1 is a preexisting EntityManager
|
|
624
|
+
const allTypes = em1.metadataStore.getEntityTypes();
|
|
625
|
+
```
|
|
626
|
+
*/
|
|
627
|
+
getEntityTypes() {
|
|
628
|
+
return getTypesFromMap(this._structuralTypeMap);
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
Returns the navigation properties whose target {@link EntityType} has not been added to this store
|
|
632
|
+
yet, as one array per missing type. An empty result means every navigation property has been
|
|
633
|
+
resolved; useful for checking metadata that is built by hand or imported in pieces.
|
|
634
|
+
*/
|
|
635
|
+
getIncompleteNavigationProperties() {
|
|
636
|
+
return Array.from(this._incompleteTypeMap.values());
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
Returns a fully qualified entityTypeName for a specified resource name. The reverse of this operation
|
|
640
|
+
can be obtained via the {@link EntityType.defaultResourceName} property
|
|
641
|
+
*/
|
|
642
|
+
getEntityTypeNameForResourceName(resourceName) {
|
|
643
|
+
assertParam(resourceName, "resourceName").isString().check();
|
|
644
|
+
return this._resourceEntityTypeMap[resourceName];
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
Associates a resourceName with an entityType.
|
|
648
|
+
|
|
649
|
+
This method is only needed in those cases where multiple resources return the same
|
|
650
|
+
entityType. In this case Metadata discovery will only determine a single resource name for
|
|
651
|
+
each entityType.
|
|
652
|
+
@param resourceName - The resource name
|
|
653
|
+
@param entityTypeOrName - If passing a string either the fully qualified name or a short name may be used. If a short name is specified and multiple types share
|
|
654
|
+
that same short name an exception will be thrown. If the entityType has not yet been discovered then a fully qualified name must be used.
|
|
655
|
+
*/
|
|
656
|
+
setEntityTypeForResourceName(resourceName, entityTypeOrName) {
|
|
657
|
+
assertParam(resourceName, "resourceName").isString().check();
|
|
658
|
+
assertParam(entityTypeOrName, "entityTypeOrName").isInstanceOf(EntityType).or().isString().check();
|
|
659
|
+
let entityTypeName;
|
|
660
|
+
if (entityTypeOrName instanceof EntityType) {
|
|
661
|
+
entityTypeName = entityTypeOrName.name;
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
entityTypeName = getQualifiedTypeName(this, entityTypeOrName, true);
|
|
665
|
+
}
|
|
666
|
+
this._resourceEntityTypeMap[resourceName] = entityTypeName;
|
|
667
|
+
let entityType = this._getStructuralType(entityTypeName, true);
|
|
668
|
+
if (entityType && entityType instanceof EntityType && !entityType.defaultResourceName) {
|
|
669
|
+
entityType.defaultResourceName = resourceName;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
/** __Dev Only__ - for use when creating a new MetadataParserAdapter */
|
|
673
|
+
static parseTypeName(entityTypeName) {
|
|
674
|
+
let typeParts = entityTypeName.split(":#");
|
|
675
|
+
if (typeParts.length > 1) {
|
|
676
|
+
return MetadataStore.makeTypeHash(typeParts[0], typeParts[1]);
|
|
677
|
+
}
|
|
678
|
+
if (stringStartsWith(entityTypeName, MetadataStore.ANONTYPE_PREFIX)) {
|
|
679
|
+
let typeHash = MetadataStore.makeTypeHash(entityTypeName);
|
|
680
|
+
typeHash.isAnonymous = true;
|
|
681
|
+
return typeHash;
|
|
682
|
+
}
|
|
683
|
+
let entityTypeNameNoAssembly = entityTypeName.split(",")[0];
|
|
684
|
+
typeParts = entityTypeNameNoAssembly.split(".");
|
|
685
|
+
if (typeParts.length > 1) {
|
|
686
|
+
let shortName = typeParts[typeParts.length - 1];
|
|
687
|
+
let namespaceParts = typeParts.slice(0, typeParts.length - 1);
|
|
688
|
+
let ns = namespaceParts.join(".");
|
|
689
|
+
return MetadataStore.makeTypeHash(shortName, ns);
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
return MetadataStore.makeTypeHash(entityTypeName);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
/** __Dev Only__ - for use when creating a new MetadataParserAdapter */
|
|
696
|
+
static makeTypeHash(shortName, ns) {
|
|
697
|
+
return {
|
|
698
|
+
/** The unqualified name, such as `"Customer"`. */
|
|
699
|
+
shortTypeName: shortName,
|
|
700
|
+
/** The namespace, such as `"Northwind.Models"`; undefined if there is none. */
|
|
701
|
+
namespace: ns,
|
|
702
|
+
/** The qualified name, such as `"Customer:#Northwind.Models"`. */
|
|
703
|
+
typeName: qualifyTypeName(shortName, ns)
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
// protected methods
|
|
707
|
+
/** @hidden @internal */
|
|
708
|
+
_checkEntityType(entity) {
|
|
709
|
+
if (entity.entityType)
|
|
710
|
+
return;
|
|
711
|
+
let typeName = entity.prototype._$typeName;
|
|
712
|
+
if (!typeName) {
|
|
713
|
+
throw new Error("This entity has not been registered. See the MetadataStore.registerEntityTypeCtor method");
|
|
714
|
+
}
|
|
715
|
+
// we know that it is an EntityType ( as opposed to a ComplexType)
|
|
716
|
+
let entityType = this._getStructuralType(typeName);
|
|
717
|
+
if (entityType) {
|
|
718
|
+
entity.entityType = entityType;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
MetadataStore.prototype._$typeName = "MetadataStore";
|
|
723
|
+
BreezeEvent.bubbleEvent(MetadataStore.prototype);
|
|
724
|
+
// The model library Breeze falls back to when none is registered (see default-adapters.ts).
|
|
725
|
+
// Installed here because a MetadataStore needs one even in a bundle with no EntityManager, and
|
|
726
|
+
// this module is in every bundle that has entity types. A getter, because the adapter's module
|
|
727
|
+
// imports this one: while this module loads, the adapter class may not exist yet.
|
|
728
|
+
setDefaultAdapters({ modelLibrary: { get ctor() { return ModelLibraryBackingStoreAdapter; } } });
|
|
729
|
+
function getTypesFromMap(typeMap) {
|
|
730
|
+
let types = [];
|
|
731
|
+
for (let key in typeMap) {
|
|
732
|
+
let value = typeMap[key];
|
|
733
|
+
// skip 'shortName' entries
|
|
734
|
+
if (key === value.name) {
|
|
735
|
+
types.push(typeMap[key]);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return types;
|
|
739
|
+
}
|
|
740
|
+
function structuralTypeFromJson(metadataStore, json, allowMerge) {
|
|
741
|
+
let typeName = qualifyTypeName(json.shortName, json.namespace);
|
|
742
|
+
let stype = metadataStore._getStructuralType(typeName, true);
|
|
743
|
+
if (stype) {
|
|
744
|
+
if (allowMerge) {
|
|
745
|
+
return mergeStructuralType(stype, json);
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
// allow it but don't replace anything.
|
|
749
|
+
return stype;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
if (!json.dataProperties) {
|
|
753
|
+
// Creating the type needs its properties. A cut-down custom-metadata object for a type that is not in the store
|
|
754
|
+
// used to fail here with a TypeError.
|
|
755
|
+
const hint = allowMerge ? " allowMerge only merges into types that are already in the store." : "";
|
|
756
|
+
throw new Error("Unable to import type '" + typeName + "': it is not in this MetadataStore and its metadata has no 'dataProperties'." + hint);
|
|
757
|
+
}
|
|
758
|
+
let config = {
|
|
759
|
+
shortName: json.shortName,
|
|
760
|
+
namespace: json.namespace,
|
|
761
|
+
isAbstract: json.isAbstract,
|
|
762
|
+
autoGeneratedKeyType: AutoGeneratedKeyType.fromName(json.autoGeneratedKeyType),
|
|
763
|
+
defaultResourceName: json.defaultResourceName,
|
|
764
|
+
custom: json.custom
|
|
765
|
+
};
|
|
766
|
+
stype = json.isComplexType ? new ComplexType(config) : new EntityType(config);
|
|
767
|
+
// baseType may not have been imported yet so we need to defer handling this type until later.
|
|
768
|
+
if (json.baseTypeName && stype instanceof EntityType) {
|
|
769
|
+
stype.baseTypeName = json.baseTypeName;
|
|
770
|
+
let baseEntityType = metadataStore._getStructuralType(json.baseTypeName, true);
|
|
771
|
+
if (baseEntityType) {
|
|
772
|
+
completeStructuralTypeFromJson(metadataStore, json, stype);
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
core.getMapArray(metadataStore._deferredTypes, json.baseTypeName).push({ json: json, stype: stype });
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
completeStructuralTypeFromJson(metadataStore, json, stype);
|
|
780
|
+
}
|
|
781
|
+
// stype may or may not have been added to the metadataStore at this point.
|
|
782
|
+
return stype;
|
|
783
|
+
}
|
|
784
|
+
function mergeStructuralType(stype, json) {
|
|
785
|
+
if (json.custom) {
|
|
786
|
+
stype.custom = json.custom;
|
|
787
|
+
}
|
|
788
|
+
mergeProps(stype, json.dataProperties);
|
|
789
|
+
mergeProps(stype, json.navigationProperties);
|
|
790
|
+
return stype;
|
|
791
|
+
}
|
|
792
|
+
function mergeProps(stype, jsonProps) {
|
|
793
|
+
if (!jsonProps)
|
|
794
|
+
return;
|
|
795
|
+
jsonProps.forEach((jsonProp) => {
|
|
796
|
+
let propName = jsonProp.name;
|
|
797
|
+
if (!propName) {
|
|
798
|
+
if (jsonProp.nameOnServer) {
|
|
799
|
+
propName = stype.metadataStore.namingConvention.serverPropertyNameToClient(jsonProp.nameOnServer, {});
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
// backslash-quote works around compiler bug
|
|
803
|
+
const msg = "Unable to complete \'importMetadata\' - cannot locate a \'name\' or \'nameOnServer\' for one of the imported property nodes";
|
|
804
|
+
throw new Error(msg);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (jsonProp.custom) {
|
|
808
|
+
let prop = stype.getProperty(propName, true);
|
|
809
|
+
prop.custom = jsonProp.custom;
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
function completeStructuralTypeFromJson(metadataStore, json, stype) {
|
|
814
|
+
// validators from baseType work because validation walks thru base types
|
|
815
|
+
// so no need to copy down.
|
|
816
|
+
if (json.validators) {
|
|
817
|
+
stype.validators = json.validators.map(Validator.fromJSON);
|
|
818
|
+
}
|
|
819
|
+
json.dataProperties.forEach(function (dp) {
|
|
820
|
+
stype._addPropertyCore(DataProperty.fromJSON(dp));
|
|
821
|
+
});
|
|
822
|
+
let isEntityType = !json.isComplexType;
|
|
823
|
+
if (isEntityType) {
|
|
824
|
+
//noinspection JSHint
|
|
825
|
+
json.navigationProperties && json.navigationProperties.forEach(function (np) {
|
|
826
|
+
stype._addPropertyCore(NavigationProperty.fromJSON(np));
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
metadataStore.addEntityType(stype);
|
|
830
|
+
let deferredTypes = metadataStore._deferredTypes;
|
|
831
|
+
let deferrals = deferredTypes.get(stype.name);
|
|
832
|
+
if (deferrals) {
|
|
833
|
+
deferrals.forEach(function (d) {
|
|
834
|
+
completeStructuralTypeFromJson(metadataStore, d.json, d.stype);
|
|
835
|
+
});
|
|
836
|
+
deferredTypes.delete(stype.name);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
function getQualifiedTypeName(metadataStore, structTypeName, throwIfNotFound) {
|
|
840
|
+
if (isQualifiedTypeName(structTypeName))
|
|
841
|
+
return structTypeName;
|
|
842
|
+
let result = metadataStore._shortNameMap.get(structTypeName);
|
|
843
|
+
if (!result && throwIfNotFound) {
|
|
844
|
+
throw new Error("Unable to locate 'entityTypeName' of: " + structTypeName);
|
|
845
|
+
}
|
|
846
|
+
return result;
|
|
847
|
+
}
|
|
848
|
+
/** Container for all of the metadata about a specific type of Entity.
|
|
849
|
+
*/
|
|
850
|
+
export class EntityType {
|
|
851
|
+
/** @hidden @internal */
|
|
852
|
+
static __nextAnonIx = 0;
|
|
853
|
+
/** Always false for an EntityType. */
|
|
854
|
+
isComplexType = false;
|
|
855
|
+
/** The {@link MetadataStore} that contains this EntityType. __Read Only__ */
|
|
856
|
+
metadataStore;
|
|
857
|
+
/** The DataProperties (see {@link DataProperty} associated with this EntityType. __Read Only__ */
|
|
858
|
+
dataProperties;
|
|
859
|
+
/** The NavigationProperties (see {@link NavigationProperty} associated with this EntityType. __Read Only__ */
|
|
860
|
+
navigationProperties;
|
|
861
|
+
/**
|
|
862
|
+
The DataProperties associated with this EntityType that make up it's {@link EntityKey}. __Read Only__ */
|
|
863
|
+
keyProperties;
|
|
864
|
+
/** The DataProperties associated with this EntityType that are foreign key properties. __Read Only__ */
|
|
865
|
+
foreignKeyProperties;
|
|
866
|
+
/** The foreign key DataProperties on *other* EntityTypes that refer to this EntityType. When an
|
|
867
|
+
entity's key changes, such as when a temporary key is replaced on save, Breeze uses these to update
|
|
868
|
+
the foreign keys of cached entities it cannot reach through a navigation property. __Read Only__ */
|
|
869
|
+
inverseForeignKeyProperties;
|
|
870
|
+
/** The DataProperties associated with this EntityType that are concurrency properties. __Read Only__ */
|
|
871
|
+
concurrencyProperties;
|
|
872
|
+
/** The DataProperties for this EntityType that contain instances of a {@link ComplexType}. __Read Only__ */
|
|
873
|
+
complexProperties;
|
|
874
|
+
/** The DataProperties associated with this EntityType that are not mapped to any backend datastore. These are effectively free standing
|
|
875
|
+
properties. __Read Only__ */
|
|
876
|
+
unmappedProperties;
|
|
877
|
+
/** The fully qualified name of this EntityType. __Read Only__ */
|
|
878
|
+
name;
|
|
879
|
+
/** The short, unqualified, name for this EntityType. __Read Only__ */
|
|
880
|
+
shortName;
|
|
881
|
+
/** The namespace for this EntityType. __Read Only__ */
|
|
882
|
+
namespace;
|
|
883
|
+
/** The name of this EntityType's base EntityType (if any) */
|
|
884
|
+
baseTypeName;
|
|
885
|
+
/** The base EntityType (if any) for this EntityType. __Read Only__ */
|
|
886
|
+
baseEntityType;
|
|
887
|
+
/** The EntityTypes that derive directly from this one. See {@link EntityType.getSelfAndSubtypes} for
|
|
888
|
+
the whole hierarchy. __Read Only__ */
|
|
889
|
+
subtypes;
|
|
890
|
+
/** Whether this EntityType is abstract. __Read Only__ */
|
|
891
|
+
isAbstract;
|
|
892
|
+
/** Whether this EntityType is anonymous. Anonymous types will never be communicated to or from the server. They are purely for
|
|
893
|
+
client side use and are given an automatically generated name. __Read Only__ */
|
|
894
|
+
isAnonymous;
|
|
895
|
+
/** Whether this EntityType has been 'frozen'. EntityTypes become frozen after the first instance
|
|
896
|
+
of that type has been created and attached to an EntityManager. */
|
|
897
|
+
isFrozen;
|
|
898
|
+
/** The {@link AutoGeneratedKeyType} for this EntityType. __Read Only__ */
|
|
899
|
+
autoGeneratedKeyType;
|
|
900
|
+
/** The default resource name associated with this EntityType. An EntityType may be queried via a variety of 'resource names' but this one
|
|
901
|
+
is used as the default when no resource name is provided. This will occur when calling {@link EntityAspect.loadNavigationProperty}
|
|
902
|
+
or when executing any {@link EntityQuery} that was created via an {@link EntityKey}. __Read Only__ */
|
|
903
|
+
defaultResourceName;
|
|
904
|
+
/** A function that is used to customize the serialization of any EntityProperties of this type. */
|
|
905
|
+
serializerFn;
|
|
906
|
+
/** A free form object that can be used to define any custom metadata for this EntityType. __Read Only__ */
|
|
907
|
+
custom;
|
|
908
|
+
/** The entity level validators associated with this EntityType. Validators can be added and
|
|
909
|
+
removed from this collection. __Read Only__. */
|
|
910
|
+
validators;
|
|
911
|
+
/** Not used by breeze-client 3: always an empty array. In 2.x the CSDL metadata parser recorded
|
|
912
|
+
data types it did not recognize here; v3 reports them with `console.warn` instead. */
|
|
913
|
+
warnings;
|
|
914
|
+
/** The function, or the name of a method on the entity, that is called with each entity of this type
|
|
915
|
+
once it has been created and populated, whether by {@link EntityType.createEntity} or from query
|
|
916
|
+
results. A base type's `initFn` runs first. Set from the `initFn` argument of
|
|
917
|
+
{@link MetadataStore.registerEntityTypeCtor}; applications set it there rather than here. */
|
|
918
|
+
initFn;
|
|
919
|
+
/** The function that a query with {@link EntityQuery.noTracking} enabled calls for each result of this
|
|
920
|
+
type, as `noTrackingFn(node, entityType)`; its return value replaces the result. Set from the
|
|
921
|
+
`noTrackingFn` argument of {@link MetadataStore.registerEntityTypeCtor}; applications set it there
|
|
922
|
+
rather than here. */
|
|
923
|
+
noTrackingFn;
|
|
924
|
+
/** @hidden @internal */
|
|
925
|
+
_extra;
|
|
926
|
+
/** @hidden @internal */
|
|
927
|
+
_ctor;
|
|
928
|
+
/** @hidden @internal */
|
|
929
|
+
_mappedPropertiesCount;
|
|
930
|
+
/**
|
|
931
|
+
@deprecated Use {@link EntityType.getCtor} instead.
|
|
932
|
+
*/
|
|
933
|
+
getEntityCtor = this.getCtor;
|
|
934
|
+
/** @hidden @internal */
|
|
935
|
+
static qualifyTypeName = qualifyTypeName;
|
|
936
|
+
/** EntityType constructor
|
|
937
|
+
```ts
|
|
938
|
+
const entityType = new EntityType({
|
|
939
|
+
shortName: "person",
|
|
940
|
+
namespace: "myAppNamespace"
|
|
941
|
+
});
|
|
942
|
+
```
|
|
943
|
+
@param config - Configuration settings or a MetadataStore. If this parameter is just a MetadataStore
|
|
944
|
+
then what will be created is an 'anonymous' type that will never be communicated to or from the server. It is purely for
|
|
945
|
+
client side use and will be given an automatically generated name. Normally, however, you will use a configuration object.
|
|
946
|
+
*/
|
|
947
|
+
constructor(config) {
|
|
948
|
+
if (arguments.length > 1) {
|
|
949
|
+
throw new Error("The EntityType ctor has a single argument that is either a 'MetadataStore' or a configuration object.");
|
|
950
|
+
}
|
|
951
|
+
// let etConfig = <EntityTypeConfig> <any> undefined;
|
|
952
|
+
let etConfig = undefined;
|
|
953
|
+
if (config._$typeName === "MetadataStore") {
|
|
954
|
+
this.metadataStore = config;
|
|
955
|
+
this.shortName = "Anon_" + (++EntityType.__nextAnonIx);
|
|
956
|
+
this.namespace = "";
|
|
957
|
+
this.isAnonymous = true;
|
|
958
|
+
// etConfig = undefined;
|
|
959
|
+
}
|
|
960
|
+
else {
|
|
961
|
+
etConfig = config;
|
|
962
|
+
assertConfig(config)
|
|
963
|
+
.whereParam("shortName").isNonEmptyString()
|
|
964
|
+
.whereParam("namespace").isString().isOptional().withDefault("")
|
|
965
|
+
.whereParam("baseTypeName").isString().isOptional()
|
|
966
|
+
.whereParam("isAbstract").isBoolean().isOptional().withDefault(false)
|
|
967
|
+
.whereParam("autoGeneratedKeyType").isEnumOf(AutoGeneratedKeyType).isOptional().withDefault(AutoGeneratedKeyType.None)
|
|
968
|
+
.whereParam("defaultResourceName").isNonEmptyString().isOptional().withDefault(null)
|
|
969
|
+
.whereParam("dataProperties").isOptional()
|
|
970
|
+
.whereParam("navigationProperties").isOptional()
|
|
971
|
+
.whereParam("serializerFn").isOptional().isFunction()
|
|
972
|
+
.whereParam("custom").isOptional()
|
|
973
|
+
.applyAll(this);
|
|
974
|
+
}
|
|
975
|
+
this.name = qualifyTypeName(this.shortName, this.namespace);
|
|
976
|
+
// the defaultResourceName may also be set up either via metadata lookup or first query or via the 'setProperties' method
|
|
977
|
+
this.dataProperties = [];
|
|
978
|
+
this.navigationProperties = [];
|
|
979
|
+
this.complexProperties = [];
|
|
980
|
+
this.keyProperties = [];
|
|
981
|
+
this.foreignKeyProperties = [];
|
|
982
|
+
this.inverseForeignKeyProperties = [];
|
|
983
|
+
this.concurrencyProperties = [];
|
|
984
|
+
this.unmappedProperties = []; // will be updated later.
|
|
985
|
+
this.validators = [];
|
|
986
|
+
this.warnings = [];
|
|
987
|
+
this._mappedPropertiesCount = 0;
|
|
988
|
+
this.subtypes = [];
|
|
989
|
+
// now process any data/nav props
|
|
990
|
+
if (etConfig && etConfig.dataProperties) {
|
|
991
|
+
addProperties(this, etConfig.dataProperties, DataProperty);
|
|
992
|
+
}
|
|
993
|
+
if (etConfig && etConfig.navigationProperties) {
|
|
994
|
+
addProperties(this, etConfig.navigationProperties, NavigationProperty);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
General purpose property set method
|
|
999
|
+
```ts
|
|
1000
|
+
// assume em1 is an EntityManager containing a number of existing entities.
|
|
1001
|
+
const custType = em1.metadataStore.getAsEntityType("Customer");
|
|
1002
|
+
custType.setProperties({
|
|
1003
|
+
autoGeneratedKeyType: AutoGeneratedKeyType.Identity,
|
|
1004
|
+
defaultResourceName: "CustomersAndIncludedOrders"
|
|
1005
|
+
});
|
|
1006
|
+
```
|
|
1007
|
+
@param config - a configuration object
|
|
1008
|
+
*/
|
|
1009
|
+
setProperties(config) {
|
|
1010
|
+
assertConfig(config)
|
|
1011
|
+
.whereParam("autoGeneratedKeyType").isEnumOf(AutoGeneratedKeyType).isOptional()
|
|
1012
|
+
.whereParam("defaultResourceName").isString().isOptional()
|
|
1013
|
+
.whereParam("serializerFn").isFunction().isOptional()
|
|
1014
|
+
.whereParam("custom").isOptional()
|
|
1015
|
+
.applyAll(this);
|
|
1016
|
+
if (config.defaultResourceName) {
|
|
1017
|
+
this.defaultResourceName = config.defaultResourceName;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
Returns whether this type is a subtype of a specified type.
|
|
1022
|
+
*/
|
|
1023
|
+
isSubtypeOf(entityType) {
|
|
1024
|
+
assertParam(entityType, "entityType").isInstanceOf(EntityType).check();
|
|
1025
|
+
let baseType = this;
|
|
1026
|
+
do {
|
|
1027
|
+
if (baseType === entityType)
|
|
1028
|
+
return true;
|
|
1029
|
+
baseType = baseType.baseEntityType;
|
|
1030
|
+
} while (baseType);
|
|
1031
|
+
return false;
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
Returns an array containing this type and any/all subtypes of this type down thru the hierarchy.
|
|
1035
|
+
*/
|
|
1036
|
+
getSelfAndSubtypes() {
|
|
1037
|
+
let result = [this];
|
|
1038
|
+
this.subtypes.forEach(function (st) {
|
|
1039
|
+
let subtypes = st.getSelfAndSubtypes();
|
|
1040
|
+
result.push.apply(result, subtypes);
|
|
1041
|
+
});
|
|
1042
|
+
return result;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
Returns the entity level validators that apply to this type: its own {@link EntityType.validators}
|
|
1046
|
+
followed by those of each base type. Returns a new array, so changing it does not change the type.
|
|
1047
|
+
*/
|
|
1048
|
+
getAllValidators() {
|
|
1049
|
+
let result = this.validators.slice(0);
|
|
1050
|
+
let bt = this.baseEntityType;
|
|
1051
|
+
while (bt) {
|
|
1052
|
+
result.push.apply(result, bt.validators);
|
|
1053
|
+
bt = bt.baseEntityType;
|
|
1054
|
+
}
|
|
1055
|
+
return result;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
Adds a {@link DataProperty} or a {@link NavigationProperty} to this EntityType.
|
|
1059
|
+
```ts
|
|
1060
|
+
// assume myEntityType is a newly constructed EntityType.
|
|
1061
|
+
myEntityType.addProperty(dataProperty1);
|
|
1062
|
+
myEntityType.addProperty(dataProperty2);
|
|
1063
|
+
myEntityType.addProperty(navigationProperty1);
|
|
1064
|
+
```
|
|
1065
|
+
*/
|
|
1066
|
+
addProperty(property) {
|
|
1067
|
+
assertParam(property, "property").isInstanceOf(DataProperty).or().isInstanceOf(NavigationProperty).check();
|
|
1068
|
+
// true is 2nd arg to force resolve of any navigation properties.
|
|
1069
|
+
let newprop = this._addPropertyCore(property, true);
|
|
1070
|
+
if (this.subtypes && this.subtypes.length) {
|
|
1071
|
+
let stype = this;
|
|
1072
|
+
stype.getSelfAndSubtypes().forEach(function (st) {
|
|
1073
|
+
if (st !== stype) {
|
|
1074
|
+
if (property.isNavigationProperty) {
|
|
1075
|
+
st._addPropertyCore(new NavigationProperty(property), true);
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
st._addPropertyCore(new DataProperty(property), true);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
return newprop;
|
|
1084
|
+
}
|
|
1085
|
+
/** @hidden @internal */
|
|
1086
|
+
_updateFromBase(baseEntityType) {
|
|
1087
|
+
this.baseEntityType = baseEntityType;
|
|
1088
|
+
if (this.autoGeneratedKeyType === AutoGeneratedKeyType.None) {
|
|
1089
|
+
this.autoGeneratedKeyType = baseEntityType.autoGeneratedKeyType;
|
|
1090
|
+
}
|
|
1091
|
+
baseEntityType.dataProperties.forEach((dp) => {
|
|
1092
|
+
let newDp = new DataProperty(dp);
|
|
1093
|
+
// don't need to copy validators becaue we will walk the hierarchy to find them
|
|
1094
|
+
newDp.validators = [];
|
|
1095
|
+
newDp.baseProperty = dp;
|
|
1096
|
+
this._addPropertyCore(newDp);
|
|
1097
|
+
}, this);
|
|
1098
|
+
baseEntityType.navigationProperties.forEach((np) => {
|
|
1099
|
+
let newNp = new NavigationProperty(np);
|
|
1100
|
+
// don't need to copy validators becaue we will walk the hierarchy to find them
|
|
1101
|
+
newNp.validators = [];
|
|
1102
|
+
newNp.baseProperty = np;
|
|
1103
|
+
this._addPropertyCore(newNp);
|
|
1104
|
+
}, this);
|
|
1105
|
+
baseEntityType.subtypes.push(this);
|
|
1106
|
+
}
|
|
1107
|
+
/** @hidden @internal */
|
|
1108
|
+
_addPropertyCore(property, shouldResolve = false) {
|
|
1109
|
+
if (this.isFrozen) {
|
|
1110
|
+
throw new Error("The '" + this.name + "' EntityType/ComplexType has been frozen. You can only add properties to an EntityType/ComplexType before any instances of that type have been created and attached to an entityManager.");
|
|
1111
|
+
}
|
|
1112
|
+
let parentType = property.parentType;
|
|
1113
|
+
if (parentType) {
|
|
1114
|
+
if (parentType !== this) {
|
|
1115
|
+
throw new Error("This property: " + property.name + " has already been added to " + property.parentType.name);
|
|
1116
|
+
}
|
|
1117
|
+
else {
|
|
1118
|
+
// adding the same property more than once to the same entityType is just ignored.
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
property.parentType = this;
|
|
1123
|
+
let ms = this.metadataStore;
|
|
1124
|
+
// if (property.isDataProperty) { // modified because doesn't act as a type guard
|
|
1125
|
+
if (property instanceof DataProperty) {
|
|
1126
|
+
this._addDataProperty(property);
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
this._addNavigationProperty(property);
|
|
1130
|
+
// metadataStore can be undefined if this entityType has not yet been added to a MetadataStore.
|
|
1131
|
+
if (shouldResolve && ms) {
|
|
1132
|
+
tryResolveNp(property, ms);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
// unmapped properties can be added AFTER entityType has already resolved all property names.
|
|
1136
|
+
if (ms && !(property.name && property.nameOnServer)) {
|
|
1137
|
+
updateClientServerNames(ms.namingConvention, property, "name");
|
|
1138
|
+
}
|
|
1139
|
+
// props can be added after entity prototype has already been wrapped.
|
|
1140
|
+
if (ms && this._extra) {
|
|
1141
|
+
if (this._extra.alreadyWrappedProps) {
|
|
1142
|
+
let proto = this._ctor.prototype;
|
|
1143
|
+
config.interfaceRegistry.modelLibrary.getDefaultInstance().initializeEntityPrototype(proto);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
createEntity(initialValues) {
|
|
1148
|
+
// ignore the _$eref once the entity is attached to an entityManager.
|
|
1149
|
+
if (initialValues && initialValues._$eref && !initialValues._$eref.entityAspect.entityManager)
|
|
1150
|
+
return initialValues._$eref;
|
|
1151
|
+
let instance = this._createInstanceCore();
|
|
1152
|
+
if (initialValues) {
|
|
1153
|
+
// only assign an _eref if the object is fully "keyed"
|
|
1154
|
+
if (this.keyProperties.every(function (kp) {
|
|
1155
|
+
return initialValues[kp.name] != null;
|
|
1156
|
+
})) {
|
|
1157
|
+
initialValues._$eref = instance;
|
|
1158
|
+
}
|
|
1159
|
+
this._updateTargetFromRaw(instance, initialValues, getRawValueFromConfig);
|
|
1160
|
+
this.navigationProperties.forEach(function (np) {
|
|
1161
|
+
let relatedEntity;
|
|
1162
|
+
let val = initialValues[np.name];
|
|
1163
|
+
if (val != undefined) {
|
|
1164
|
+
let navEntityType = np.entityType;
|
|
1165
|
+
if (np.isScalar) {
|
|
1166
|
+
relatedEntity = val.entityAspect ? val : navEntityType.createEntity(val);
|
|
1167
|
+
instance.setProperty(np.name, relatedEntity);
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
let relatedEntities = instance.getProperty(np.name);
|
|
1171
|
+
val.forEach((v) => {
|
|
1172
|
+
relatedEntity = v.entityAspect ? v : navEntityType.createEntity(v);
|
|
1173
|
+
relatedEntities.push(relatedEntity);
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
this._initializeInstance(instance);
|
|
1180
|
+
return instance;
|
|
1181
|
+
}
|
|
1182
|
+
/** @hidden @internal */
|
|
1183
|
+
_createInstanceCore() {
|
|
1184
|
+
let aCtor = this.getCtor();
|
|
1185
|
+
let instance = new aCtor();
|
|
1186
|
+
new EntityAspect(instance);
|
|
1187
|
+
return instance;
|
|
1188
|
+
}
|
|
1189
|
+
/** @hidden @internal */
|
|
1190
|
+
_initializeInstance(instance) {
|
|
1191
|
+
if (this.baseEntityType) {
|
|
1192
|
+
this.baseEntityType._initializeInstance(instance);
|
|
1193
|
+
}
|
|
1194
|
+
let initFn = this.initFn;
|
|
1195
|
+
if (initFn) {
|
|
1196
|
+
let fn = (typeof initFn === "string") ? instance[initFn] : initFn;
|
|
1197
|
+
fn(instance);
|
|
1198
|
+
}
|
|
1199
|
+
this.complexProperties && this.complexProperties.forEach(function (cp) {
|
|
1200
|
+
let complexType = cp.dataType;
|
|
1201
|
+
let ctInstance = instance.getProperty(cp.name);
|
|
1202
|
+
if (Array.isArray(ctInstance)) {
|
|
1203
|
+
ctInstance.forEach((ctInst) => {
|
|
1204
|
+
complexType._initializeInstance(ctInst);
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
else {
|
|
1208
|
+
complexType._initializeInstance(ctInstance);
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
// not needed for complexObjects
|
|
1212
|
+
if (instance.entityAspect) {
|
|
1213
|
+
instance.entityAspect._initialized = true;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
Returns the constructor for this EntityType.
|
|
1218
|
+
@param forceRefresh - Whether to ignore any cached version of this constructor. (default == false)
|
|
1219
|
+
@returns The constructor for this EntityType.
|
|
1220
|
+
*/
|
|
1221
|
+
getCtor(forceRefresh = false) {
|
|
1222
|
+
if (this._ctor && !forceRefresh)
|
|
1223
|
+
return this._ctor;
|
|
1224
|
+
let ctorRegistry = this.metadataStore._ctorRegistry;
|
|
1225
|
+
let r = ctorRegistry.get(this.name) || ctorRegistry.get(this.shortName) || {};
|
|
1226
|
+
let aCtor = r.ctor || this._ctor;
|
|
1227
|
+
let ctorType = aCtor && aCtor.prototype && (aCtor.prototype.entityType || aCtor.prototype.complexType);
|
|
1228
|
+
if (ctorType && ctorType.metadataStore !== this.metadataStore) {
|
|
1229
|
+
// We can't risk a mismatch between the ctor and the type info in a specific metadatastore
|
|
1230
|
+
// because modelLibraries rely on type info to intercept ctor properties
|
|
1231
|
+
throw new Error("Cannot register the same constructor for " + this.name + " in different metadata stores. Please define a separate constructor for each metadata store.");
|
|
1232
|
+
}
|
|
1233
|
+
if (r.ctor && forceRefresh) {
|
|
1234
|
+
this._extra = undefined;
|
|
1235
|
+
}
|
|
1236
|
+
if (!aCtor) {
|
|
1237
|
+
let createCtor = config.interfaceRegistry.modelLibrary.getDefaultInstance().createCtor;
|
|
1238
|
+
aCtor = createCtor ? createCtor(this) : createEmptyCtor(this);
|
|
1239
|
+
}
|
|
1240
|
+
this.initFn = r.initFn;
|
|
1241
|
+
this.noTrackingFn = r.noTrackingFn;
|
|
1242
|
+
aCtor.prototype._$typeName = this.name;
|
|
1243
|
+
this._setCtor(aCtor);
|
|
1244
|
+
return aCtor;
|
|
1245
|
+
}
|
|
1246
|
+
/** @hidden @internal */
|
|
1247
|
+
// May make public later.
|
|
1248
|
+
_setCtor(aCtor, interceptor) {
|
|
1249
|
+
let instanceProto = aCtor.prototype;
|
|
1250
|
+
// place for extra breeze related data
|
|
1251
|
+
this._extra = this._extra || {};
|
|
1252
|
+
let instance = new aCtor();
|
|
1253
|
+
calcUnmappedProperties(this, instance);
|
|
1254
|
+
if (this._$typeName === "EntityType") {
|
|
1255
|
+
// insure that all of the properties are on the 'template' instance before watching the class.
|
|
1256
|
+
instanceProto.entityType = this;
|
|
1257
|
+
}
|
|
1258
|
+
else {
|
|
1259
|
+
instanceProto.complexType = this;
|
|
1260
|
+
}
|
|
1261
|
+
// defaultPropertyInterceptor is a 'global' (but internal to breeze) function;
|
|
1262
|
+
instanceProto._$interceptor = interceptor || defaultPropertyInterceptor;
|
|
1263
|
+
config.interfaceRegistry.modelLibrary.getDefaultInstance().initializeEntityPrototype(instanceProto);
|
|
1264
|
+
this._ctor = aCtor;
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
Adds either an entity or property level validator to this EntityType.
|
|
1268
|
+
```ts
|
|
1269
|
+
// assume em1 is an EntityManager containing a number of existing entities.
|
|
1270
|
+
const custType = em1.metadataStore.getAsEntityType("Customer");
|
|
1271
|
+
const countryProp = custType.getProperty("country");
|
|
1272
|
+
const valFn = (v: string | null) => v == null || v.startsWith("US");
|
|
1273
|
+
const countryValidator = new Validator("countryIsUS", valFn,
|
|
1274
|
+
{ displayName: "Country", messageTemplate: "'%displayName%' must start with 'US'" });
|
|
1275
|
+
custType.addValidator(countryValidator, countryProp);
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
This is the same as adding an entity level validator via the 'validators' property of DataProperty or NavigationProperty
|
|
1279
|
+
```ts
|
|
1280
|
+
countryProp.validators.push(countryValidator);
|
|
1281
|
+
```
|
|
1282
|
+
|
|
1283
|
+
Entity level validators can also be added by omitting the 'property' parameter.
|
|
1284
|
+
```ts
|
|
1285
|
+
custType.addValidator(someEntityLevelValidator);
|
|
1286
|
+
```
|
|
1287
|
+
|
|
1288
|
+
or
|
|
1289
|
+
```ts
|
|
1290
|
+
custType.validators.push(someEntityLevelValidator);
|
|
1291
|
+
```
|
|
1292
|
+
@param validator - Validator to add.
|
|
1293
|
+
@param property - Property to add this validator to. If omitted, the validator is assumed to be an
|
|
1294
|
+
entity level validator and is added to the EntityType's 'validators'.
|
|
1295
|
+
*/
|
|
1296
|
+
addValidator(validator, property) {
|
|
1297
|
+
assertParam(validator, "validator").isInstanceOf(Validator).check();
|
|
1298
|
+
assertParam(property, "property").isOptional().isString().or().isEntityProperty().check();
|
|
1299
|
+
if (property != null) {
|
|
1300
|
+
let prop = (typeof property === 'string') ? this.getProperty(property, true) : property;
|
|
1301
|
+
prop.validators.push(validator);
|
|
1302
|
+
}
|
|
1303
|
+
else {
|
|
1304
|
+
this.validators.push(validator);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
Returns all of the properties ( dataProperties and navigationProperties) for this EntityType.
|
|
1309
|
+
```ts
|
|
1310
|
+
// assume em1 is an EntityManager containing a number of existing entities.
|
|
1311
|
+
const custType = em1.metadataStore.getAsEntityType("Customer");
|
|
1312
|
+
const arrayOfProps = custType.getProperties();
|
|
1313
|
+
```
|
|
1314
|
+
@returns An array of Data and Navigation properties.
|
|
1315
|
+
*/
|
|
1316
|
+
getProperties() {
|
|
1317
|
+
return this.dataProperties.concat(this.navigationProperties);
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
Returns all of the property names ( for both dataProperties and navigationProperties) for this EntityType.
|
|
1321
|
+
```ts
|
|
1322
|
+
// assume em1 is an EntityManager containing a number of existing entities.
|
|
1323
|
+
const custType = em1.metadataStore.getAsEntityType("Customer");
|
|
1324
|
+
const arrayOfPropNames = custType.getPropertyNames();
|
|
1325
|
+
```
|
|
1326
|
+
*/
|
|
1327
|
+
getPropertyNames() {
|
|
1328
|
+
return this.getProperties().map(core.pluck('name'));
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
Returns a data property with the specified name or null.
|
|
1332
|
+
```ts
|
|
1333
|
+
// assume em1 is an EntityManager containing a number of existing entities.
|
|
1334
|
+
const custType = em1.metadataStore.getAsEntityType("Customer");
|
|
1335
|
+
const companyNameDataProp = custType.getDataProperty("companyName");
|
|
1336
|
+
```
|
|
1337
|
+
@returns A DataProperty or null if not found.
|
|
1338
|
+
*/
|
|
1339
|
+
getDataProperty(propertyName) {
|
|
1340
|
+
return core.arrayFirst(this.dataProperties, core.propEq('name', propertyName));
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
Returns a navigation property with the specified name or null.
|
|
1344
|
+
```ts
|
|
1345
|
+
// assume em1 is an EntityManager containing a number of existing entities.
|
|
1346
|
+
const custType = em1.metadataStore.getAsEntityType("Customer");
|
|
1347
|
+
const customerOrdersNavProp = custType.getNavigationProperty("orders");
|
|
1348
|
+
```
|
|
1349
|
+
@returns A NavigationProperty or null if not found.
|
|
1350
|
+
*/
|
|
1351
|
+
getNavigationProperty(propertyName) {
|
|
1352
|
+
return core.arrayFirst(this.navigationProperties, core.propEq('name', propertyName));
|
|
1353
|
+
}
|
|
1354
|
+
/**
|
|
1355
|
+
Returns either a DataProperty or a NavigationProperty with the specified name or null.
|
|
1356
|
+
|
|
1357
|
+
This method also accepts a '.' delimited property path and will return the 'property' at the
|
|
1358
|
+
end of the path.
|
|
1359
|
+
```ts
|
|
1360
|
+
const custType = em1.metadataStore.getAsEntityType("Customer");
|
|
1361
|
+
const companyNameProp = custType.getProperty("companyName");
|
|
1362
|
+
```
|
|
1363
|
+
|
|
1364
|
+
This method can also walk a property path to return a property
|
|
1365
|
+
```ts
|
|
1366
|
+
const orderDetailType = em1.metadataStore.getAsEntityType("OrderDetail");
|
|
1367
|
+
const companyNameProp2 = orderDetailType.getProperty("order.customer.companyName");
|
|
1368
|
+
// companyNameProp === companyNameProp2
|
|
1369
|
+
```
|
|
1370
|
+
@param [throwIfNotFound=false] {Boolean} Whether to throw an exception if not found.
|
|
1371
|
+
@returns A DataProperty or NavigationProperty or null if not found.
|
|
1372
|
+
*/
|
|
1373
|
+
getProperty(propertyPath, throwIfNotFound = false) {
|
|
1374
|
+
let props = this.getPropertiesOnPath(propertyPath, false, throwIfNotFound);
|
|
1375
|
+
return (props && props.length > 0) ? props[props.length - 1] : null;
|
|
1376
|
+
}
|
|
1377
|
+
/** @hidden @internal */
|
|
1378
|
+
// TODO: have this return empty array instead of null and fix consumers.
|
|
1379
|
+
// TODO: think about renaming with '_' prefix.
|
|
1380
|
+
getPropertiesOnPath(propertyPath, useServerName, throwIfNotFound = false) {
|
|
1381
|
+
let propertyNames = (Array.isArray(propertyPath)) ? propertyPath : propertyPath.trim().split('.');
|
|
1382
|
+
let ok = true;
|
|
1383
|
+
let key = useServerName === true ? "nameOnServer" : useServerName === false ? "name" : null;
|
|
1384
|
+
let parentType = this;
|
|
1385
|
+
const getProps = (propName) => {
|
|
1386
|
+
const fn = key === null ? core.propsEq("name", "nameOnServer", propName) : core.propEq(key, propName);
|
|
1387
|
+
let prop = core.arrayFirst(parentType.getProperties(), fn);
|
|
1388
|
+
if (prop) {
|
|
1389
|
+
parentType = (prop instanceof NavigationProperty) ? prop.entityType : prop.dataType;
|
|
1390
|
+
// parentType = prop.isNavigationProperty ? prop.entityType : prop.dataType;
|
|
1391
|
+
}
|
|
1392
|
+
else if (throwIfNotFound) {
|
|
1393
|
+
throw new Error("unable to locate property: " + propName + " on entityType: " + parentType.name);
|
|
1394
|
+
}
|
|
1395
|
+
else {
|
|
1396
|
+
ok = false;
|
|
1397
|
+
}
|
|
1398
|
+
return prop;
|
|
1399
|
+
};
|
|
1400
|
+
let props = propertyNames.map(getProps);
|
|
1401
|
+
return ok ? props : null;
|
|
1402
|
+
}
|
|
1403
|
+
/** Translates a client-side property path into the names the server uses, applying this type's
|
|
1404
|
+
{@link NamingConvention} to each segment: with the default camelCase convention,
|
|
1405
|
+
`"company.companyName"` becomes `"Company.CompanyName"`. A {@link UriBuilderAdapter} uses it to
|
|
1406
|
+
write a query the server can read.
|
|
1407
|
+
@param propertyPath - a path of property names separated by `.`
|
|
1408
|
+
@param delimiter - what to join the server names with. Defaults to `.`
|
|
1409
|
+
@returns The same path in server terms. */
|
|
1410
|
+
clientPropertyPathToServer(propertyPath, delimiter = '.') {
|
|
1411
|
+
let propNames;
|
|
1412
|
+
if (this.isAnonymous) {
|
|
1413
|
+
let fn = this.metadataStore.namingConvention.clientPropertyNameToServer;
|
|
1414
|
+
propNames = propertyPath.split(".").map(function (propName) {
|
|
1415
|
+
return fn(propName);
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
else {
|
|
1419
|
+
let props = this.getPropertiesOnPath(propertyPath, false, true);
|
|
1420
|
+
propNames = props.map((prop) => prop.nameOnServer);
|
|
1421
|
+
}
|
|
1422
|
+
return propNames.join(delimiter);
|
|
1423
|
+
}
|
|
1424
|
+
/** Builds the {@link EntityKey} for an entity still in its raw server form, before it has been
|
|
1425
|
+
materialized. A {@link DataServiceAdapter} or {@link JsonResultsAdapter} uses it to find out whether
|
|
1426
|
+
the cache already holds the entity a node describes.
|
|
1427
|
+
@param rawEntity - the untyped node as it arrived from the server
|
|
1428
|
+
@param rawValueFn - reads one {@link DataProperty} off that node. It is given the node and the
|
|
1429
|
+
property, and is what knows whether the node is keyed by client or by server names
|
|
1430
|
+
@returns The key, with each value parsed into its property's {@link DataType}. */
|
|
1431
|
+
getEntityKeyFromRawEntity(rawEntity, rawValueFn) {
|
|
1432
|
+
let keyValues = this.keyProperties.map((dp) => {
|
|
1433
|
+
let val = rawValueFn(rawEntity, dp);
|
|
1434
|
+
return DataType.parseRawValue(val, dp.dataType);
|
|
1435
|
+
});
|
|
1436
|
+
return new EntityKey(this, keyValues);
|
|
1437
|
+
}
|
|
1438
|
+
/** @hidden @internal */
|
|
1439
|
+
_updateTargetFromRaw(target, raw, rawValueFn) {
|
|
1440
|
+
// called recursively for complex properties
|
|
1441
|
+
this.dataProperties.forEach((dp) => {
|
|
1442
|
+
if (!dp.isSettable)
|
|
1443
|
+
return;
|
|
1444
|
+
let rawVal = rawValueFn(raw, dp);
|
|
1445
|
+
if (rawVal === undefined)
|
|
1446
|
+
return;
|
|
1447
|
+
let dataType = dp.dataType; // this will be a complexType when dp is a complexProperty
|
|
1448
|
+
let oldVal;
|
|
1449
|
+
if (dp.isComplexProperty) {
|
|
1450
|
+
let complexType = dp.dataType;
|
|
1451
|
+
if (rawVal === null)
|
|
1452
|
+
return; // rawVal may be null in nosql dbs where it was never defined for the given row.
|
|
1453
|
+
oldVal = target.getProperty(dp.name);
|
|
1454
|
+
if (dp.isScalar) {
|
|
1455
|
+
complexType._updateTargetFromRaw(oldVal, rawVal, rawValueFn);
|
|
1456
|
+
}
|
|
1457
|
+
else {
|
|
1458
|
+
if (Array.isArray(rawVal)) {
|
|
1459
|
+
let newVal = rawVal.map(function (rawCo) {
|
|
1460
|
+
let newCo = complexType._createInstanceCore(target, dp);
|
|
1461
|
+
complexType._updateTargetFromRaw(newCo, rawCo, rawValueFn);
|
|
1462
|
+
complexType._initializeInstance(newCo);
|
|
1463
|
+
return newCo;
|
|
1464
|
+
});
|
|
1465
|
+
if (!core.arrayEquals(oldVal, newVal, coEquals)) {
|
|
1466
|
+
// clear the old array and push new objects into it.
|
|
1467
|
+
oldVal.length = 0;
|
|
1468
|
+
newVal.forEach(function (nv) {
|
|
1469
|
+
oldVal.push(nv);
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
else {
|
|
1474
|
+
oldVal.length = 0;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
else {
|
|
1479
|
+
if (dp.isScalar) {
|
|
1480
|
+
let newVal = DataType.parseRawValue(rawVal, dataType);
|
|
1481
|
+
target.setProperty(dp.name, newVal);
|
|
1482
|
+
}
|
|
1483
|
+
else {
|
|
1484
|
+
oldVal = target.getProperty(dp.name);
|
|
1485
|
+
if (Array.isArray(rawVal)) {
|
|
1486
|
+
// need to compare values
|
|
1487
|
+
let newVal = rawVal.map((rv) => {
|
|
1488
|
+
return DataType.parseRawValue(rv, dataType);
|
|
1489
|
+
});
|
|
1490
|
+
if (!core.arrayEquals(oldVal, newVal)) {
|
|
1491
|
+
// clear the old array and push new objects into it.
|
|
1492
|
+
oldVal.length = 0;
|
|
1493
|
+
newVal.forEach(function (nv) {
|
|
1494
|
+
oldVal.push(nv);
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
else {
|
|
1499
|
+
oldVal.length = 0;
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
});
|
|
1504
|
+
// if merging from an import then raw will have an entityAspect or a complexAspect
|
|
1505
|
+
let rawAspect = raw.entityAspect || raw.complexAspect;
|
|
1506
|
+
if (rawAspect) {
|
|
1507
|
+
let targetAspect = EntityAspect.isEntity(target) ? target.entityAspect : target.complexAspect;
|
|
1508
|
+
if (rawAspect.originalValuesMap) {
|
|
1509
|
+
targetAspect.originalValues = rawAspect.originalValuesMap;
|
|
1510
|
+
}
|
|
1511
|
+
if (rawAspect.extraMetadata) {
|
|
1512
|
+
targetAspect.extraMetadata = rawAspect.extraMetadata;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
Returns a string representation of this EntityType.
|
|
1518
|
+
*/
|
|
1519
|
+
toString() {
|
|
1520
|
+
return this.name;
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
Returns this type's metadata as a plain object in Breeze's native JSON metadata format, leaving out
|
|
1524
|
+
default values and empty arrays. Only the properties this type declares are included, not those it
|
|
1525
|
+
inherits from a base type. `JSON.stringify` calls it when {@link MetadataStore.exportMetadata}
|
|
1526
|
+
serializes the store.
|
|
1527
|
+
*/
|
|
1528
|
+
toJSON() {
|
|
1529
|
+
return core.toJson(this, {
|
|
1530
|
+
shortName: null,
|
|
1531
|
+
namespace: null,
|
|
1532
|
+
baseTypeName: null,
|
|
1533
|
+
isAbstract: false,
|
|
1534
|
+
autoGeneratedKeyType: null, // do not suppress default value
|
|
1535
|
+
defaultResourceName: null,
|
|
1536
|
+
dataProperties: localPropsOnly,
|
|
1537
|
+
navigationProperties: localPropsOnly,
|
|
1538
|
+
validators: null,
|
|
1539
|
+
custom: null
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
/** @hidden @internal */
|
|
1543
|
+
_updateNames(property) {
|
|
1544
|
+
let nc = this.metadataStore.namingConvention;
|
|
1545
|
+
updateClientServerNames(nc, property, "name");
|
|
1546
|
+
if (property.isNavigationProperty) {
|
|
1547
|
+
updateClientServerNames(nc, property, "foreignKeyNames");
|
|
1548
|
+
updateClientServerNames(nc, property, "invForeignKeyNames");
|
|
1549
|
+
// these will get set later via _updateNps
|
|
1550
|
+
// this.inverse
|
|
1551
|
+
// this.entityType
|
|
1552
|
+
// this.relatedDataProperties
|
|
1553
|
+
// dataProperty.relatedNavigationProperty
|
|
1554
|
+
// dataProperty.inverseNavigationProperty
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
/** @hidden @internal */
|
|
1558
|
+
_checkNavProperty(navigationProperty) {
|
|
1559
|
+
// if (navigationProperty.isNavigationProperty) {
|
|
1560
|
+
if (navigationProperty instanceof NavigationProperty) {
|
|
1561
|
+
if (navigationProperty.parentType !== this) {
|
|
1562
|
+
throw new Error(core.formatString("The navigationProperty '%1' is not a property of entity type '%2'", navigationProperty.name, this.name));
|
|
1563
|
+
}
|
|
1564
|
+
return navigationProperty;
|
|
1565
|
+
}
|
|
1566
|
+
if (typeof (navigationProperty) === 'string') {
|
|
1567
|
+
let np = this.getProperty(navigationProperty);
|
|
1568
|
+
// if (np && np.isNavigationProperty) return np;
|
|
1569
|
+
if (np && np instanceof NavigationProperty)
|
|
1570
|
+
return np;
|
|
1571
|
+
}
|
|
1572
|
+
throw new Error("The 'navigationProperty' parameter must either be a NavigationProperty or the name of a NavigationProperty");
|
|
1573
|
+
}
|
|
1574
|
+
/** @hidden @internal */
|
|
1575
|
+
_addDataProperty(dp) {
|
|
1576
|
+
this.dataProperties.push(dp);
|
|
1577
|
+
if (dp.isPartOfKey) {
|
|
1578
|
+
this.keyProperties.push(dp);
|
|
1579
|
+
}
|
|
1580
|
+
if (dp.isComplexProperty) {
|
|
1581
|
+
this.complexProperties.push(dp);
|
|
1582
|
+
}
|
|
1583
|
+
if (dp.concurrencyMode && dp.concurrencyMode !== "None") {
|
|
1584
|
+
this.concurrencyProperties.push(dp);
|
|
1585
|
+
}
|
|
1586
|
+
if (dp.isUnmapped) {
|
|
1587
|
+
this.unmappedProperties.push(dp);
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
/** @hidden @internal */
|
|
1591
|
+
_addNavigationProperty(np) {
|
|
1592
|
+
this.navigationProperties.push(np);
|
|
1593
|
+
if (!isQualifiedTypeName(np.entityTypeName)) {
|
|
1594
|
+
np.entityTypeName = qualifyTypeName(np.entityTypeName, this.namespace);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
/** @hidden @internal */
|
|
1598
|
+
_updateCps() {
|
|
1599
|
+
let metadataStore = this.metadataStore;
|
|
1600
|
+
let incompleteTypeMap = metadataStore._incompleteComplexTypeMap;
|
|
1601
|
+
this.complexProperties.forEach(function (cp) {
|
|
1602
|
+
if (cp.complexType)
|
|
1603
|
+
return;
|
|
1604
|
+
if (!resolveCp(cp, metadataStore)) {
|
|
1605
|
+
core.getMapArray(incompleteTypeMap, cp.complexTypeName).push(cp);
|
|
1606
|
+
}
|
|
1607
|
+
});
|
|
1608
|
+
if (this.isComplexType) {
|
|
1609
|
+
(incompleteTypeMap.get(this.name) || []).forEach(function (cp) {
|
|
1610
|
+
resolveCp(cp, metadataStore);
|
|
1611
|
+
});
|
|
1612
|
+
incompleteTypeMap.delete(this.name);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
/** @hidden @internal */
|
|
1616
|
+
_updateNps() {
|
|
1617
|
+
let metadataStore = this.metadataStore;
|
|
1618
|
+
// resolve all navProps for this entityType
|
|
1619
|
+
this.navigationProperties.forEach(function (np) {
|
|
1620
|
+
tryResolveNp(np, metadataStore);
|
|
1621
|
+
});
|
|
1622
|
+
let incompleteTypeMap = metadataStore._incompleteTypeMap;
|
|
1623
|
+
// next resolve all navProp that point to this entityType.
|
|
1624
|
+
(incompleteTypeMap.get(this.name) || []).forEach(function (np) {
|
|
1625
|
+
tryResolveNp(np, metadataStore);
|
|
1626
|
+
});
|
|
1627
|
+
// every navProp that pointed to this type should now be resolved
|
|
1628
|
+
incompleteTypeMap.delete(this.name);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
EntityType.prototype._$typeName = "EntityType";
|
|
1632
|
+
function getRawValueFromConfig(rawEntity, dp) {
|
|
1633
|
+
// 'true' fork can happen if an initializer contains an actaul instance of an already created complex object.
|
|
1634
|
+
return (rawEntity.entityAspect || rawEntity.complexAspect) ? rawEntity.getProperty(dp.name) : rawEntity[dp.name];
|
|
1635
|
+
}
|
|
1636
|
+
function updateClientServerNames(nc, parent, clientPropName) {
|
|
1637
|
+
let serverPropName = clientPropName + "OnServer";
|
|
1638
|
+
let clientName = parent[clientPropName];
|
|
1639
|
+
if (clientName && clientName.length) {
|
|
1640
|
+
// if (parent.isUnmapped) return;
|
|
1641
|
+
let serverNames = core.toArray(clientName).map(function (cName) {
|
|
1642
|
+
let sName = nc.clientPropertyNameToServer(cName, parent);
|
|
1643
|
+
let testName = nc.serverPropertyNameToClient(sName, parent);
|
|
1644
|
+
if (cName !== testName) {
|
|
1645
|
+
throw new Error("NamingConvention for this client property name does not roundtrip properly:" + cName + "-->" + testName);
|
|
1646
|
+
}
|
|
1647
|
+
return sName;
|
|
1648
|
+
});
|
|
1649
|
+
parent[serverPropName] = Array.isArray(clientName) ? serverNames : serverNames[0];
|
|
1650
|
+
}
|
|
1651
|
+
else {
|
|
1652
|
+
let serverName = parent[serverPropName];
|
|
1653
|
+
if ((!serverName) || serverName.length === 0)
|
|
1654
|
+
return;
|
|
1655
|
+
let clientNames = core.toArray(serverName).map(function (sName) {
|
|
1656
|
+
let cName = nc.serverPropertyNameToClient(sName, parent);
|
|
1657
|
+
let testName = nc.clientPropertyNameToServer(cName, parent);
|
|
1658
|
+
if (sName !== testName) {
|
|
1659
|
+
throw new Error("NamingConvention for this server property name does not roundtrip properly:" + sName + "-->" + testName);
|
|
1660
|
+
}
|
|
1661
|
+
return cName;
|
|
1662
|
+
});
|
|
1663
|
+
parent[clientPropName] = Array.isArray(serverName) ? clientNames : clientNames[0];
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
function createEmptyCtor(type) {
|
|
1667
|
+
// The only reason this ever built a function from a string was the name: a constructor made
|
|
1668
|
+
// with Function('return function Order(){}')() shows as Order in a debugger. Function.name is
|
|
1669
|
+
// configurable, so it can simply be set - and Breeze then contains no eval and no
|
|
1670
|
+
// new Function, which is what a strict Content Security Policy forbids.
|
|
1671
|
+
const ctor = function () { };
|
|
1672
|
+
Object.defineProperty(ctor, 'name', { value: type.name.replace(/\W/g, '_'), configurable: true });
|
|
1673
|
+
return ctor;
|
|
1674
|
+
}
|
|
1675
|
+
function coEquals(co1, co2) {
|
|
1676
|
+
let complexType = co1.complexAspect.parentProperty.dataType;
|
|
1677
|
+
let dataProps = complexType.dataProperties;
|
|
1678
|
+
let areEqual = dataProps.every(function (dp) {
|
|
1679
|
+
if (!dp.isSettable)
|
|
1680
|
+
return true;
|
|
1681
|
+
let v1 = co1.getProperty(dp.name);
|
|
1682
|
+
let v2 = co2.getProperty(dp.name);
|
|
1683
|
+
if (dp.isComplexProperty && dp.isScalar) {
|
|
1684
|
+
return coEquals(v1, v2);
|
|
1685
|
+
}
|
|
1686
|
+
else if (dp.isComplexProperty && !dp.isScalar) {
|
|
1687
|
+
return core.arrayEquals(v1, v2, coEquals);
|
|
1688
|
+
}
|
|
1689
|
+
else {
|
|
1690
|
+
let dataType = dp.dataType; // this will be a complexType when dp is a complexProperty
|
|
1691
|
+
return (v1 === v2 || (dataType && dataType.normalize && v1 && v2 && dataType.normalize(v1) === dataType.normalize(v2)));
|
|
1692
|
+
}
|
|
1693
|
+
});
|
|
1694
|
+
return areEqual;
|
|
1695
|
+
}
|
|
1696
|
+
function localPropsOnly(props) {
|
|
1697
|
+
return props.filter(function (prop) {
|
|
1698
|
+
return prop.baseProperty == null;
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
function resolveCp(cp, metadataStore) {
|
|
1702
|
+
let complexType = metadataStore._getStructuralType(cp.complexTypeName, true);
|
|
1703
|
+
if (!complexType)
|
|
1704
|
+
return false;
|
|
1705
|
+
if (!(complexType instanceof ComplexType)) {
|
|
1706
|
+
throw new Error("Unable to resolve ComplexType with the name: " + cp.complexTypeName + " for the property: " + cp.name);
|
|
1707
|
+
}
|
|
1708
|
+
cp.dataType = complexType;
|
|
1709
|
+
cp.defaultValue = null;
|
|
1710
|
+
return true;
|
|
1711
|
+
}
|
|
1712
|
+
function tryResolveNp(np, metadataStore) {
|
|
1713
|
+
if (np.entityType)
|
|
1714
|
+
return true;
|
|
1715
|
+
let entityType = metadataStore._getStructuralType(np.entityTypeName, true);
|
|
1716
|
+
if (entityType) {
|
|
1717
|
+
np.entityType = entityType;
|
|
1718
|
+
np._resolveNp();
|
|
1719
|
+
// don't bother removing - _updateNps will do it later.
|
|
1720
|
+
// __arrayRemoveItem(incompleteNps, np, false);
|
|
1721
|
+
}
|
|
1722
|
+
else {
|
|
1723
|
+
let incompleteNps = core.getMapArray(metadataStore._incompleteTypeMap, np.entityTypeName);
|
|
1724
|
+
core.arrayAddItemUnique(incompleteNps, np);
|
|
1725
|
+
}
|
|
1726
|
+
return !!entityType;
|
|
1727
|
+
}
|
|
1728
|
+
function calcUnmappedProperties(stype, instance) {
|
|
1729
|
+
let metadataPropNames = stype.getPropertyNames();
|
|
1730
|
+
let modelLib = config.interfaceRegistry.modelLibrary.getDefaultInstance();
|
|
1731
|
+
let trackablePropNames = modelLib.getTrackablePropertyNames(instance);
|
|
1732
|
+
trackablePropNames.forEach(function (pn) {
|
|
1733
|
+
if (metadataPropNames.indexOf(pn) === -1) {
|
|
1734
|
+
let val = instance[pn];
|
|
1735
|
+
try {
|
|
1736
|
+
if (typeof val === "function")
|
|
1737
|
+
val = val();
|
|
1738
|
+
}
|
|
1739
|
+
catch (e) {
|
|
1740
|
+
}
|
|
1741
|
+
let dt = DataType.fromValue(val);
|
|
1742
|
+
let newProp = new DataProperty({
|
|
1743
|
+
name: pn,
|
|
1744
|
+
dataType: dt,
|
|
1745
|
+
isNullable: true,
|
|
1746
|
+
isUnmapped: true
|
|
1747
|
+
});
|
|
1748
|
+
newProp.isSettable = core.isSettable(instance, pn);
|
|
1749
|
+
if (stype instanceof EntityType && stype.subtypes != null && stype.subtypes.length) {
|
|
1750
|
+
stype.getSelfAndSubtypes().forEach((st) => {
|
|
1751
|
+
st._addPropertyCore(new DataProperty(newProp));
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
else {
|
|
1755
|
+
stype._addPropertyCore(newProp);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
/** Container for all of the metadata about a specific type of Complex object.
|
|
1761
|
+
```ts
|
|
1762
|
+
const complexType = new ComplexType({
|
|
1763
|
+
shortName: "address",
|
|
1764
|
+
namespace: "myAppNamespace"
|
|
1765
|
+
});
|
|
1766
|
+
```
|
|
1767
|
+
@param config - Configuration settings
|
|
1768
|
+
*/
|
|
1769
|
+
export class ComplexType {
|
|
1770
|
+
/** For polymorphic purpose only - always true here */
|
|
1771
|
+
isComplexType = true;
|
|
1772
|
+
/** The {@link MetadataStore} containing this ComplexType. */
|
|
1773
|
+
metadataStore;
|
|
1774
|
+
/** The fully qualifed name of this ComplexType. __Read Only__ */
|
|
1775
|
+
name;
|
|
1776
|
+
/** The short, unqualified, name for this ComplexType. __Read Only__ */
|
|
1777
|
+
shortName;
|
|
1778
|
+
/** The namespace for this ComplexType. __Read Only__ */
|
|
1779
|
+
namespace;
|
|
1780
|
+
/** The DataProperties (see {@link DataProperty} associated with this ComplexType. __Read Only__ */
|
|
1781
|
+
dataProperties;
|
|
1782
|
+
/** The DataProperties for this ComplexType that contain instances of a {@link ComplexType}. __Read Only__ */
|
|
1783
|
+
complexProperties;
|
|
1784
|
+
/**
|
|
1785
|
+
The entity level validators associated with this ComplexType. Validators can be added and
|
|
1786
|
+
removed from this collection. __Read Only__ */
|
|
1787
|
+
validators;
|
|
1788
|
+
/** For polymorphic purpose only - always empty here */
|
|
1789
|
+
concurrencyProperties;
|
|
1790
|
+
/** The DataProperties associated with this ComplexType that are not mapped to any backend datastore. These are effectively free standing
|
|
1791
|
+
properties. __Read Only__ */
|
|
1792
|
+
unmappedProperties;
|
|
1793
|
+
/** Always empty: a ComplexType has no navigation properties. Present so that code can treat an
|
|
1794
|
+
EntityType and a ComplexType alike. __Read Only__ */
|
|
1795
|
+
// keyProperties and navigationProperties are not used on complexTypes - but here to allow sharing of code between EntityType and ComplexType.
|
|
1796
|
+
navigationProperties;
|
|
1797
|
+
/** Always empty: a ComplexType has no key. Present so that code can treat an EntityType and a
|
|
1798
|
+
ComplexType alike. __Read Only__ */
|
|
1799
|
+
// and may be used later to enforce uniqueness on arrays of complextypes.
|
|
1800
|
+
keyProperties;
|
|
1801
|
+
/** Not used by breeze-client 3, and never set on a ComplexType. See {@link EntityType.warnings}. */
|
|
1802
|
+
warnings;
|
|
1803
|
+
/** A function that customizes how this type's data properties are serialized when an entity holding
|
|
1804
|
+
it is saved; see {@link MetadataStore.serializerFn}, which is used when this is not set. The
|
|
1805
|
+
constructor does not accept it: assign it directly. */
|
|
1806
|
+
serializerFn;
|
|
1807
|
+
/** A free form object that can be used to define any custom metadata for this ComplexType. */
|
|
1808
|
+
custom;
|
|
1809
|
+
/** @hidden @internal */
|
|
1810
|
+
_mappedPropertiesCount;
|
|
1811
|
+
/** @hidden @internal */
|
|
1812
|
+
_extra;
|
|
1813
|
+
// copy entityType methods onto complexType
|
|
1814
|
+
/** See {@link EntityType.getCtor} */
|
|
1815
|
+
getCtor = EntityType.prototype.getCtor;
|
|
1816
|
+
/** Creates a new instance of this ComplexType, optionally setting the property values given in
|
|
1817
|
+
`initialValues`; the ComplexType counterpart of {@link EntityType.createEntity}.
|
|
1818
|
+
```ts
|
|
1819
|
+
const locType = em1.metadataStore.getAsComplexType("Location");
|
|
1820
|
+
const loc = locType.createInstance<Location>({ city: "Boston" }); // Location, unchecked
|
|
1821
|
+
```
|
|
1822
|
+
*/
|
|
1823
|
+
// note the name change.
|
|
1824
|
+
createInstance = EntityType.prototype.createEntity;
|
|
1825
|
+
/** See [EntityType.addValidator] */
|
|
1826
|
+
addValidator = EntityType.prototype.addValidator;
|
|
1827
|
+
/** Returns the {@link DataProperty} with the specified name, or null. Also accepts a `.` delimited
|
|
1828
|
+
path through nested complex properties. See {@link EntityType.getProperty}. */
|
|
1829
|
+
getProperty = EntityType.prototype.getProperty;
|
|
1830
|
+
/** Returns the properties along a `.` delimited property path, one per segment, or null if a segment
|
|
1831
|
+
is not found (unless `throwIfNotFound` is true, which throws instead). `useServerName` matches
|
|
1832
|
+
segments against `nameOnServer` when true, `name` when false, and either when null. Used by Breeze
|
|
1833
|
+
internally; applications normally use {@link ComplexType.getProperty}. */
|
|
1834
|
+
getPropertiesOnPath = EntityType.prototype.getPropertiesOnPath;
|
|
1835
|
+
/** Returns the names of all of this type's data properties. */
|
|
1836
|
+
getPropertyNames = EntityType.prototype.getPropertyNames;
|
|
1837
|
+
/** @hidden @internal */
|
|
1838
|
+
_addPropertyCore = EntityType.prototype._addPropertyCore;
|
|
1839
|
+
/** @hidden @internal */
|
|
1840
|
+
_addDataProperty = EntityType.prototype._addDataProperty;
|
|
1841
|
+
/** @hidden @internal */
|
|
1842
|
+
_updateNames = EntityType.prototype._updateNames;
|
|
1843
|
+
/** @hidden @internal */
|
|
1844
|
+
_updateCps = EntityType.prototype._updateCps;
|
|
1845
|
+
/** @hidden @internal */
|
|
1846
|
+
_initializeInstance = EntityType.prototype._initializeInstance;
|
|
1847
|
+
/** @hidden @internal */
|
|
1848
|
+
_updateTargetFromRaw = EntityType.prototype._updateTargetFromRaw;
|
|
1849
|
+
/** @hidden @internal */
|
|
1850
|
+
_setCtor = EntityType.prototype._setCtor;
|
|
1851
|
+
/** Creates a ComplexType from a {@link ComplexTypeConfig}. Add it to a {@link MetadataStore}
|
|
1852
|
+
with {@link MetadataStore.addEntityType} before using it. */
|
|
1853
|
+
constructor(config) {
|
|
1854
|
+
if (arguments.length > 1) {
|
|
1855
|
+
throw new Error("The ComplexType ctor has a single argument that is a configuration object.");
|
|
1856
|
+
}
|
|
1857
|
+
assertConfig(config)
|
|
1858
|
+
.whereParam("shortName").isNonEmptyString()
|
|
1859
|
+
.whereParam("namespace").isString().isOptional().withDefault("")
|
|
1860
|
+
.whereParam("dataProperties").isOptional()
|
|
1861
|
+
.whereParam("isComplexType").isOptional().isBoolean() // needed because this ctor can get called from the addEntityType method which needs the isComplexType prop
|
|
1862
|
+
.whereParam("custom").isOptional()
|
|
1863
|
+
.applyAll(this);
|
|
1864
|
+
this.name = qualifyTypeName(this.shortName, this.namespace);
|
|
1865
|
+
this.isComplexType = true;
|
|
1866
|
+
this.dataProperties = [];
|
|
1867
|
+
this.complexProperties = [];
|
|
1868
|
+
this.validators = [];
|
|
1869
|
+
this.concurrencyProperties = [];
|
|
1870
|
+
this.unmappedProperties = [];
|
|
1871
|
+
this._mappedPropertiesCount = 0;
|
|
1872
|
+
// keyProperties and navigationProperties are not used on complexTypes - but here to allow sharing of code between EntityType and ComplexType.
|
|
1873
|
+
this.navigationProperties = [];
|
|
1874
|
+
this.keyProperties = []; // may be used later to enforce uniqueness on arrays of complextypes.
|
|
1875
|
+
if (config.dataProperties) {
|
|
1876
|
+
addProperties(this, config.dataProperties, DataProperty);
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
/**
|
|
1880
|
+
General purpose property set method
|
|
1881
|
+
```ts
|
|
1882
|
+
// assume em1 is an EntityManager
|
|
1883
|
+
const locType = em1.metadataStore.getAsComplexType("Location");
|
|
1884
|
+
locType.setProperties({
|
|
1885
|
+
custom: { foo: 7, bar: "test" }
|
|
1886
|
+
});
|
|
1887
|
+
```
|
|
1888
|
+
@param config - Custom config object
|
|
1889
|
+
@param config.custom - {Object}
|
|
1890
|
+
*/
|
|
1891
|
+
setProperties(config) {
|
|
1892
|
+
assertConfig(config)
|
|
1893
|
+
.whereParam("custom").isOptional()
|
|
1894
|
+
.applyAll(this);
|
|
1895
|
+
}
|
|
1896
|
+
/** Returns the type level validators for this ComplexType: its own {@link ComplexType.validators}
|
|
1897
|
+
array itself, not a copy, since ComplexTypes have no base types. */
|
|
1898
|
+
getAllValidators() {
|
|
1899
|
+
// ComplexType inheritance is not YET supported.
|
|
1900
|
+
return this.validators;
|
|
1901
|
+
}
|
|
1902
|
+
/** @hidden @internal */
|
|
1903
|
+
_createInstanceCore(parent, parentProperty) {
|
|
1904
|
+
let aCtor = this.getCtor();
|
|
1905
|
+
let instance = new aCtor();
|
|
1906
|
+
new ComplexAspect(instance, parent, parentProperty);
|
|
1907
|
+
// initialization occurs during either attach or in createInstance call.
|
|
1908
|
+
return instance;
|
|
1909
|
+
}
|
|
1910
|
+
/** Adds a {@link DataProperty} to this ComplexType.
|
|
1911
|
+
```ts
|
|
1912
|
+
// assume addressType is a newly constructed ComplexType.
|
|
1913
|
+
addressType.addProperty(new DataProperty({ name: "city", dataType: DataType.String }));
|
|
1914
|
+
```
|
|
1915
|
+
*/
|
|
1916
|
+
addProperty(dataProperty) {
|
|
1917
|
+
assertParam(dataProperty, "dataProperty").isInstanceOf(DataProperty).check();
|
|
1918
|
+
return this._addPropertyCore(dataProperty);
|
|
1919
|
+
}
|
|
1920
|
+
/** Returns all of the properties of this ComplexType: its {@link ComplexType.dataProperties} array
|
|
1921
|
+
itself, not a copy. */
|
|
1922
|
+
getProperties() {
|
|
1923
|
+
return this.dataProperties;
|
|
1924
|
+
}
|
|
1925
|
+
/** Returns this type's metadata as a plain object in Breeze's native JSON metadata format, leaving
|
|
1926
|
+
out default values and empty arrays. `JSON.stringify` calls it when
|
|
1927
|
+
{@link MetadataStore.exportMetadata} serializes the store. */
|
|
1928
|
+
toJSON() {
|
|
1929
|
+
return core.toJson(this, {
|
|
1930
|
+
shortName: null,
|
|
1931
|
+
namespace: null,
|
|
1932
|
+
isComplexType: null,
|
|
1933
|
+
dataProperties: null,
|
|
1934
|
+
validators: null,
|
|
1935
|
+
custom: null
|
|
1936
|
+
});
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
ComplexType.prototype._$typeName = "ComplexType";
|
|
1940
|
+
/** Creates an instance of this complexType */
|
|
1941
|
+
ComplexType.prototype.createInstance = EntityType.prototype.createEntity;
|
|
1942
|
+
/**
|
|
1943
|
+
A DataProperty describes the metadata for a single property of an {@link EntityType} that contains simple data.
|
|
1944
|
+
|
|
1945
|
+
Instances of the DataProperty class are constructed automatically during Metadata retrieval. However it is also possible to construct them
|
|
1946
|
+
directly via the constructor.
|
|
1947
|
+
*/
|
|
1948
|
+
export class DataProperty {
|
|
1949
|
+
/** Is this a DataProperty? - always true here. Allows polymorphic treatment of DataProperties and NavigationProperties. __Read Only__ */
|
|
1950
|
+
isDataProperty = true;
|
|
1951
|
+
/** Is this a NavigationProperty? - always false here. Allows polymorphic treatment of DataProperties and NavigationProperties. __Read Only__ */
|
|
1952
|
+
isNavigationProperty = false;
|
|
1953
|
+
/** DataProperty constructor
|
|
1954
|
+
```ts
|
|
1955
|
+
const lastNameProp = new DataProperty({
|
|
1956
|
+
name: "lastName",
|
|
1957
|
+
dataType: DataType.String,
|
|
1958
|
+
isNullable: true,
|
|
1959
|
+
maxLength: 20
|
|
1960
|
+
});
|
|
1961
|
+
// assuming personEntityType is a newly constructed EntityType
|
|
1962
|
+
personEntityType.addProperty(lastNameProp);
|
|
1963
|
+
```
|
|
1964
|
+
@param config - A configuration Object or a DataProperty
|
|
1965
|
+
*/
|
|
1966
|
+
constructor(config) {
|
|
1967
|
+
assertConfig(config)
|
|
1968
|
+
.whereParam("name").isString().isOptional()
|
|
1969
|
+
.whereParam("nameOnServer").isString().isOptional()
|
|
1970
|
+
.whereParam("dataType").isEnumOf(DataType).isOptional().or().isString().or().isInstanceOf(ComplexType)
|
|
1971
|
+
.whereParam("complexTypeName").isOptional()
|
|
1972
|
+
.whereParam("isNullable").isBoolean().isOptional().withDefault(true)
|
|
1973
|
+
.whereParam("isScalar").isOptional().withDefault(true) // will be false for some NoSQL databases.
|
|
1974
|
+
.whereParam("defaultValue").isOptional()
|
|
1975
|
+
.whereParam("isPartOfKey").isBoolean().isOptional()
|
|
1976
|
+
.whereParam("isUnmapped").isBoolean().isOptional()
|
|
1977
|
+
.whereParam("isSettable").isBoolean().isOptional().withDefault(true)
|
|
1978
|
+
.whereParam("concurrencyMode").isString().isOptional()
|
|
1979
|
+
.whereParam("maxLength").isNumber().isOptional()
|
|
1980
|
+
.whereParam("validators").isInstanceOf(Validator).isArray().isOptional().withDefault([])
|
|
1981
|
+
.whereParam("displayName").isOptional()
|
|
1982
|
+
.whereParam("enumType").isOptional()
|
|
1983
|
+
.whereParam("rawTypeName").isOptional() // occurs with undefined datatypes
|
|
1984
|
+
.whereParam("custom").isOptional()
|
|
1985
|
+
.applyAll(this);
|
|
1986
|
+
let hasName = !!(this.name || this.nameOnServer);
|
|
1987
|
+
if (!hasName) {
|
|
1988
|
+
throw new Error("A DataProperty must be instantiated with either a 'name' or a 'nameOnServer' property");
|
|
1989
|
+
}
|
|
1990
|
+
// name/nameOnServer is resolved later when a metadataStore is available.
|
|
1991
|
+
if (this.complexTypeName) {
|
|
1992
|
+
this.isComplexProperty = true;
|
|
1993
|
+
// this.dataType = null; // TODO: would like to remove this line because dataType will be set later.
|
|
1994
|
+
}
|
|
1995
|
+
else if (typeof (this.dataType) === "string") {
|
|
1996
|
+
let dt = DataType.fromName(this.dataType);
|
|
1997
|
+
if (!dt) {
|
|
1998
|
+
throw new Error("Unable to find a DataType enumeration by the name of: " + this.dataType);
|
|
1999
|
+
}
|
|
2000
|
+
this.dataType = dt;
|
|
2001
|
+
}
|
|
2002
|
+
else if (!this.dataType) {
|
|
2003
|
+
this.dataType = DataType.String;
|
|
2004
|
+
}
|
|
2005
|
+
// == as opposed to === is deliberate here.
|
|
2006
|
+
if (this.defaultValue == null) {
|
|
2007
|
+
if (this.isNullable) {
|
|
2008
|
+
this.defaultValue = null;
|
|
2009
|
+
}
|
|
2010
|
+
else {
|
|
2011
|
+
if (this.isComplexProperty) {
|
|
2012
|
+
// what to do? - shouldn't happen from EF - but otherwise ???
|
|
2013
|
+
}
|
|
2014
|
+
else if (this.dataType === DataType.Binary) {
|
|
2015
|
+
this.defaultValue = "AAAAAAAAJ3U="; // hack for all binary fields but value is specifically valid for timestamp fields - arbitrary valid 8 byte base64 value.
|
|
2016
|
+
}
|
|
2017
|
+
else {
|
|
2018
|
+
this.defaultValue = this.dataType.defaultValue;
|
|
2019
|
+
const msg = "A nonnullable DataProperty cannot have a null defaultValue. Name: " + (this.name || this.nameOnServer);
|
|
2020
|
+
if (this.defaultValue === null) {
|
|
2021
|
+
throw new Error(msg); // if defaultValue is explicity set to null, that's an error
|
|
2022
|
+
}
|
|
2023
|
+
else if (this.defaultValue == null) {
|
|
2024
|
+
console.warn(msg); // if defaultValue is not set, that's a warning
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
else if (this.dataType.isNumeric) {
|
|
2030
|
+
// in case the defaultValue comes in as a string ( which it does in EF6).
|
|
2031
|
+
if (typeof (this.defaultValue) === "string") {
|
|
2032
|
+
this.defaultValue = parseFloat(this.defaultValue);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
if (this.isComplexProperty) {
|
|
2036
|
+
this.isScalar = this.isScalar == null || this.isScalar === true;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
/**
|
|
2040
|
+
Reads a property's value from a raw entity whose keys are server property names, such as a node
|
|
2041
|
+
in query results. Returns the property's `defaultValue` when the node has no value for it; for an
|
|
2042
|
+
unmapped property it reads `nameOnServer`, falling back to `name`, and applies no default. Pass it as
|
|
2043
|
+
the `rawValueFn` of {@link EntityType.getEntityKeyFromRawEntity} when writing an adapter;
|
|
2044
|
+
applications do not normally need it.
|
|
2045
|
+
*/
|
|
2046
|
+
static getRawValueFromServer(rawEntity, dp) {
|
|
2047
|
+
if (dp.isUnmapped) {
|
|
2048
|
+
return rawEntity[dp.nameOnServer || dp.name];
|
|
2049
|
+
}
|
|
2050
|
+
else {
|
|
2051
|
+
let val = rawEntity[dp.nameOnServer];
|
|
2052
|
+
return val !== undefined ? val : dp.defaultValue;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
/**
|
|
2056
|
+
Reads a property's value from a raw entity whose keys are client property names, such as an entity
|
|
2057
|
+
in the output of {@link EntityManager.exportEntities}. Returns the property's `defaultValue` when the
|
|
2058
|
+
raw entity has no value for it. The client-name counterpart of
|
|
2059
|
+
{@link DataProperty.getRawValueFromServer}; applications do not normally need it.
|
|
2060
|
+
*/
|
|
2061
|
+
static getRawValueFromClient(rawEntity, dp) {
|
|
2062
|
+
let val = rawEntity[dp.name];
|
|
2063
|
+
return val !== undefined ? val : dp.defaultValue;
|
|
2064
|
+
}
|
|
2065
|
+
/**
|
|
2066
|
+
Returns the value of the named field of this property, such as `"displayName"`. If it is null or
|
|
2067
|
+
undefined here, the value is taken from the property this one inherits from on a base type (see
|
|
2068
|
+
{@link DataProperty.baseProperty}), and so on up the hierarchy. Validation uses it to find a
|
|
2069
|
+
property's display name.
|
|
2070
|
+
*/
|
|
2071
|
+
resolveProperty(propName) {
|
|
2072
|
+
let result = this[propName];
|
|
2073
|
+
let baseProp = this.baseProperty;
|
|
2074
|
+
while (result == undefined && baseProp != null) {
|
|
2075
|
+
result = baseProp[propName];
|
|
2076
|
+
baseProp = baseProp.baseProperty;
|
|
2077
|
+
}
|
|
2078
|
+
return result;
|
|
2079
|
+
}
|
|
2080
|
+
/** Returns a name that identifies this property across the store: the qualified name of its parent
|
|
2081
|
+
type and the property name, joined by `--`. Breeze uses it in error messages and to generate
|
|
2082
|
+
association names; applications do not normally need it. */
|
|
2083
|
+
formatName() {
|
|
2084
|
+
return this.parentType.name + "--" + this.name;
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
General purpose property set method
|
|
2088
|
+
```ts
|
|
2089
|
+
// assume myEntityType is an EntityType
|
|
2090
|
+
const prop = myEntityType.getProperty("myProperty");
|
|
2091
|
+
prop.setProperties({
|
|
2092
|
+
custom: { foo: 7, bar: "test" }
|
|
2093
|
+
});
|
|
2094
|
+
```
|
|
2095
|
+
@param config - A configuration object.
|
|
2096
|
+
*/
|
|
2097
|
+
setProperties(config) {
|
|
2098
|
+
assertConfig(config)
|
|
2099
|
+
.whereParam("displayName").isOptional()
|
|
2100
|
+
.whereParam("custom").isOptional()
|
|
2101
|
+
.applyAll(this);
|
|
2102
|
+
}
|
|
2103
|
+
/** Returns the validators that apply to this property: its own {@link DataProperty.validators}
|
|
2104
|
+
followed by those of the properties it inherits from on base types. Returns a new array. */
|
|
2105
|
+
getAllValidators() {
|
|
2106
|
+
let validators = this.validators.slice(0);
|
|
2107
|
+
let baseProp = this.baseProperty;
|
|
2108
|
+
while (baseProp) {
|
|
2109
|
+
validators.push.apply(validators, baseProp.validators);
|
|
2110
|
+
baseProp = baseProp.baseProperty;
|
|
2111
|
+
}
|
|
2112
|
+
return validators;
|
|
2113
|
+
}
|
|
2114
|
+
/** Returns this property's metadata as a plain object in Breeze's native JSON metadata format,
|
|
2115
|
+
leaving out default values; a complex property is described by its `complexTypeName`. Used when
|
|
2116
|
+
{@link MetadataStore.exportMetadata} serializes the store. */
|
|
2117
|
+
toJSON() {
|
|
2118
|
+
// do not serialize dataTypes that are complexTypes
|
|
2119
|
+
return core.toJson(this, {
|
|
2120
|
+
name: null,
|
|
2121
|
+
dataType: function (v) {
|
|
2122
|
+
return (v && v instanceof DataType) ? v.name : undefined;
|
|
2123
|
+
}, // do not serialize dataTypes that are complexTypes
|
|
2124
|
+
complexTypeName: null,
|
|
2125
|
+
isNullable: true,
|
|
2126
|
+
defaultValue: null,
|
|
2127
|
+
isPartOfKey: false,
|
|
2128
|
+
isUnmapped: false,
|
|
2129
|
+
isSettable: true,
|
|
2130
|
+
concurrencyMode: null,
|
|
2131
|
+
maxLength: null,
|
|
2132
|
+
validators: null,
|
|
2133
|
+
displayName: null,
|
|
2134
|
+
enumType: null,
|
|
2135
|
+
rawTypeName: null,
|
|
2136
|
+
isScalar: true,
|
|
2137
|
+
custom: null
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
/** Creates a DataProperty from the JSON form that {@link DataProperty.toJSON} produces. Used by
|
|
2141
|
+
{@link MetadataStore.importMetadata}. A `dataType` name the client does not know becomes
|
|
2142
|
+
{@link DataType.Undefined}, with the name kept in `rawTypeName` and a console warning. Modifies the
|
|
2143
|
+
object passed in. */
|
|
2144
|
+
static fromJSON(json) {
|
|
2145
|
+
const typeName = json.dataType;
|
|
2146
|
+
json.dataType = DataType.fromName(typeName);
|
|
2147
|
+
if (typeName && !json.dataType) {
|
|
2148
|
+
// A type the client does not know, from a newer server say. Keep the property, as 'Undefined' with the server's
|
|
2149
|
+
// name in 'rawTypeName' - the way the server itself sends spatial types - rather than pass it off as a String.
|
|
2150
|
+
json.dataType = DataType.Undefined;
|
|
2151
|
+
json.rawTypeName = json.rawTypeName || typeName;
|
|
2152
|
+
warnUnknownDataType(typeName);
|
|
2153
|
+
}
|
|
2154
|
+
// Parse default value into correct data type. (dateTime instances require extra work to deserialize properly.)
|
|
2155
|
+
if (json.defaultValue && json.dataType && json.dataType.parse) {
|
|
2156
|
+
json.defaultValue = json.dataType.parse(json.defaultValue, typeof json.defaultValue);
|
|
2157
|
+
}
|
|
2158
|
+
if (json.validators) {
|
|
2159
|
+
json.validators = json.validators.map(Validator.fromJSON);
|
|
2160
|
+
}
|
|
2161
|
+
return new DataProperty(json);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
DataProperty.prototype._$typeName = "DataProperty";
|
|
2165
|
+
const _unknownDataTypeNames = new Set();
|
|
2166
|
+
function warnUnknownDataType(typeName) {
|
|
2167
|
+
if (_unknownDataTypeNames.has(typeName))
|
|
2168
|
+
return;
|
|
2169
|
+
_unknownDataTypeNames.add(typeName);
|
|
2170
|
+
console.warn("Metadata names a data type the client does not know: '" + typeName +
|
|
2171
|
+
"'. Its properties are imported as DataType.Undefined, with the name in 'rawTypeName'.");
|
|
2172
|
+
}
|
|
2173
|
+
/** A NavigationProperty describes the metadata for a single property of an {@link EntityType} that return instances of other EntityTypes.
|
|
2174
|
+
|
|
2175
|
+
Instances of the NavigationProperty class are constructed automatically during Metadata retrieval. However it is also possible to construct them
|
|
2176
|
+
directly via the constructor.
|
|
2177
|
+
*/
|
|
2178
|
+
export class NavigationProperty {
|
|
2179
|
+
/** Is this a DataProperty? - always false here
|
|
2180
|
+
Allows polymorphic treatment of DataProperties and NavigationProperties. __Read Only__ */
|
|
2181
|
+
isDataProperty = false;
|
|
2182
|
+
/** Is this a NavigationProperty? - always true here
|
|
2183
|
+
Allows polymorphic treatment of DataProperties and NavigationProperties. __Read Only__ */
|
|
2184
|
+
isNavigationProperty = true;
|
|
2185
|
+
/** Returns the qualified name of the parent type and the property name, joined by `--`. See
|
|
2186
|
+
{@link DataProperty.formatName}; applications do not normally need it. */
|
|
2187
|
+
formatName = DataProperty.prototype.formatName;
|
|
2188
|
+
/** Returns the validators that apply to this property, including those inherited from base types.
|
|
2189
|
+
See {@link DataProperty.getAllValidators}. */
|
|
2190
|
+
getAllValidators = DataProperty.prototype.getAllValidators;
|
|
2191
|
+
/** Returns the value of the named field of this property, falling back to the property it inherits
|
|
2192
|
+
from on a base type. See {@link DataProperty.resolveProperty}. */
|
|
2193
|
+
resolveProperty = DataProperty.prototype.resolveProperty;
|
|
2194
|
+
/** NavigationProperty constructor
|
|
2195
|
+
```ts
|
|
2196
|
+
const homeAddressProp = new NavigationProperty({
|
|
2197
|
+
name: "homeAddress",
|
|
2198
|
+
entityTypeName: "Address:#myNamespace",
|
|
2199
|
+
isScalar: true,
|
|
2200
|
+
associationName: "address_person",
|
|
2201
|
+
foreignKeyNames: ["homeAddressId"]
|
|
2202
|
+
});
|
|
2203
|
+
const homeAddressIdProp = new DataProperty({
|
|
2204
|
+
name: "homeAddressId",
|
|
2205
|
+
dataType: DataType.Int32
|
|
2206
|
+
});
|
|
2207
|
+
// assuming personEntityType is a newly constructed EntityType
|
|
2208
|
+
personEntityType.addProperty(homeAddressProp);
|
|
2209
|
+
personEntityType.addProperty(homeAddressIdProp);
|
|
2210
|
+
```
|
|
2211
|
+
@param config - A configuration object.
|
|
2212
|
+
*/
|
|
2213
|
+
constructor(config) {
|
|
2214
|
+
assertConfig(config)
|
|
2215
|
+
.whereParam("name").isString().isOptional()
|
|
2216
|
+
.whereParam("nameOnServer").isString().isOptional()
|
|
2217
|
+
.whereParam("entityTypeName").isString()
|
|
2218
|
+
.whereParam("isScalar").isBoolean().isOptional().withDefault(true)
|
|
2219
|
+
.whereParam("associationName").isString().isOptional()
|
|
2220
|
+
.whereParam("foreignKeyNames").isArray().isString().isOptional().withDefault([])
|
|
2221
|
+
.whereParam("foreignKeyNamesOnServer").isArray().isString().isOptional().withDefault([])
|
|
2222
|
+
.whereParam("invForeignKeyNames").isArray().isString().isOptional().withDefault([])
|
|
2223
|
+
.whereParam("invForeignKeyNamesOnServer").isArray().isString().isOptional().withDefault([])
|
|
2224
|
+
.whereParam("validators").isInstanceOf(Validator).isArray().isOptional().withDefault([])
|
|
2225
|
+
.whereParam("displayName").isOptional()
|
|
2226
|
+
.whereParam("custom").isOptional()
|
|
2227
|
+
.applyAll(this);
|
|
2228
|
+
let hasName = !!(this.name || this.nameOnServer);
|
|
2229
|
+
if (!hasName) {
|
|
2230
|
+
throw new Error("A Navigation property must be instantiated with either a 'name' or a 'nameOnServer' property");
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
/**
|
|
2234
|
+
General purpose property set method
|
|
2235
|
+
```ts
|
|
2236
|
+
// assume myEntityType is an EntityType
|
|
2237
|
+
const prop = myEntityType.getProperty("myProperty");
|
|
2238
|
+
prop.setProperties({
|
|
2239
|
+
custom: { foo: 7, bar: "test" }
|
|
2240
|
+
});
|
|
2241
|
+
```
|
|
2242
|
+
@param config - A config object
|
|
2243
|
+
*/
|
|
2244
|
+
// TODO: create an interface for this.
|
|
2245
|
+
setProperties(config) {
|
|
2246
|
+
if (!this.parentType) {
|
|
2247
|
+
throw new Error("Cannot call NavigationProperty.setProperties until the parent EntityType of the NavigationProperty has been set.");
|
|
2248
|
+
}
|
|
2249
|
+
let inverse = config.inverse;
|
|
2250
|
+
if (inverse)
|
|
2251
|
+
delete config.inverse;
|
|
2252
|
+
assertConfig(config)
|
|
2253
|
+
.whereParam("displayName").isOptional()
|
|
2254
|
+
.whereParam("foreignKeyNames").isArray().isString().isOptional().withDefault([])
|
|
2255
|
+
.whereParam("invForeignKeyNames").isArray().isString().isOptional().withDefault([])
|
|
2256
|
+
.whereParam("custom").isOptional()
|
|
2257
|
+
.applyAll(this);
|
|
2258
|
+
this.parentType._updateNames(this);
|
|
2259
|
+
this._resolveNp();
|
|
2260
|
+
if (inverse) {
|
|
2261
|
+
this.setInverse(inverse);
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
/** The inverse of this NavigationProperty. The NavigationProperty that represents a navigation in the opposite direction
|
|
2265
|
+
to this NavigationProperty. May be undefined for a undirectional NavigationProperty. __Read Only__ */
|
|
2266
|
+
get inverse() {
|
|
2267
|
+
return this.getInverse();
|
|
2268
|
+
}
|
|
2269
|
+
/** @hidden @internal */
|
|
2270
|
+
getInverse() {
|
|
2271
|
+
let np = this;
|
|
2272
|
+
while (!np._inverse && np.baseProperty) {
|
|
2273
|
+
np = np.baseProperty;
|
|
2274
|
+
}
|
|
2275
|
+
return np._inverse;
|
|
2276
|
+
}
|
|
2277
|
+
/**
|
|
2278
|
+
Makes the specified navigation property, on the type this property returns, the inverse of this one.
|
|
2279
|
+
Both get the same {@link NavigationProperty.associationName}: this property's if it has one, otherwise
|
|
2280
|
+
the other's, otherwise a generated one. For metadata built by hand; metadata from the server already
|
|
2281
|
+
pairs inverses by association name. Also available as the `inverse` option of
|
|
2282
|
+
{@link NavigationProperty.setProperties}. Both types must already be in the same MetadataStore.
|
|
2283
|
+
@param inverseNp - The inverse NavigationProperty, or its name on the type this property returns.
|
|
2284
|
+
Throws if it is not found, does not point back at this property's parent type, or either property
|
|
2285
|
+
already has an inverse.
|
|
2286
|
+
*/
|
|
2287
|
+
setInverse(inverseNp) {
|
|
2288
|
+
// let invNp: NavigationProperty;
|
|
2289
|
+
let invNp = (inverseNp instanceof NavigationProperty) ? inverseNp : this.entityType.getNavigationProperty(inverseNp);
|
|
2290
|
+
if (!invNp) {
|
|
2291
|
+
throw throwSetInverseError(this, "Unable to find inverse property: " + inverseNp);
|
|
2292
|
+
}
|
|
2293
|
+
if (this._inverse || invNp._inverse) {
|
|
2294
|
+
throwSetInverseError(this, "It has already been set on one side or the other.");
|
|
2295
|
+
}
|
|
2296
|
+
if (invNp.entityType !== this.parentType) {
|
|
2297
|
+
throwSetInverseError(this, invNp.formatName() + " is not a valid inverse property for this.");
|
|
2298
|
+
}
|
|
2299
|
+
if (this.associationName) {
|
|
2300
|
+
invNp.associationName = this.associationName;
|
|
2301
|
+
}
|
|
2302
|
+
else {
|
|
2303
|
+
if (!invNp.associationName) {
|
|
2304
|
+
invNp.associationName = this.formatName() + "_" + invNp.formatName();
|
|
2305
|
+
}
|
|
2306
|
+
this.associationName = invNp.associationName;
|
|
2307
|
+
}
|
|
2308
|
+
this._resolveNp();
|
|
2309
|
+
invNp._resolveNp();
|
|
2310
|
+
}
|
|
2311
|
+
// // In progress - will be used for manual metadata config
|
|
2312
|
+
// createInverse(config: any) {
|
|
2313
|
+
// if (!this.entityType) {
|
|
2314
|
+
// throwCreateInverseError(this, "has not yet been defined.");
|
|
2315
|
+
// }
|
|
2316
|
+
// if (this.entityType.isFrozen) {
|
|
2317
|
+
// throwCreateInverseError(this, "is frozen.");
|
|
2318
|
+
// }
|
|
2319
|
+
// let metadataStore = this.entityType.metadataStore;
|
|
2320
|
+
// if (metadataStore == null) {
|
|
2321
|
+
// throwCreateInverseError(this, "has not yet been added to the metadataStore.");
|
|
2322
|
+
// }
|
|
2323
|
+
// config.entityTypeName = this.parentEntityType.name;
|
|
2324
|
+
// config.associationName = this.associationName;
|
|
2325
|
+
// let invNp = new NavigationProperty(config);
|
|
2326
|
+
// this.parentEntityType.addNavigationProperty(invNp);
|
|
2327
|
+
// return invNp;
|
|
2328
|
+
// };
|
|
2329
|
+
/** Returns this property's metadata as a plain object in Breeze's native JSON metadata format,
|
|
2330
|
+
leaving out default values. Only client-side names are written. Used when
|
|
2331
|
+
{@link MetadataStore.exportMetadata} serializes the store. */
|
|
2332
|
+
toJSON() {
|
|
2333
|
+
return core.toJson(this, {
|
|
2334
|
+
name: null,
|
|
2335
|
+
entityTypeName: null,
|
|
2336
|
+
isScalar: null,
|
|
2337
|
+
associationName: null,
|
|
2338
|
+
validators: null,
|
|
2339
|
+
displayName: null,
|
|
2340
|
+
foreignKeyNames: null,
|
|
2341
|
+
invForeignKeyNames: null,
|
|
2342
|
+
custom: null
|
|
2343
|
+
});
|
|
2344
|
+
}
|
|
2345
|
+
/** Creates a NavigationProperty from the JSON form that {@link NavigationProperty.toJSON} produces.
|
|
2346
|
+
Used by {@link MetadataStore.importMetadata}. Modifies the object passed in. */
|
|
2347
|
+
static fromJSON(json) {
|
|
2348
|
+
if (json.validators) {
|
|
2349
|
+
json.validators = json.validators.map(Validator.fromJSON);
|
|
2350
|
+
}
|
|
2351
|
+
return new NavigationProperty(json);
|
|
2352
|
+
}
|
|
2353
|
+
/** @hidden @internal */
|
|
2354
|
+
_resolveNp() {
|
|
2355
|
+
let np = this;
|
|
2356
|
+
let entityType = np.entityType;
|
|
2357
|
+
let invNp = core.arrayFirst(entityType.navigationProperties, (altNp) => {
|
|
2358
|
+
// Can't do this because of possibility of comparing a base class np with a subclass altNp.
|
|
2359
|
+
// return altNp.associationName === np.associationName
|
|
2360
|
+
// && altNp !== np;
|
|
2361
|
+
// So use this instead.
|
|
2362
|
+
return altNp.associationName === np.associationName &&
|
|
2363
|
+
(altNp.name !== np.name || altNp.entityTypeName !== np.entityTypeName);
|
|
2364
|
+
});
|
|
2365
|
+
np._inverse = invNp || undefined;
|
|
2366
|
+
//if (invNp && invNp.inverse == null) {
|
|
2367
|
+
// invNp._resolveNp();
|
|
2368
|
+
//}
|
|
2369
|
+
if (!invNp) {
|
|
2370
|
+
// unidirectional 1-n relationship
|
|
2371
|
+
np.invForeignKeyNames.forEach(function (invFkName) {
|
|
2372
|
+
let fkProp = entityType.getDataProperty(invFkName);
|
|
2373
|
+
if (fkProp == null) {
|
|
2374
|
+
throw new Error("EntityType '" + np.entityTypeName + "' has no foreign key matching '" + invFkName + "'");
|
|
2375
|
+
}
|
|
2376
|
+
let invEntityType = np.parentType;
|
|
2377
|
+
invNp = core.arrayFirst(invEntityType.navigationProperties, (np2) => {
|
|
2378
|
+
return np2.invForeignKeyNames && np2.invForeignKeyNames.indexOf(fkProp.name) >= 0 && np2.entityType === fkProp.parentType;
|
|
2379
|
+
});
|
|
2380
|
+
fkProp.inverseNavigationProperty = invNp || undefined;
|
|
2381
|
+
core.arrayAddItemUnique(entityType.foreignKeyProperties, fkProp);
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2384
|
+
resolveRelated(np);
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
NavigationProperty.prototype._$typeName = "NavigationProperty";
|
|
2388
|
+
function throwSetInverseError(np, message) {
|
|
2389
|
+
throw new Error("Cannot set the inverse property for: " + np.formatName() + ". " + message);
|
|
2390
|
+
}
|
|
2391
|
+
// Not current used.
|
|
2392
|
+
// function throwCreateInverseError(np: NavigationProperty, message: string) {
|
|
2393
|
+
// throw new Error("Cannot create inverse for: " + np.formatName() + ". The entityType for this navigation property " + message);
|
|
2394
|
+
// }
|
|
2395
|
+
// sets navigation property: relatedDataProperties and dataProperty: relatedNavigationProperty
|
|
2396
|
+
function resolveRelated(np) {
|
|
2397
|
+
let fkNames = np.foreignKeyNames;
|
|
2398
|
+
if (fkNames.length === 0)
|
|
2399
|
+
return;
|
|
2400
|
+
let parentEntityType = np.parentType;
|
|
2401
|
+
let fkProps = fkNames.map(function (fkName) {
|
|
2402
|
+
return parentEntityType.getDataProperty(fkName);
|
|
2403
|
+
});
|
|
2404
|
+
let fkPropCollection = parentEntityType.foreignKeyProperties;
|
|
2405
|
+
fkProps.forEach((dp) => {
|
|
2406
|
+
core.arrayAddItemUnique(fkPropCollection, dp);
|
|
2407
|
+
dp.relatedNavigationProperty = np;
|
|
2408
|
+
// now update the inverse
|
|
2409
|
+
core.arrayAddItemUnique(np.entityType.inverseForeignKeyProperties, dp);
|
|
2410
|
+
if (np.relatedDataProperties) {
|
|
2411
|
+
core.arrayAddItemUnique(np.relatedDataProperties, dp);
|
|
2412
|
+
}
|
|
2413
|
+
else {
|
|
2414
|
+
np.relatedDataProperties = [dp];
|
|
2415
|
+
}
|
|
2416
|
+
});
|
|
2417
|
+
}
|
|
2418
|
+
/**
|
|
2419
|
+
AutoGeneratedKeyType is an 'Enum' containing all of the valid states for an automatically generated key.
|
|
2420
|
+
*/
|
|
2421
|
+
export class AutoGeneratedKeyType extends BreezeEnum {
|
|
2422
|
+
/**
|
|
2423
|
+
This entity does not have an autogenerated key.
|
|
2424
|
+
The client must set the key before adding the entity to the EntityManager
|
|
2425
|
+
*/
|
|
2426
|
+
static None = new AutoGeneratedKeyType();
|
|
2427
|
+
/**
|
|
2428
|
+
This entity's key is an Identity column and is set by the backend database.
|
|
2429
|
+
Keys for new entities will be temporary until the entities are saved at which point the keys will
|
|
2430
|
+
be converted to their 'real' versions.
|
|
2431
|
+
*/
|
|
2432
|
+
static Identity = new AutoGeneratedKeyType();
|
|
2433
|
+
/**
|
|
2434
|
+
This entity's key is generated by a KeyGenerator and is set by the backend database.
|
|
2435
|
+
Keys for new entities will be temporary until the entities are saved at which point the keys will
|
|
2436
|
+
be converted to their 'real' versions.
|
|
2437
|
+
*/
|
|
2438
|
+
static KeyGenerator = new AutoGeneratedKeyType();
|
|
2439
|
+
}
|
|
2440
|
+
AutoGeneratedKeyType.prototype._$typeName = "AutoGeneratedKeyType";
|
|
2441
|
+
AutoGeneratedKeyType.resolveSymbols();
|
|
2442
|
+
// functions shared between classes related to Metadata
|
|
2443
|
+
function isQualifiedTypeName(entityTypeName) {
|
|
2444
|
+
return entityTypeName.indexOf(":#") >= 0;
|
|
2445
|
+
}
|
|
2446
|
+
export function qualifyTypeName(shortName, ns) {
|
|
2447
|
+
if (ns && ns.length > 0) {
|
|
2448
|
+
return shortName + ":#" + ns;
|
|
2449
|
+
}
|
|
2450
|
+
else {
|
|
2451
|
+
return shortName;
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
/**
|
|
2455
|
+
The {@link EntityType} a constructor stands for.
|
|
2456
|
+
|
|
2457
|
+
{@link MetadataStore.registerEntityTypeCtor} puts the type on the class's prototype, and that is
|
|
2458
|
+
what the constructor-taking overloads of `EntityQuery.from`, `EntityManager.createEntity` and the
|
|
2459
|
+
rest read. A class that was never registered has nothing to read, so this throws rather than
|
|
2460
|
+
letting the caller build a query against `undefined`.
|
|
2461
|
+
|
|
2462
|
+
```ts
|
|
2463
|
+
em.metadataStore.registerEntityTypeCtor('Customer', Customer);
|
|
2464
|
+
entityTypeForCtor(Customer).defaultResourceName; // 'Customers'
|
|
2465
|
+
```
|
|
2466
|
+
@param entityCtor - A constructor registered with a MetadataStore.
|
|
2467
|
+
*/
|
|
2468
|
+
export function entityTypeForCtor(entityCtor) {
|
|
2469
|
+
const entityType = entityCtor && entityCtor.prototype && entityCtor.prototype.entityType;
|
|
2470
|
+
if (!entityType) {
|
|
2471
|
+
const name = (entityCtor && entityCtor.name) || String(entityCtor);
|
|
2472
|
+
throw new Error(core.formatString("'%1' is not registered with a MetadataStore, so its EntityType is unknown. Call " +
|
|
2473
|
+
"metadataStore.registerEntityTypeCtor('%1', %1) before using the constructor with this API.", name));
|
|
2474
|
+
}
|
|
2475
|
+
return entityType;
|
|
2476
|
+
}
|
|
2477
|
+
// Used by both ComplexType and EntityType
|
|
2478
|
+
function addProperties(entityType, propObj, ctor) {
|
|
2479
|
+
if (propObj == null)
|
|
2480
|
+
return;
|
|
2481
|
+
if (Array.isArray(propObj)) {
|
|
2482
|
+
// Each item is a DataProperty/NavigationProperty, or the config to make one from. (Not
|
|
2483
|
+
// forEach(_addPropertyCore): that passed each item's index as `shouldResolve`.)
|
|
2484
|
+
for (const item of propObj) {
|
|
2485
|
+
entityType._addPropertyCore(item instanceof ctor ? item : new ctor(item));
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
else if (typeof (propObj) === 'object') {
|
|
2489
|
+
for (let key in propObj) {
|
|
2490
|
+
if (hasOwnProp(propObj, key)) {
|
|
2491
|
+
// A copy with the name added: the caller's config object is left as it was.
|
|
2492
|
+
let value = propObj[key];
|
|
2493
|
+
let prop = new ctor({ ...value, name: key });
|
|
2494
|
+
entityType._addPropertyCore(prop);
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
else {
|
|
2499
|
+
throw new Error("The 'dataProperties' or 'navigationProperties' values must be either an array of data/nav properties or an object where each property defines a data/nav property");
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
//# sourceMappingURL=entity-metadata.js.map
|