@balena/pinejs 21.1.0-build-odata-metadata-json-395a55cb54e7b9ce0960ab93aad5f69d6c0e0462-2 → 21.1.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/.pinejs-cache.json +1 -1
- package/.versionbot/CHANGELOG.yml +95 -11
- package/CHANGELOG.md +24 -2
- package/out/odata-metadata/odata-metadata-generator.d.ts +1 -61
- package/out/odata-metadata/odata-metadata-generator.js +97 -186
- package/out/odata-metadata/odata-metadata-generator.js.map +1 -1
- package/out/sbvr-api/permissions.d.ts +0 -4
- package/out/sbvr-api/permissions.js +1 -1
- package/out/sbvr-api/permissions.js.map +1 -1
- package/out/sbvr-api/sbvr-utils.js +3 -18
- package/out/sbvr-api/sbvr-utils.js.map +1 -1
- package/out/sbvr-api/uri-parser.js +1 -1
- package/out/sbvr-api/uri-parser.js.map +1 -1
- package/package.json +3 -5
- package/src/odata-metadata/odata-metadata-generator.ts +126 -344
- package/src/sbvr-api/permissions.ts +2 -2
- package/src/sbvr-api/sbvr-utils.ts +3 -33
- package/src/sbvr-api/uri-parser.ts +1 -1
- package/out/odata-metadata/open-api-specification-generator.d.ts +0 -2
- package/out/odata-metadata/open-api-specification-generator.js +0 -50
- package/out/odata-metadata/open-api-specification-generator.js.map +0 -1
- package/src/odata-metadata/open-api-specification-generator.ts +0 -98
- package/typings/odata-openapi.d.ts +0 -6
@@ -6,199 +6,6 @@ import type {
|
|
6
6
|
import type { SbvrType } from '@balena/sbvr-types';
|
7
7
|
import { sbvrTypes } from '../sbvr-api/sbvr-utils.js';
|
8
8
|
import { version } from '../config-loader/env.js';
|
9
|
-
import type { PermissionLookup } from '../sbvr-api/permissions.js';
|
10
|
-
|
11
|
-
// OData JSON v4 CSDL Vocabulary constants
|
12
|
-
// http://docs.oasis-open.org/odata/odata-vocabularies/v4.0/odata-vocabularies-v4.0.html
|
13
|
-
const odataVocabularyReferences: ODataCsdlV4References = {
|
14
|
-
'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.json':
|
15
|
-
{
|
16
|
-
$Include: [
|
17
|
-
{
|
18
|
-
$Namespace: 'Org.OData.Core.V1',
|
19
|
-
$Alias: 'Core',
|
20
|
-
'@Core.DefaultNamespace': true,
|
21
|
-
},
|
22
|
-
],
|
23
|
-
},
|
24
|
-
'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Measures.V1.json':
|
25
|
-
{
|
26
|
-
$Include: [
|
27
|
-
{
|
28
|
-
$Namespace: 'Org.OData.Measures.V1',
|
29
|
-
$Alias: 'Measures',
|
30
|
-
},
|
31
|
-
],
|
32
|
-
},
|
33
|
-
'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Aggregation.V1.json':
|
34
|
-
{
|
35
|
-
$Include: [
|
36
|
-
{
|
37
|
-
$Namespace: 'Org.OData.Aggregation.V1',
|
38
|
-
$Alias: 'Aggregation',
|
39
|
-
},
|
40
|
-
],
|
41
|
-
},
|
42
|
-
'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.json':
|
43
|
-
{
|
44
|
-
$Include: [
|
45
|
-
{
|
46
|
-
$Namespace: 'Org.OData.Capabilities.V1',
|
47
|
-
$Alias: 'Capabilities',
|
48
|
-
},
|
49
|
-
],
|
50
|
-
},
|
51
|
-
};
|
52
|
-
|
53
|
-
/**
|
54
|
-
* Odata Common Schema Definition Language JSON format
|
55
|
-
* http://docs.oasis-open.org/odata/odata-json-format/v4.0/odata-json-format-v4.0.html
|
56
|
-
*/
|
57
|
-
|
58
|
-
type ODataCsdlV4References = {
|
59
|
-
[URI: string]: {
|
60
|
-
$Include: Array<{
|
61
|
-
$Namespace: string;
|
62
|
-
$Alias: string;
|
63
|
-
[annotation: string]: string | boolean;
|
64
|
-
}>;
|
65
|
-
};
|
66
|
-
};
|
67
|
-
|
68
|
-
type ODataCsdlV4BaseProperty = {
|
69
|
-
[annotation: string]: string | boolean | undefined;
|
70
|
-
$Type?: string;
|
71
|
-
$Nullable?: boolean;
|
72
|
-
};
|
73
|
-
|
74
|
-
type ODataCsdlV4StructuralProperty = ODataCsdlV4BaseProperty & {
|
75
|
-
$Kind?: 'Property'; // This member SHOULD be omitted to reduce document size.
|
76
|
-
};
|
77
|
-
|
78
|
-
type ODataCsdlV4NavigationProperty = ODataCsdlV4BaseProperty & {
|
79
|
-
$Kind: 'NavigationProperty';
|
80
|
-
$Partner?: string;
|
81
|
-
};
|
82
|
-
|
83
|
-
type ODataCsdlV4Property =
|
84
|
-
| ODataCsdlV4BaseProperty
|
85
|
-
| ODataCsdlV4StructuralProperty
|
86
|
-
| ODataCsdlV4NavigationProperty;
|
87
|
-
|
88
|
-
type ODataCsdlV4EntityType = {
|
89
|
-
$Kind: 'EntityType';
|
90
|
-
$Key: string[];
|
91
|
-
[property: string]: true | string[] | string | ODataCsdlV4Property;
|
92
|
-
};
|
93
|
-
|
94
|
-
type ODataCsdlV4EntityContainerEntries = {
|
95
|
-
// $Collection: true;
|
96
|
-
$Type: string;
|
97
|
-
[property: string]: true | string | ODataCapabilitiesUDIRRestrictionsMethod;
|
98
|
-
};
|
99
|
-
|
100
|
-
type ODataCsdlV4Entities = {
|
101
|
-
[resource: string]: ODataCsdlV4EntityType;
|
102
|
-
};
|
103
|
-
|
104
|
-
type ODataCsdlV4EntityContainer = {
|
105
|
-
$Kind: 'EntityContainer';
|
106
|
-
'@Capabilities.BatchSupported'?: boolean;
|
107
|
-
[resourceOrAnnotation: string]:
|
108
|
-
| boolean
|
109
|
-
| string
|
110
|
-
| ODataCsdlV4EntityContainerEntries
|
111
|
-
| undefined;
|
112
|
-
};
|
113
|
-
|
114
|
-
type ODataCsdlV4Schema = {
|
115
|
-
$Alias: string;
|
116
|
-
'@Core.DefaultNamespace': true;
|
117
|
-
[resource: string]:
|
118
|
-
| string
|
119
|
-
| boolean
|
120
|
-
| ODataCsdlV4EntityContainer
|
121
|
-
| ODataCsdlV4EntityType;
|
122
|
-
};
|
123
|
-
|
124
|
-
type OdataCsdlV4 = {
|
125
|
-
$Version: string;
|
126
|
-
$Reference: ODataCsdlV4References;
|
127
|
-
$EntityContainer: string;
|
128
|
-
[schema: string]: string | ODataCsdlV4References | ODataCsdlV4Schema;
|
129
|
-
};
|
130
|
-
|
131
|
-
type PreparedPermissionsLookup = {
|
132
|
-
[vocabulary: string]: {
|
133
|
-
[resource: string]: {
|
134
|
-
read: boolean;
|
135
|
-
create: boolean;
|
136
|
-
update: boolean;
|
137
|
-
delete: boolean;
|
138
|
-
};
|
139
|
-
};
|
140
|
-
};
|
141
|
-
|
142
|
-
type PreparedAbstractModel = {
|
143
|
-
vocabulary: string;
|
144
|
-
abstractSqlModel: AbstractSqlModel;
|
145
|
-
preparedPermissionLookup: PreparedPermissionsLookup;
|
146
|
-
};
|
147
|
-
|
148
|
-
type ODataCapabilitiesUDIRRestrictionsMethod =
|
149
|
-
| { Updatable: boolean }
|
150
|
-
| { Deletable: boolean }
|
151
|
-
| { Insertable: boolean }
|
152
|
-
| { Readable: boolean }
|
153
|
-
| { Filterable: boolean };
|
154
|
-
|
155
|
-
const restrictionsLookup = (
|
156
|
-
method: keyof PreparedPermissionsLookup[string][string] | 'all',
|
157
|
-
value: boolean,
|
158
|
-
) => {
|
159
|
-
const lookup = {
|
160
|
-
update: {
|
161
|
-
'@Capabilities.UpdateRestrictions': {
|
162
|
-
Updatable: value,
|
163
|
-
},
|
164
|
-
'@Capabilities.FilterRestrictions': {
|
165
|
-
Filterable: true,
|
166
|
-
},
|
167
|
-
},
|
168
|
-
delete: {
|
169
|
-
'@Capabilities.DeleteRestrictions': {
|
170
|
-
Deletable: value,
|
171
|
-
},
|
172
|
-
'@Capabilities.FilterRestrictions': {
|
173
|
-
Filterable: true,
|
174
|
-
},
|
175
|
-
},
|
176
|
-
create: {
|
177
|
-
'@Capabilities.InsertRestrictions': {
|
178
|
-
Insertable: value,
|
179
|
-
},
|
180
|
-
},
|
181
|
-
read: {
|
182
|
-
'@Capabilities.ReadRestrictions': {
|
183
|
-
Readable: value,
|
184
|
-
},
|
185
|
-
'@Capabilities.FilterRestrictions': {
|
186
|
-
Filterable: true,
|
187
|
-
},
|
188
|
-
},
|
189
|
-
};
|
190
|
-
|
191
|
-
if (method === 'all') {
|
192
|
-
return {
|
193
|
-
...lookup['update'],
|
194
|
-
...lookup['delete'],
|
195
|
-
...lookup['create'],
|
196
|
-
...lookup['read'],
|
197
|
-
};
|
198
|
-
} else {
|
199
|
-
return lookup[method] ?? {};
|
200
|
-
}
|
201
|
-
};
|
202
9
|
|
203
10
|
const getResourceName = (resourceName: string): string =>
|
204
11
|
resourceName
|
@@ -207,25 +14,17 @@ const getResourceName = (resourceName: string): string =>
|
|
207
14
|
.join('__');
|
208
15
|
|
209
16
|
const forEachUniqueTable = <T>(
|
210
|
-
model:
|
211
|
-
callback: (
|
212
|
-
tableName: string,
|
213
|
-
table: AbstractSqlTable & { referenceScheme: string },
|
214
|
-
) => T,
|
17
|
+
model: AbstractSqlModel['tables'],
|
18
|
+
callback: (tableName: string, table: AbstractSqlTable) => T,
|
215
19
|
): T[] => {
|
216
20
|
const usedTableNames: { [tableName: string]: true } = {};
|
217
21
|
|
218
22
|
const result = [];
|
219
|
-
|
220
|
-
for (const key of Object.keys(model.abstractSqlModel.tables).sort()) {
|
221
|
-
const table = model.abstractSqlModel.tables[key] as AbstractSqlTable & {
|
222
|
-
referenceScheme: string;
|
223
|
-
};
|
23
|
+
for (const [key, table] of Object.entries(model)) {
|
224
24
|
if (
|
225
25
|
typeof table !== 'string' &&
|
226
26
|
!table.primitive &&
|
227
|
-
!usedTableNames[table.name]
|
228
|
-
model.preparedPermissionLookup
|
27
|
+
!usedTableNames[table.name]
|
229
28
|
) {
|
230
29
|
usedTableNames[table.name] = true;
|
231
30
|
result.push(callback(key, table));
|
@@ -234,49 +33,9 @@ const forEachUniqueTable = <T>(
|
|
234
33
|
return result;
|
235
34
|
};
|
236
35
|
|
237
|
-
/**
|
238
|
-
* parsing dictionary of vocabulary.resource.operation permissions string
|
239
|
-
* into dictionary of resource to operation for later lookup
|
240
|
-
*/
|
241
|
-
|
242
|
-
const preparePermissionsLookup = (
|
243
|
-
permissionLookup: PermissionLookup,
|
244
|
-
): PreparedPermissionsLookup => {
|
245
|
-
const resourcesAndOps: PreparedPermissionsLookup = {};
|
246
|
-
|
247
|
-
for (const resourceOpsAuths of Object.keys(permissionLookup)) {
|
248
|
-
const [vocabulary, resource, rule] = resourceOpsAuths.split('.');
|
249
|
-
resourcesAndOps[vocabulary] ??= {};
|
250
|
-
resourcesAndOps[vocabulary][resource] ??= {
|
251
|
-
['read']: false,
|
252
|
-
['create']: false,
|
253
|
-
['update']: false,
|
254
|
-
['delete']: false,
|
255
|
-
};
|
256
|
-
|
257
|
-
if (rule === 'all' || (resource === 'all' && rule === undefined)) {
|
258
|
-
resourcesAndOps[vocabulary][resource] = {
|
259
|
-
['read']: true,
|
260
|
-
['create']: true,
|
261
|
-
['update']: true,
|
262
|
-
['delete']: true,
|
263
|
-
};
|
264
|
-
} else if (
|
265
|
-
rule === 'read' ||
|
266
|
-
rule === 'create' ||
|
267
|
-
rule === 'update' ||
|
268
|
-
rule === 'delete'
|
269
|
-
) {
|
270
|
-
resourcesAndOps[vocabulary][resource][rule] = true;
|
271
|
-
}
|
272
|
-
}
|
273
|
-
return resourcesAndOps;
|
274
|
-
};
|
275
|
-
|
276
36
|
export const generateODataMetadata = (
|
277
37
|
vocabulary: string,
|
278
38
|
abstractSqlModel: AbstractSqlModel,
|
279
|
-
permissionsLookup?: PermissionLookup,
|
280
39
|
) => {
|
281
40
|
const complexTypes: { [fieldType: string]: string } = {};
|
282
41
|
const resolveDataType = (fieldType: keyof typeof sbvrTypes): string => {
|
@@ -291,109 +50,132 @@ export const generateODataMetadata = (
|
|
291
50
|
return sbvrTypes[fieldType].types.odata.name;
|
292
51
|
};
|
293
52
|
|
294
|
-
const
|
295
|
-
|
296
|
-
:
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
}
|
303
|
-
|
304
|
-
const metaBalenaEntries: ODataCsdlV4Entities = {};
|
305
|
-
const entityContainer: ODataCsdlV4EntityContainer = {
|
306
|
-
$Kind: 'EntityContainer',
|
307
|
-
'@Capabilities.KeyAsSegmentSupported': false,
|
308
|
-
};
|
309
|
-
|
310
|
-
forEachUniqueTable(model, (_key, { idField, name: resourceName, fields }) => {
|
53
|
+
const model = abstractSqlModel.tables;
|
54
|
+
const associations: Array<{
|
55
|
+
name: string;
|
56
|
+
ends: Array<{
|
57
|
+
resourceName: string;
|
58
|
+
cardinality: '1' | '0..1' | '*';
|
59
|
+
}>;
|
60
|
+
}> = [];
|
61
|
+
forEachUniqueTable(model, (_key, { name: resourceName, fields }) => {
|
311
62
|
resourceName = getResourceName(resourceName);
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
$Kind: 'EntityType',
|
324
|
-
$Key: [idField],
|
325
|
-
};
|
326
|
-
|
327
|
-
fields
|
328
|
-
.filter(({ dataType }) => dataType !== 'ForeignKey')
|
329
|
-
.map(({ dataType, fieldName, required }) => {
|
330
|
-
dataType = resolveDataType(dataType as keyof typeof sbvrTypes);
|
331
|
-
fieldName = getResourceName(fieldName);
|
332
|
-
|
333
|
-
uniqueTable[fieldName] = {
|
334
|
-
$Type: dataType,
|
335
|
-
$Nullable: !required,
|
336
|
-
'@Core.Computed':
|
337
|
-
fieldName === 'created_at' || fieldName === 'modified_at'
|
338
|
-
? true
|
339
|
-
: false,
|
340
|
-
};
|
341
|
-
});
|
342
|
-
|
343
|
-
fields
|
344
|
-
.filter(
|
345
|
-
({ dataType, references }) =>
|
346
|
-
dataType === 'ForeignKey' && references != null,
|
347
|
-
)
|
348
|
-
.map(({ fieldName, references, required }) => {
|
349
|
-
const { resourceName: referencedResource } = references!;
|
350
|
-
const referencedResourceName =
|
351
|
-
model.abstractSqlModel.tables[referencedResource]?.name;
|
352
|
-
const typeReference = referencedResourceName || referencedResource;
|
353
|
-
|
354
|
-
fieldName = getResourceName(fieldName);
|
355
|
-
uniqueTable[fieldName] = {
|
356
|
-
$Kind: 'NavigationProperty',
|
357
|
-
$Partner: resourceName,
|
358
|
-
$Nullable: !required,
|
359
|
-
$Type: vocabulary + '.' + getResourceName(typeReference),
|
360
|
-
};
|
361
|
-
});
|
362
|
-
|
363
|
-
metaBalenaEntries[resourceName] = uniqueTable;
|
364
|
-
|
365
|
-
let entityCon: ODataCsdlV4EntityContainerEntries = {
|
366
|
-
$Collection: true,
|
367
|
-
$Type: vocabulary + '.' + resourceName,
|
368
|
-
};
|
369
|
-
for (const [resKey, resValue] of Object.entries(permissions) as Array<
|
370
|
-
[keyof PreparedPermissionsLookup[string][string], boolean]
|
371
|
-
>) {
|
372
|
-
entityCon = { ...entityCon, ...restrictionsLookup(resKey, resValue) };
|
63
|
+
for (const { dataType, required, references } of fields) {
|
64
|
+
if (dataType === 'ForeignKey' && references != null) {
|
65
|
+
const { resourceName: referencedResource } = references;
|
66
|
+
associations.push({
|
67
|
+
name: resourceName + referencedResource,
|
68
|
+
ends: [
|
69
|
+
{ resourceName, cardinality: required ? '1' : '0..1' },
|
70
|
+
{ resourceName: referencedResource, cardinality: '*' },
|
71
|
+
],
|
72
|
+
});
|
73
|
+
}
|
373
74
|
}
|
374
|
-
|
375
|
-
entityContainer[resourceName] = entityCon;
|
376
75
|
});
|
377
76
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
77
|
+
return (
|
78
|
+
`
|
79
|
+
<?xml version="1.0" encoding="iso-8859-1" standalone="yes"?>
|
80
|
+
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
|
81
|
+
<edmx:DataServices xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:DataServiceVersion="2.0">
|
82
|
+
<Schema Namespace="${vocabulary}"
|
83
|
+
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
|
84
|
+
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
|
85
|
+
xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
|
86
|
+
|
87
|
+
` +
|
88
|
+
forEachUniqueTable(
|
89
|
+
model,
|
90
|
+
(_key, { idField, name: resourceName, fields }) => {
|
91
|
+
resourceName = getResourceName(resourceName);
|
92
|
+
return (
|
93
|
+
`
|
94
|
+
<EntityType Name="${resourceName}">
|
95
|
+
<Key>
|
96
|
+
<PropertyRef Name="${idField}" />
|
97
|
+
</Key>
|
98
|
+
|
99
|
+
` +
|
100
|
+
fields
|
101
|
+
.filter(({ dataType }) => dataType !== 'ForeignKey')
|
102
|
+
.map(({ dataType, fieldName, required }) => {
|
103
|
+
dataType = resolveDataType(dataType as keyof typeof sbvrTypes);
|
104
|
+
fieldName = getResourceName(fieldName);
|
105
|
+
return `<Property Name="${fieldName}" Type="${dataType}" Nullable="${!required}" />`;
|
106
|
+
})
|
107
|
+
.join('\n') +
|
108
|
+
'\n' +
|
109
|
+
fields
|
110
|
+
.filter(
|
111
|
+
({ dataType, references }) =>
|
112
|
+
dataType === 'ForeignKey' && references != null,
|
113
|
+
)
|
114
|
+
.map(({ fieldName, references }) => {
|
115
|
+
const { resourceName: referencedResource } = references!;
|
116
|
+
fieldName = getResourceName(fieldName);
|
117
|
+
return `<NavigationProperty Name="${fieldName}" Relationship="${vocabulary}.${
|
118
|
+
resourceName + referencedResource
|
119
|
+
}" FromRole="${resourceName}" ToRole="${referencedResource}" />`;
|
120
|
+
})
|
121
|
+
.join('\n') +
|
122
|
+
'\n' +
|
123
|
+
`
|
124
|
+
</EntityType>`
|
125
|
+
);
|
126
|
+
},
|
127
|
+
).join('\n\n') +
|
128
|
+
associations
|
129
|
+
.map(({ name, ends }) => {
|
130
|
+
name = getResourceName(name);
|
131
|
+
return (
|
132
|
+
`<Association Name="${name}">` +
|
133
|
+
'\n\t' +
|
134
|
+
ends
|
135
|
+
.map(
|
136
|
+
({ resourceName, cardinality }) =>
|
137
|
+
`<End Role="${resourceName}" Type="${vocabulary}.${resourceName}" Multiplicity="${cardinality}" />`,
|
138
|
+
)
|
139
|
+
.join('\n\t') +
|
140
|
+
'\n' +
|
141
|
+
`</Association>`
|
142
|
+
);
|
143
|
+
})
|
144
|
+
.join('\n') +
|
145
|
+
`
|
146
|
+
<EntityContainer Name="${vocabulary}Service" m:IsDefaultEntityContainer="true">
|
147
|
+
|
148
|
+
` +
|
149
|
+
forEachUniqueTable(model, (_key, { name: resourceName }) => {
|
150
|
+
resourceName = getResourceName(resourceName);
|
151
|
+
return `<EntitySet Name="${resourceName}" EntityType="${vocabulary}.${resourceName}" />`;
|
152
|
+
}).join('\n') +
|
153
|
+
'\n' +
|
154
|
+
associations
|
155
|
+
.map(({ name, ends }) => {
|
156
|
+
name = getResourceName(name);
|
157
|
+
return (
|
158
|
+
`<AssociationSet Name="${name}" Association="${vocabulary}.${name}">` +
|
159
|
+
'\n\t' +
|
160
|
+
ends
|
161
|
+
.map(
|
162
|
+
({ resourceName }) =>
|
163
|
+
`<End Role="${resourceName}" EntitySet="${vocabulary}.${resourceName}" />`,
|
164
|
+
)
|
165
|
+
.join('\n\t') +
|
166
|
+
`
|
167
|
+
</AssociationSet>`
|
168
|
+
);
|
169
|
+
})
|
170
|
+
.join('\n') +
|
171
|
+
`
|
172
|
+
</EntityContainer>` +
|
173
|
+
Object.values(complexTypes).join('\n') +
|
174
|
+
`
|
175
|
+
</Schema>
|
176
|
+
</edmx:DataServices>
|
177
|
+
</edmx:Edmx>`
|
178
|
+
);
|
397
179
|
};
|
398
180
|
|
399
181
|
generateODataMetadata.version = version;
|
@@ -328,7 +328,7 @@ const namespaceRelationships = (
|
|
328
328
|
}
|
329
329
|
};
|
330
330
|
|
331
|
-
|
331
|
+
type PermissionLookup = Dictionary<true | string[]>;
|
332
332
|
|
333
333
|
const getPermissionsLookup = env.createCache(
|
334
334
|
'permissionsLookup',
|
@@ -1707,7 +1707,7 @@ const getGuestPermissions = memoize(
|
|
1707
1707
|
{ promise: true },
|
1708
1708
|
);
|
1709
1709
|
|
1710
|
-
|
1710
|
+
const getReqPermissions = async (
|
1711
1711
|
req: PermissionReq,
|
1712
1712
|
odataBinds: ODataBinds = [] as any as ODataBinds,
|
1713
1713
|
) => {
|
@@ -48,7 +48,6 @@ import * as asyncMigrator from '../migrator/async.js';
|
|
48
48
|
import * as syncMigrator from '../migrator/sync.js';
|
49
49
|
import { generateODataMetadata } from '../odata-metadata/odata-metadata-generator.js';
|
50
50
|
import { importSBVR } from '../server-glue/sbvr-loader.js';
|
51
|
-
import { generateODataMetadataAsOpenApi } from '../odata-metadata/open-api-specification-generator.js';
|
52
51
|
|
53
52
|
import type DevModel from './dev.js';
|
54
53
|
const devModel = await importSBVR('./dev.sbvr', import.meta);
|
@@ -112,7 +111,6 @@ import {
|
|
112
111
|
type MigrationExecutionResult,
|
113
112
|
setExecutedMigrations,
|
114
113
|
} from '../migrator/utils.js';
|
115
|
-
import { metadataEndpoints } from './uri-parser.js';
|
116
114
|
|
117
115
|
const LF2AbstractSQLTranslator = LF2AbstractSQL.createTranslator(sbvrTypes);
|
118
116
|
const LF2AbstractSQLTranslatorVersion = `${LF2AbstractSQLVersion}+${sbvrTypesVersion}`;
|
@@ -1337,10 +1335,7 @@ const runODataRequest = (req: Express.Request, vocabulary: string) => {
|
|
1337
1335
|
})
|
1338
1336
|
: resolveSynonym($request);
|
1339
1337
|
|
1340
|
-
if (
|
1341
|
-
abstractSqlModel.tables[resolvedResourceName] == null &&
|
1342
|
-
!metadataEndpoints.includes(resolvedResourceName)
|
1343
|
-
) {
|
1338
|
+
if (abstractSqlModel.tables[resolvedResourceName] == null) {
|
1344
1339
|
throw new UnauthorizedError();
|
1345
1340
|
}
|
1346
1341
|
|
@@ -1843,35 +1838,10 @@ const respondGet = async (
|
|
1843
1838
|
return response;
|
1844
1839
|
} else {
|
1845
1840
|
if (request.resourceName === '$metadata') {
|
1846
|
-
const permLookup = await permissions.getReqPermissions(req);
|
1847
|
-
const spec = generateODataMetadata(
|
1848
|
-
vocab,
|
1849
|
-
models[vocab].abstractSql,
|
1850
|
-
permLookup,
|
1851
|
-
);
|
1852
|
-
return {
|
1853
|
-
statusCode: 200,
|
1854
|
-
body: spec,
|
1855
|
-
headers: { 'content-type': 'application/json' },
|
1856
|
-
};
|
1857
|
-
} else if (request.resourceName === 'openapi.json') {
|
1858
|
-
// https://docs.oasis-open.org/odata/odata-openapi/v1.0/cn01/odata-openapi-v1.0-cn01.html#sec_ProvidingOASDocumentsforanODataServi
|
1859
|
-
// Following the OASIS OData to openapi translation guide the openapi.json is an independent resource
|
1860
|
-
const permLookup = await permissions.getReqPermissions(req);
|
1861
|
-
const spec = generateODataMetadata(
|
1862
|
-
vocab,
|
1863
|
-
models[vocab].abstractSql,
|
1864
|
-
permLookup,
|
1865
|
-
);
|
1866
|
-
const openApispec = generateODataMetadataAsOpenApi(
|
1867
|
-
spec,
|
1868
|
-
req.originalUrl.replace('openapi.json', ''),
|
1869
|
-
req.hostname,
|
1870
|
-
);
|
1871
1841
|
return {
|
1872
1842
|
statusCode: 200,
|
1873
|
-
body:
|
1874
|
-
headers: { 'content-type': '
|
1843
|
+
body: models[vocab].odataMetadata,
|
1844
|
+
headers: { 'content-type': 'xml' },
|
1875
1845
|
};
|
1876
1846
|
} else {
|
1877
1847
|
// TODO: request.resourceName can be '$serviceroot' or a resource and we should return an odata xml document based on that
|
@@ -267,7 +267,7 @@ const memoizedOdata2AbstractSQL = (() => {
|
|
267
267
|
};
|
268
268
|
})();
|
269
269
|
|
270
|
-
export const metadataEndpoints = ['$metadata', '$serviceroot'
|
270
|
+
export const metadataEndpoints = ['$metadata', '$serviceroot'];
|
271
271
|
|
272
272
|
export function parseOData(
|
273
273
|
b: UnparsedRequest & { _isChangeSet?: false },
|
@@ -1,50 +0,0 @@
|
|
1
|
-
import * as odataMetadata from 'odata-openapi';
|
2
|
-
import _ from 'lodash';
|
3
|
-
export const generateODataMetadataAsOpenApi = (odataCsdl, versionBasePathUrl = '', hostname = '') => {
|
4
|
-
const openAPIJson = odataMetadata.csdl2openapi(odataCsdl, {
|
5
|
-
scheme: 'https',
|
6
|
-
host: hostname,
|
7
|
-
basePath: versionBasePathUrl,
|
8
|
-
diagram: false,
|
9
|
-
maxLevels: 5,
|
10
|
-
});
|
11
|
-
const parameters = openAPIJson?.components?.parameters;
|
12
|
-
parameters['filter'] = {
|
13
|
-
name: '$filter',
|
14
|
-
description: 'Filter items by property values, see [Filtering](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionfilter)',
|
15
|
-
in: 'query',
|
16
|
-
schema: {
|
17
|
-
type: 'string',
|
18
|
-
},
|
19
|
-
};
|
20
|
-
for (const idx of Object.keys(openAPIJson.paths)) {
|
21
|
-
const properties = openAPIJson?.paths[idx]?.get?.responses?.['200']?.content?.['application/json']?.schema?.properties;
|
22
|
-
if (properties?.value) {
|
23
|
-
properties['d'] = properties.value;
|
24
|
-
delete properties.value;
|
25
|
-
}
|
26
|
-
if (properties?.['@odata.count']) {
|
27
|
-
delete properties['@odata.count'];
|
28
|
-
}
|
29
|
-
const entityCollectionPath = openAPIJson?.paths[idx];
|
30
|
-
const singleEntityPath = openAPIJson?.paths[idx + '({id})'];
|
31
|
-
if (entityCollectionPath != null && singleEntityPath != null) {
|
32
|
-
const genericFilterParameterRef = {
|
33
|
-
$ref: '#/components/parameters/filter',
|
34
|
-
};
|
35
|
-
for (const action of ['delete', 'patch']) {
|
36
|
-
entityCollectionPath[action] = _.clone(singleEntityPath?.[action]);
|
37
|
-
if (entityCollectionPath[action]) {
|
38
|
-
entityCollectionPath[action]['parameters'] = [
|
39
|
-
genericFilterParameterRef,
|
40
|
-
];
|
41
|
-
}
|
42
|
-
}
|
43
|
-
}
|
44
|
-
}
|
45
|
-
if (openAPIJson?.paths['/$batch']) {
|
46
|
-
delete openAPIJson.paths['/$batch'];
|
47
|
-
}
|
48
|
-
return openAPIJson;
|
49
|
-
};
|
50
|
-
//# sourceMappingURL=open-api-specification-generator.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"open-api-specification-generator.js","sourceRoot":"","sources":["../../src/odata-metadata/open-api-specification-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,aAAa,MAAM,eAAe,CAAC;AAE/C,OAAO,CAAC,MAAM,QAAQ,CAAC;AAEvB,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAC7C,SAAmD,EACnD,kBAAkB,GAAG,EAAE,EACvB,QAAQ,GAAG,EAAE,EACZ,EAAE;IAEH,MAAM,WAAW,GAAQ,aAAa,CAAC,YAAY,CAAC,SAAS,EAAE;QAC9D,MAAM,EAAE,OAAO;QACf,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,kBAAkB;QAC5B,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,CAAC;KACZ,CAAC,CAAC;IA2BH,MAAM,UAAU,GAAG,WAAW,EAAE,UAAU,EAAE,UAAU,CAAC;IACvD,UAAU,CAAC,QAAQ,CAAC,GAAG;QACtB,IAAI,EAAE,SAAS;QACf,WAAW,EACV,4JAA4J;QAC7J,EAAE,EAAE,OAAO;QACX,MAAM,EAAE;YACP,IAAI,EAAE,QAAQ;SACd;KACD,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QAElD,MAAM,UAAU,GACf,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAC1D,kBAAkB,CAClB,EAAE,MAAM,EAAE,UAAU,CAAC;QACvB,IAAI,UAAU,EAAE,KAAK,EAAE,CAAC;YACvB,UAAU,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC;YACnC,OAAO,UAAU,CAAC,KAAK,CAAC;QACzB,CAAC;QAGD,IAAI,UAAU,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC;YAClC,OAAO,UAAU,CAAC,cAAc,CAAC,CAAC;QACnC,CAAC;QAKD,MAAM,oBAAoB,GAAG,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QACrD,MAAM,gBAAgB,GAAG,WAAW,EAAE,KAAK,CAAC,GAAG,GAAG,QAAQ,CAAC,CAAC;QAC5D,IAAI,oBAAoB,IAAI,IAAI,IAAI,gBAAgB,IAAI,IAAI,EAAE,CAAC;YAC9D,MAAM,yBAAyB,GAAG;gBACjC,IAAI,EAAE,gCAAgC;aACtC,CAAC;YACF,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;gBAC1C,oBAAoB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;gBACnE,IAAI,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC;oBAClC,oBAAoB,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,GAAG;wBAC5C,yBAAyB;qBACzB,CAAC;gBACH,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;IAID,IAAI,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,OAAO,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,WAAW,CAAC;AACpB,CAAC,CAAC"}
|