angular-odata 0.161.0 → 0.163.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/README.md +165 -7
- package/fesm2022/angular-odata.mjs +116 -43
- package/fesm2022/angular-odata.mjs.map +1 -1
- package/package.json +1 -1
- package/schematics/apigen/angular/base.js +7 -7
- package/schematics/apigen/angular/base.js.map +1 -1
- package/schematics/apigen/angular/entity.d.ts +2 -1
- package/schematics/apigen/angular/entity.js +6 -2
- package/schematics/apigen/angular/entity.js.map +1 -1
- package/schematics/apigen/angular/model.d.ts +2 -1
- package/schematics/apigen/angular/model.js +7 -6
- package/schematics/apigen/angular/model.js.map +1 -1
- package/schematics/apigen/angular/package.js.map +1 -1
- package/schematics/apigen/files/entity/__fileName__.ts +1 -1
- package/schematics/apigen/files/model/__fileName__.ts +2 -2
- package/schematics/metadata/index.js +4 -2
- package/schematics/metadata/index.js.map +1 -1
- package/schematics/metadata/parser/csdl/csdl-annotation.d.ts +7 -3
- package/schematics/metadata/parser/csdl/csdl-annotation.js +11 -3
- package/schematics/metadata/parser/csdl/csdl-annotation.js.map +1 -1
- package/schematics/metadata/parser/csdl/csdl-function-action.d.ts +2 -13
- package/schematics/metadata/parser/csdl/csdl-function-action.js +35 -8
- package/schematics/metadata/parser/csdl/csdl-function-action.js.map +1 -1
- package/schematics/metadata/parser/csdl/csdl-structural-property.d.ts +1 -0
- package/schematics/metadata/parser/csdl/csdl-structural-property.js +6 -1
- package/schematics/metadata/parser/csdl/csdl-structural-property.js.map +1 -1
- package/schematics/metadata/parser/index.js +2 -2
- package/schematics/metadata/parser/index.js.map +1 -1
- package/types/angular-odata.d.ts +33 -26
package/README.md
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
<h1 align="center">Angular OData</h1>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<em>A fluent
|
|
4
|
+
<em>A typed, fluent client for OData services in Angular.</em>
|
|
5
5
|
<br>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
|
-
<a href="
|
|
9
|
+
<a href="https://diegomvh.github.io/angular-odata/docs/api/additional-documentation/introduction.html">Handbook</a>
|
|
10
10
|
·
|
|
11
|
-
<a href="
|
|
11
|
+
<a href="https://diegomvh.github.io/angular-odata/docs/api/index.html">API reference</a>
|
|
12
12
|
·
|
|
13
13
|
<a href="https://github.com/diegomvh/AngularODataEntity">Demo</a>
|
|
14
|
+
·
|
|
15
|
+
<a href="https://github.com/diegomvh/angular-odata/blob/main/CONTRIBUTING.md">Contributing</a>
|
|
14
16
|
<br>
|
|
15
17
|
<br>
|
|
16
18
|
</p>
|
|
@@ -26,14 +28,170 @@
|
|
|
26
28
|
|
|
27
29
|
<hr>
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
[OData](https://www.odata.org/) is a standard for REST APIs: every service describes its
|
|
32
|
+
data in a `$metadata` document and accepts the same query options (`$filter`, `$select`,
|
|
33
|
+
`$expand`, `$orderby`, …). **angular-odata** lets your Angular application talk to any OData
|
|
34
|
+
service without writing URLs by hand: you build requests with typed TypeScript code and get
|
|
35
|
+
back typed results.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// GET People?$select=UserName,FirstName&$filter=contains(UserName, 'russell')&$top=10
|
|
39
|
+
client
|
|
40
|
+
.entitySet<Person>('People')
|
|
41
|
+
.query((q) => {
|
|
42
|
+
q.filter(({ e, t }) => e().contains(t.UserName, 'russell'));
|
|
43
|
+
q.select(['UserName', 'FirstName']);
|
|
44
|
+
q.top(10);
|
|
45
|
+
})
|
|
46
|
+
.fetch()
|
|
47
|
+
.subscribe(({ entities }) => console.log(entities));
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
- **Typed query builder**: `$filter`, `$select`, `$expand`, `$orderby`, `$search`,
|
|
53
|
+
`$compute`, `$apply` and paging, with field names checked by the compiler.
|
|
54
|
+
- **Complete OData API**: entity sets, entities, navigation properties, functions,
|
|
55
|
+
actions, singletons, media and `$batch` requests.
|
|
56
|
+
- **Code generation**: one command creates interfaces, services and models from your
|
|
57
|
+
service's `$metadata`.
|
|
58
|
+
- **Models and collections**: change tracking, validation and `save()` / `destroy()`.
|
|
59
|
+
- **Caching**: in-memory, Web Storage or IndexedDB, with fetch policies such as
|
|
60
|
+
`cache-first` and `cache-and-network`.
|
|
61
|
+
- **Several APIs** in the same application, each with its own configuration.
|
|
30
62
|
|
|
31
|
-
|
|
63
|
+
## Quick start
|
|
64
|
+
|
|
65
|
+
### 1. Install
|
|
32
66
|
|
|
33
67
|
```bash
|
|
34
68
|
npm i angular-odata
|
|
35
69
|
```
|
|
36
70
|
|
|
37
|
-
|
|
71
|
+
### 2. Register the client
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// app.config.ts
|
|
75
|
+
import { ApplicationConfig } from '@angular/core';
|
|
76
|
+
import { provideODataClient } from 'angular-odata';
|
|
77
|
+
|
|
78
|
+
export const appConfig: ApplicationConfig = {
|
|
79
|
+
providers: [
|
|
80
|
+
provideODataClient({
|
|
81
|
+
config: { serviceRootUrl: 'https://services.odata.org/V4/TripPinServiceRW/' },
|
|
82
|
+
}),
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Applications that use NgModules can import `ODataModule.forRoot({ config })` instead.
|
|
88
|
+
|
|
89
|
+
### 3. Query your service
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { Component, inject, signal } from '@angular/core';
|
|
93
|
+
import { ODataClient } from 'angular-odata';
|
|
94
|
+
|
|
95
|
+
interface Person {
|
|
96
|
+
UserName: string;
|
|
97
|
+
FirstName: string;
|
|
98
|
+
LastName: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@Component({
|
|
102
|
+
selector: 'app-people',
|
|
103
|
+
template: `
|
|
104
|
+
@for (person of people(); track person.UserName) {
|
|
105
|
+
<p>{{ person.FirstName }} {{ person.LastName }}</p>
|
|
106
|
+
}
|
|
107
|
+
`,
|
|
108
|
+
})
|
|
109
|
+
export class PeopleComponent {
|
|
110
|
+
private client = inject(ODataClient);
|
|
111
|
+
people = signal<Person[]>([]);
|
|
112
|
+
|
|
113
|
+
ngOnInit() {
|
|
114
|
+
this.client
|
|
115
|
+
.entitySet<Person>('People')
|
|
116
|
+
.query((q) => {
|
|
117
|
+
q.orderBy('LastName');
|
|
118
|
+
q.top(10);
|
|
119
|
+
})
|
|
120
|
+
.fetch()
|
|
121
|
+
.subscribe(({ entities }) => this.people.set(entities ?? []));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 4. Optional: generate a typed API
|
|
127
|
+
|
|
128
|
+
Instead of writing interfaces and services by hand, generate them from the `$metadata` of
|
|
129
|
+
your service:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
ng generate angular-odata:apigen --name=TripPin --metadata='https://services.odata.org/V4/TripPinServiceRW/$metadata'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { PeopleService, TripPinConfig } from './trip-pin';
|
|
137
|
+
|
|
138
|
+
// app.config.ts
|
|
139
|
+
provideODataClient({ config: TripPinConfig });
|
|
140
|
+
|
|
141
|
+
// Any component or service
|
|
142
|
+
const people = inject(PeopleService);
|
|
143
|
+
people
|
|
144
|
+
.entity('russellwhyte')
|
|
145
|
+
.fetch()
|
|
146
|
+
.subscribe(({ entity }) => console.log(entity));
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## A few more examples
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const people = client.entitySet<Person>('People');
|
|
153
|
+
|
|
154
|
+
// Create, update and delete
|
|
155
|
+
people.create({ UserName: 'jdoe', FirstName: 'John', LastName: 'Doe' }).subscribe();
|
|
156
|
+
people.entity('jdoe').modify({ FirstName: 'Johnny' }).subscribe();
|
|
157
|
+
people.entity('jdoe').destroy().subscribe();
|
|
158
|
+
|
|
159
|
+
// Expand related entities: $expand=Trips($select=Name,Budget)
|
|
160
|
+
people
|
|
161
|
+
.entity('russellwhyte')
|
|
162
|
+
.query((q) =>
|
|
163
|
+
q.expand(({ e, t }) =>
|
|
164
|
+
e().field(t.Trips, (f) => f.select(({ e, t }) => e().field(t.Name).field(t.Budget))),
|
|
165
|
+
),
|
|
166
|
+
)
|
|
167
|
+
.fetch()
|
|
168
|
+
.subscribe(({ entity }) => console.log(entity?.Trips));
|
|
169
|
+
|
|
170
|
+
// Follow the server's next links and get every entity
|
|
171
|
+
people.fetchAll().subscribe(({ entities }) => console.log(entities.length));
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Documentation
|
|
175
|
+
|
|
176
|
+
- **[Handbook](https://diegomvh.github.io/angular-odata/docs/api/additional-documentation/introduction.html)**:
|
|
177
|
+
guides for configuration, resources, the query builder, services, models, caching and code
|
|
178
|
+
generation.
|
|
179
|
+
- **[API reference](https://diegomvh.github.io/angular-odata/docs/api/index.html)**: every
|
|
180
|
+
class and method, generated from the source code.
|
|
181
|
+
- **[Demo application](https://github.com/diegomvh/AngularODataEntity)**: a complete Angular
|
|
182
|
+
application that uses the TripPin and Northwind sample services.
|
|
183
|
+
|
|
184
|
+
## Compatibility
|
|
185
|
+
|
|
186
|
+
- **Angular**: 20 or later.
|
|
187
|
+
- **OData**: version 4 is fully supported; versions 3 and 2 have basic support.
|
|
188
|
+
|
|
189
|
+
## Contributing
|
|
190
|
+
|
|
191
|
+
Bug reports and pull requests are welcome. Read the
|
|
192
|
+
[contributing guide](https://github.com/diegomvh/angular-odata/blob/main/CONTRIBUTING.md)
|
|
193
|
+
to get started.
|
|
194
|
+
|
|
195
|
+
## License
|
|
38
196
|
|
|
39
|
-
|
|
197
|
+
[MIT](https://github.com/diegomvh/angular-odata/blob/main/LICENSE)
|
|
@@ -1593,7 +1593,7 @@ const Objects = {
|
|
|
1593
1593
|
},
|
|
1594
1594
|
resolveKey(key, { single = true } = {}) {
|
|
1595
1595
|
const type = Types.rawType(key);
|
|
1596
|
-
if (['
|
|
1596
|
+
if (['Number', 'String'].indexOf(type) !== -1)
|
|
1597
1597
|
return key;
|
|
1598
1598
|
if (type !== 'Map' && type !== 'Object') {
|
|
1599
1599
|
return undefined;
|
|
@@ -3214,7 +3214,7 @@ class ODataSchema extends ODataAnnotatable {
|
|
|
3214
3214
|
this.containers = (config.containers ?? []).map((config) => new ODataEntityContainer(config, this));
|
|
3215
3215
|
}
|
|
3216
3216
|
isNamespaceOf(type) {
|
|
3217
|
-
return type.startsWith(this.namespace)
|
|
3217
|
+
return type.startsWith(this.namespace) || (this.alias != null && type.startsWith(this.alias));
|
|
3218
3218
|
}
|
|
3219
3219
|
get entitySets() {
|
|
3220
3220
|
return this.containers.reduce((acc, container) => [...acc, ...container.entitySets], []);
|
|
@@ -5998,8 +5998,7 @@ class ODataRequest {
|
|
|
5998
5998
|
if (apiOptions.prefer?.maxPageSize !== undefined && ['GET'].indexOf(this._method) !== -1)
|
|
5999
5999
|
prefer.push(`odata.maxpagesize=${apiOptions.prefer?.maxPageSize}`);
|
|
6000
6000
|
// Annotations
|
|
6001
|
-
if (apiOptions.prefer?.includeAnnotations !== undefined &&
|
|
6002
|
-
['GET'].indexOf(this._method) !== -1)
|
|
6001
|
+
if (apiOptions.prefer?.includeAnnotations !== undefined && ['GET'].indexOf(this._method) !== -1)
|
|
6003
6002
|
prefer.push(`odata.include-annotations=${apiOptions.prefer?.includeAnnotations}`);
|
|
6004
6003
|
// Omit Null Values
|
|
6005
6004
|
if (apiOptions.prefer?.omitNullValues === true && ['GET'].indexOf(this._method) !== -1)
|
|
@@ -8148,18 +8147,25 @@ class CsdlRecord {
|
|
|
8148
8147
|
}
|
|
8149
8148
|
}
|
|
8150
8149
|
class CsdlPropertyValue {
|
|
8151
|
-
|
|
8150
|
+
Property;
|
|
8151
|
+
Bool;
|
|
8152
8152
|
String;
|
|
8153
8153
|
Date;
|
|
8154
8154
|
EnumMember;
|
|
8155
|
-
|
|
8156
|
-
|
|
8155
|
+
TextContent;
|
|
8156
|
+
constructor({ Property, Bool, String, Date, EnumMember, TextContent, }) {
|
|
8157
|
+
this.Property = Property;
|
|
8158
|
+
this.Bool = Bool;
|
|
8157
8159
|
this.String = String;
|
|
8158
8160
|
this.Date = Date;
|
|
8159
8161
|
this.EnumMember = EnumMember?.map((a) => new CsdlEnumMember(a));
|
|
8162
|
+
this.TextContent = TextContent;
|
|
8160
8163
|
}
|
|
8161
8164
|
toJson() {
|
|
8162
|
-
const json = {
|
|
8165
|
+
const json = { Property: this.Property };
|
|
8166
|
+
if (this.Bool !== undefined) {
|
|
8167
|
+
json['Bool'] = this.Bool;
|
|
8168
|
+
}
|
|
8163
8169
|
if (this.String !== undefined) {
|
|
8164
8170
|
json['String'] = this.String;
|
|
8165
8171
|
}
|
|
@@ -8169,10 +8175,16 @@ class CsdlPropertyValue {
|
|
|
8169
8175
|
if (Array.isArray(this.EnumMember) && this.EnumMember.length > 0) {
|
|
8170
8176
|
json['EnumMember'] = this.EnumMember.map((m) => m.toJson());
|
|
8171
8177
|
}
|
|
8178
|
+
if (this.TextContent !== undefined) {
|
|
8179
|
+
json['TextContent'] = this.TextContent;
|
|
8180
|
+
}
|
|
8172
8181
|
return json;
|
|
8173
8182
|
}
|
|
8174
8183
|
toConfig() {
|
|
8175
|
-
const config = {
|
|
8184
|
+
const config = { property: this.Property };
|
|
8185
|
+
if (this.Bool !== undefined) {
|
|
8186
|
+
config['bool'] = this.Bool;
|
|
8187
|
+
}
|
|
8176
8188
|
if (this.String !== undefined) {
|
|
8177
8189
|
config['string'] = this.String;
|
|
8178
8190
|
}
|
|
@@ -8182,6 +8194,9 @@ class CsdlPropertyValue {
|
|
|
8182
8194
|
if (Array.isArray(this.EnumMember) && this.EnumMember.length > 0) {
|
|
8183
8195
|
config['enumMembers'] = this.EnumMember.map((m) => m.toConfig());
|
|
8184
8196
|
}
|
|
8197
|
+
if (this.TextContent !== undefined) {
|
|
8198
|
+
config['textContent'] = this.TextContent;
|
|
8199
|
+
}
|
|
8185
8200
|
return config;
|
|
8186
8201
|
}
|
|
8187
8202
|
}
|
|
@@ -8402,10 +8417,8 @@ class CsdlEnumType extends CsdlAnnotable {
|
|
|
8402
8417
|
return {
|
|
8403
8418
|
...super.toConfig(),
|
|
8404
8419
|
name: this.Name,
|
|
8405
|
-
fields: this.Member.reduce((acc, m) => ({
|
|
8406
|
-
|
|
8407
|
-
[m.Name]: m.toConfig(),
|
|
8408
|
-
}), {}),
|
|
8420
|
+
fields: this.Member.reduce((acc, m) => ({ ...acc, [m.Name]: m.toConfig(), }), {}),
|
|
8421
|
+
members: this.Member.reduce((acc, m) => ({ ...acc, [m.Name]: m.Value, [m.Value]: m.Name }), {}),
|
|
8409
8422
|
flags: this.IsFlags,
|
|
8410
8423
|
};
|
|
8411
8424
|
}
|
|
@@ -8426,11 +8439,11 @@ class CsdlMember extends CsdlAnnotable {
|
|
|
8426
8439
|
return json;
|
|
8427
8440
|
}
|
|
8428
8441
|
toConfig(base) {
|
|
8429
|
-
|
|
8442
|
+
return {
|
|
8430
8443
|
...super.toConfig(),
|
|
8444
|
+
name: this.Name,
|
|
8431
8445
|
value: this.Value,
|
|
8432
8446
|
};
|
|
8433
|
-
return config;
|
|
8434
8447
|
}
|
|
8435
8448
|
}
|
|
8436
8449
|
|
|
@@ -8447,12 +8460,15 @@ class CsdlStructuralProperty extends CsdlAnnotable {
|
|
|
8447
8460
|
this.Type = this.Collection ? Type.substring(11, Type.length - 1) : Type;
|
|
8448
8461
|
}
|
|
8449
8462
|
toJson() {
|
|
8450
|
-
|
|
8463
|
+
const json = {
|
|
8451
8464
|
...super.toJson(),
|
|
8452
8465
|
Name: this.Name,
|
|
8453
|
-
Type: this.Collection ? `Collection(${this.Type})` : this.Type
|
|
8454
|
-
Nullable: this.Nullable,
|
|
8466
|
+
Type: this.Collection ? `Collection(${this.Type})` : this.Type
|
|
8455
8467
|
};
|
|
8468
|
+
if (this.Nullable !== undefined) {
|
|
8469
|
+
json['Nullable'] = this.Nullable;
|
|
8470
|
+
}
|
|
8471
|
+
return json;
|
|
8456
8472
|
}
|
|
8457
8473
|
}
|
|
8458
8474
|
class CsdlProperty extends CsdlStructuralProperty {
|
|
@@ -8795,7 +8811,7 @@ class CsdlFunction extends CsdlCallable {
|
|
|
8795
8811
|
entitySetPath: this.EntitySetPath,
|
|
8796
8812
|
bound: this.IsBound,
|
|
8797
8813
|
composable: this.IsComposable,
|
|
8798
|
-
parameters: this.Parameter?.
|
|
8814
|
+
parameters: this.Parameter?.reduce((acc, p) => ({ ...acc, [p.Name]: p.toConfig() }), {}),
|
|
8799
8815
|
return: this.ReturnType?.toConfig(),
|
|
8800
8816
|
};
|
|
8801
8817
|
}
|
|
@@ -8814,7 +8830,7 @@ class CsdlAction extends CsdlCallable {
|
|
|
8814
8830
|
name: this.Name,
|
|
8815
8831
|
entitySetPath: this.EntitySetPath,
|
|
8816
8832
|
bound: this.IsBound,
|
|
8817
|
-
parameters: this.Parameter?.
|
|
8833
|
+
parameters: this.Parameter?.reduce((acc, p) => ({ ...acc, [p.Name]: p.toConfig() }), {}),
|
|
8818
8834
|
return: this.ReturnType?.toConfig(),
|
|
8819
8835
|
};
|
|
8820
8836
|
}
|
|
@@ -8881,24 +8897,51 @@ class CsdlParameter extends CsdlAnnotable {
|
|
|
8881
8897
|
this.SRID = SRID;
|
|
8882
8898
|
}
|
|
8883
8899
|
toJson() {
|
|
8884
|
-
|
|
8900
|
+
const json = {
|
|
8885
8901
|
...super.toJson(),
|
|
8886
8902
|
Name: this.Name,
|
|
8887
|
-
Type: this.Collection ? `Collection(${this.Type})` : this.Type
|
|
8888
|
-
Nullable: this.Nullable,
|
|
8889
|
-
MaxLength: this.MaxLength,
|
|
8890
|
-
Precision: this.Precision,
|
|
8891
|
-
Scale: this.Scale,
|
|
8892
|
-
SRID: this.SRID,
|
|
8903
|
+
Type: this.Collection ? `Collection(${this.Type})` : this.Type
|
|
8893
8904
|
};
|
|
8905
|
+
if (this.Nullable !== undefined) {
|
|
8906
|
+
json['Nullable'] = this.Nullable;
|
|
8907
|
+
}
|
|
8908
|
+
if (this.MaxLength !== undefined) {
|
|
8909
|
+
json['MaxLength'] = this.MaxLength;
|
|
8910
|
+
}
|
|
8911
|
+
if (this.Precision !== undefined) {
|
|
8912
|
+
json['Precision'] = this.Precision;
|
|
8913
|
+
}
|
|
8914
|
+
if (this.Scale !== undefined) {
|
|
8915
|
+
json['Scale'] = this.Scale;
|
|
8916
|
+
}
|
|
8917
|
+
if (this.SRID !== undefined) {
|
|
8918
|
+
json['SRID'] = this.SRID;
|
|
8919
|
+
}
|
|
8920
|
+
return json;
|
|
8894
8921
|
}
|
|
8895
8922
|
toConfig() {
|
|
8896
|
-
|
|
8923
|
+
const config = {
|
|
8897
8924
|
...super.toConfig(),
|
|
8925
|
+
name: this.Name,
|
|
8898
8926
|
type: this.Type,
|
|
8899
|
-
|
|
8900
|
-
collection: false,
|
|
8927
|
+
collection: this.Collection,
|
|
8901
8928
|
};
|
|
8929
|
+
if (this.Nullable !== undefined) {
|
|
8930
|
+
config.nullable = this.Nullable;
|
|
8931
|
+
}
|
|
8932
|
+
if (this.MaxLength !== undefined) {
|
|
8933
|
+
config.maxLength = this.MaxLength;
|
|
8934
|
+
}
|
|
8935
|
+
if (this.Precision !== undefined) {
|
|
8936
|
+
config.precision = this.Precision;
|
|
8937
|
+
}
|
|
8938
|
+
if (this.Scale !== undefined) {
|
|
8939
|
+
config.scale = this.Scale;
|
|
8940
|
+
}
|
|
8941
|
+
if (this.SRID !== undefined) {
|
|
8942
|
+
config.srid = this.SRID;
|
|
8943
|
+
}
|
|
8944
|
+
return config;
|
|
8902
8945
|
}
|
|
8903
8946
|
}
|
|
8904
8947
|
class CsdlReturnType {
|
|
@@ -8919,20 +8962,47 @@ class CsdlReturnType {
|
|
|
8919
8962
|
this.SRID = SRID;
|
|
8920
8963
|
}
|
|
8921
8964
|
toJson() {
|
|
8922
|
-
|
|
8923
|
-
Type: this.Collection ? `Collection(${this.Type})` : this.Type
|
|
8924
|
-
Nullable: this.Nullable,
|
|
8925
|
-
MaxLength: this.MaxLength,
|
|
8926
|
-
Precision: this.Precision,
|
|
8927
|
-
Scale: this.Scale,
|
|
8928
|
-
SRID: this.SRID,
|
|
8965
|
+
const json = {
|
|
8966
|
+
Type: this.Collection ? `Collection(${this.Type})` : this.Type
|
|
8929
8967
|
};
|
|
8968
|
+
if (this.Nullable !== undefined) {
|
|
8969
|
+
json['Nullable'] = this.Nullable;
|
|
8970
|
+
}
|
|
8971
|
+
if (this.MaxLength !== undefined) {
|
|
8972
|
+
json['MaxLength'] = this.MaxLength;
|
|
8973
|
+
}
|
|
8974
|
+
if (this.Precision !== undefined) {
|
|
8975
|
+
json['Precision'] = this.Precision;
|
|
8976
|
+
}
|
|
8977
|
+
if (this.Scale !== undefined) {
|
|
8978
|
+
json['Scale'] = this.Scale;
|
|
8979
|
+
}
|
|
8980
|
+
if (this.SRID !== undefined) {
|
|
8981
|
+
json['SRID'] = this.SRID;
|
|
8982
|
+
}
|
|
8983
|
+
return json;
|
|
8930
8984
|
}
|
|
8931
8985
|
toConfig() {
|
|
8932
|
-
|
|
8986
|
+
const config = {
|
|
8933
8987
|
type: this.Type,
|
|
8934
|
-
collection:
|
|
8988
|
+
collection: this.Collection
|
|
8935
8989
|
};
|
|
8990
|
+
if (this.Nullable !== undefined) {
|
|
8991
|
+
config.nullable = this.Nullable;
|
|
8992
|
+
}
|
|
8993
|
+
if (this.MaxLength !== undefined) {
|
|
8994
|
+
config.maxLength = this.MaxLength;
|
|
8995
|
+
}
|
|
8996
|
+
if (this.Precision !== undefined) {
|
|
8997
|
+
config.precision = this.Precision;
|
|
8998
|
+
}
|
|
8999
|
+
if (this.Scale !== undefined) {
|
|
9000
|
+
config.scale = this.Scale;
|
|
9001
|
+
}
|
|
9002
|
+
if (this.SRID !== undefined) {
|
|
9003
|
+
config.srid = this.SRID;
|
|
9004
|
+
}
|
|
9005
|
+
return config;
|
|
8936
9006
|
}
|
|
8937
9007
|
}
|
|
8938
9008
|
|
|
@@ -9460,8 +9530,6 @@ class ODataMetadataParser {
|
|
|
9460
9530
|
case ODataMetadataParser.ATTRIBUTE_TERM:
|
|
9461
9531
|
case ODataMetadataParser.ATTRIBUTE_QUALIFIER:
|
|
9462
9532
|
case ODataMetadataParser.ATTRIBUTE_STRING:
|
|
9463
|
-
case ODataMetadataParser.ATTRIBUTE_BOOL:
|
|
9464
|
-
case ODataMetadataParser.ATTRIBUTE_INT:
|
|
9465
9533
|
case ODataMetadataParser.ATTRIBUTE_TARGET_NAMESPACE:
|
|
9466
9534
|
case ODataMetadataParser.ATTRIBUTE_NAME:
|
|
9467
9535
|
case ODataMetadataParser.ATTRIBUTE_TYPE:
|
|
@@ -9483,6 +9551,7 @@ class ODataMetadataParser {
|
|
|
9483
9551
|
case ODataMetadataParser.ATTRIBUTE_BASE_TERM:
|
|
9484
9552
|
case ODataMetadataParser.ATTRIBUTE_APPLIES_TO:
|
|
9485
9553
|
return this.getAttributeValue(attributes, field.name);
|
|
9554
|
+
case ODataMetadataParser.ATTRIBUTE_BOOL:
|
|
9486
9555
|
case ODataMetadataParser.ATTRIBUTE_NULLABLE:
|
|
9487
9556
|
case ODataMetadataParser.ATTRIBUTE_UNICODE:
|
|
9488
9557
|
case ODataMetadataParser.ATTRIBUTE_OPEN_TYPE:
|
|
@@ -9494,6 +9563,7 @@ class ODataMetadataParser {
|
|
|
9494
9563
|
case ODataMetadataParser.ATTRIBUTE_ABSTRACT:
|
|
9495
9564
|
case ODataMetadataParser.ATTRIBUTE_IS_FLAGS:
|
|
9496
9565
|
return this.propertyValueToBoolean(this.getAttributeValue(attributes, field.name));
|
|
9566
|
+
case ODataMetadataParser.ATTRIBUTE_INT:
|
|
9497
9567
|
case ODataMetadataParser.ATTRIBUTE_VALUE:
|
|
9498
9568
|
case ODataMetadataParser.ATTRIBUTE_MAX_LENGTH:
|
|
9499
9569
|
case ODataMetadataParser.ATTRIBUTE_PRECISION:
|
|
@@ -11348,7 +11418,7 @@ class ODataModel {
|
|
|
11348
11418
|
resource instanceof ODataNavigationPropertyResource))
|
|
11349
11419
|
return throwError(() => new Error('save: Resource type ODataEntityResource/ODataNavigationPropertyResource needed'));
|
|
11350
11420
|
// Resolve method and resource key
|
|
11351
|
-
if (method === undefined && this.
|
|
11421
|
+
if (method === undefined && this.isCompoundKey())
|
|
11352
11422
|
return throwError(() => new Error('save: Composite key require a specific method, use create/update/modify'));
|
|
11353
11423
|
method = method || (!resource.hasKey() ? 'create' : 'update');
|
|
11354
11424
|
if (resource instanceof ODataEntityResource &&
|
|
@@ -11425,6 +11495,9 @@ class ODataModel {
|
|
|
11425
11495
|
isNew() {
|
|
11426
11496
|
return !this._meta.hasKey(this);
|
|
11427
11497
|
}
|
|
11498
|
+
isCompoundKey() {
|
|
11499
|
+
return this.schema().isCompoundKey();
|
|
11500
|
+
}
|
|
11428
11501
|
withResource(resource, ctx) {
|
|
11429
11502
|
return this._meta.withResource(this, resource, ctx);
|
|
11430
11503
|
}
|
|
@@ -13019,7 +13092,7 @@ class ODataApi {
|
|
|
13019
13092
|
});
|
|
13020
13093
|
return true;
|
|
13021
13094
|
}
|
|
13022
|
-
populate({ schemas, references, models, collections }) {
|
|
13095
|
+
populate({ schemas, references, models, collections, }) {
|
|
13023
13096
|
const odataSchemas = (schemas ?? []).map((schema) => new ODataSchema(schema, this));
|
|
13024
13097
|
//TODO: Merge duplicates
|
|
13025
13098
|
this.schemas = [...this.schemas, ...odataSchemas];
|