@balena/pinejs 21.0.5-build-renovate-body-parser-2-x-9d6dece509c90b60fdbc72a2d1432b14096fb624-1 → 21.1.0-build-odata-metadata-json-395a55cb54e7b9ce0960ab93aad5f69d6c0e0462-2
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 +13 -8
- package/CHANGELOG.md +3 -3
- package/VERSION +1 -1
- package/out/odata-metadata/odata-metadata-generator.d.ts +61 -1
- package/out/odata-metadata/odata-metadata-generator.js +186 -97
- package/out/odata-metadata/odata-metadata-generator.js.map +1 -1
- package/out/odata-metadata/open-api-specification-generator.d.ts +2 -0
- package/out/odata-metadata/open-api-specification-generator.js +50 -0
- package/out/odata-metadata/open-api-specification-generator.js.map +1 -0
- package/out/sbvr-api/permissions.d.ts +4 -0
- package/out/sbvr-api/permissions.js +1 -1
- package/out/sbvr-api/permissions.js.map +1 -1
- package/out/sbvr-api/sbvr-utils.js +18 -3
- 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 +5 -3
- package/src/odata-metadata/odata-metadata-generator.ts +344 -126
- package/src/odata-metadata/open-api-specification-generator.ts +98 -0
- package/src/sbvr-api/permissions.ts +2 -2
- package/src/sbvr-api/sbvr-utils.ts +33 -3
- package/src/sbvr-api/uri-parser.ts +1 -1
- package/typings/odata-openapi.d.ts +6 -0
@@ -1,16 +1,21 @@
|
|
1
1
|
- commits:
|
2
|
-
- subject:
|
3
|
-
hash:
|
2
|
+
- subject: Return $metadata resource as odata + openapi spec
|
3
|
+
hash: f5a29ea44c6d1c6b933a727474329fad195ff6c9
|
4
4
|
body: |
|
5
|
-
|
5
|
+
Returning odata and openapi specs in json format.
|
6
|
+
Specs are scoped to the request permissions.
|
7
|
+
Different users (roles) will receive different metadata endpoints
|
8
|
+
and resources.
|
6
9
|
footer:
|
7
|
-
Change-type:
|
8
|
-
change-type:
|
9
|
-
|
10
|
+
Change-type: minor
|
11
|
+
change-type: minor
|
12
|
+
Signed-off-by: Harald Fischer <harald@balena.io>
|
13
|
+
signed-off-by: Harald Fischer <harald@balena.io>
|
14
|
+
author: Harald Fischer
|
10
15
|
nested: []
|
11
|
-
version: 21.0
|
16
|
+
version: 21.1.0
|
12
17
|
title: ""
|
13
|
-
date: 2025-
|
18
|
+
date: 2025-04-01T11:35:56.344Z
|
14
19
|
- commits:
|
15
20
|
- subject: Update minio/mc Docker tag to RELEASE.2024-11-21T17-21-54Z
|
16
21
|
hash: dcb916fe5dbfd46de8284678eed69ce91a04ea42
|
package/CHANGELOG.md
CHANGED
@@ -4,10 +4,10 @@ All notable changes to this project will be documented in this file
|
|
4
4
|
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
5
5
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
6
6
|
|
7
|
-
# v21.0
|
8
|
-
## (2025-
|
7
|
+
# v21.1.0
|
8
|
+
## (2025-04-01)
|
9
9
|
|
10
|
-
*
|
10
|
+
* Return $metadata resource as odata + openapi spec [Harald Fischer]
|
11
11
|
|
12
12
|
# v21.0.4
|
13
13
|
## (2025-03-31)
|
package/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
21.0
|
1
|
+
21.1.0
|
@@ -1,5 +1,65 @@
|
|
1
1
|
import type { AbstractSqlModel } from '@balena/abstract-sql-compiler';
|
2
|
+
import type { PermissionLookup } from '../sbvr-api/permissions.js';
|
3
|
+
type ODataCsdlV4References = {
|
4
|
+
[URI: string]: {
|
5
|
+
$Include: Array<{
|
6
|
+
$Namespace: string;
|
7
|
+
$Alias: string;
|
8
|
+
[annotation: string]: string | boolean;
|
9
|
+
}>;
|
10
|
+
};
|
11
|
+
};
|
12
|
+
type ODataCsdlV4BaseProperty = {
|
13
|
+
[annotation: string]: string | boolean | undefined;
|
14
|
+
$Type?: string;
|
15
|
+
$Nullable?: boolean;
|
16
|
+
};
|
17
|
+
type ODataCsdlV4StructuralProperty = ODataCsdlV4BaseProperty & {
|
18
|
+
$Kind?: 'Property';
|
19
|
+
};
|
20
|
+
type ODataCsdlV4NavigationProperty = ODataCsdlV4BaseProperty & {
|
21
|
+
$Kind: 'NavigationProperty';
|
22
|
+
$Partner?: string;
|
23
|
+
};
|
24
|
+
type ODataCsdlV4Property = ODataCsdlV4BaseProperty | ODataCsdlV4StructuralProperty | ODataCsdlV4NavigationProperty;
|
25
|
+
type ODataCsdlV4EntityType = {
|
26
|
+
$Kind: 'EntityType';
|
27
|
+
$Key: string[];
|
28
|
+
[property: string]: true | string[] | string | ODataCsdlV4Property;
|
29
|
+
};
|
30
|
+
type ODataCsdlV4EntityContainerEntries = {
|
31
|
+
$Type: string;
|
32
|
+
[property: string]: true | string | ODataCapabilitiesUDIRRestrictionsMethod;
|
33
|
+
};
|
34
|
+
type ODataCsdlV4EntityContainer = {
|
35
|
+
$Kind: 'EntityContainer';
|
36
|
+
'@Capabilities.BatchSupported'?: boolean;
|
37
|
+
[resourceOrAnnotation: string]: boolean | string | ODataCsdlV4EntityContainerEntries | undefined;
|
38
|
+
};
|
39
|
+
type ODataCsdlV4Schema = {
|
40
|
+
$Alias: string;
|
41
|
+
'@Core.DefaultNamespace': true;
|
42
|
+
[resource: string]: string | boolean | ODataCsdlV4EntityContainer | ODataCsdlV4EntityType;
|
43
|
+
};
|
44
|
+
type OdataCsdlV4 = {
|
45
|
+
$Version: string;
|
46
|
+
$Reference: ODataCsdlV4References;
|
47
|
+
$EntityContainer: string;
|
48
|
+
[schema: string]: string | ODataCsdlV4References | ODataCsdlV4Schema;
|
49
|
+
};
|
50
|
+
type ODataCapabilitiesUDIRRestrictionsMethod = {
|
51
|
+
Updatable: boolean;
|
52
|
+
} | {
|
53
|
+
Deletable: boolean;
|
54
|
+
} | {
|
55
|
+
Insertable: boolean;
|
56
|
+
} | {
|
57
|
+
Readable: boolean;
|
58
|
+
} | {
|
59
|
+
Filterable: boolean;
|
60
|
+
};
|
2
61
|
export declare const generateODataMetadata: {
|
3
|
-
(vocabulary: string, abstractSqlModel: AbstractSqlModel):
|
62
|
+
(vocabulary: string, abstractSqlModel: AbstractSqlModel, permissionsLookup?: PermissionLookup): OdataCsdlV4;
|
4
63
|
version: any;
|
5
64
|
};
|
65
|
+
export {};
|
@@ -1,5 +1,84 @@
|
|
1
1
|
import { sbvrTypes } from '../sbvr-api/sbvr-utils.js';
|
2
2
|
import { version } from '../config-loader/env.js';
|
3
|
+
const odataVocabularyReferences = {
|
4
|
+
'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.json': {
|
5
|
+
$Include: [
|
6
|
+
{
|
7
|
+
$Namespace: 'Org.OData.Core.V1',
|
8
|
+
$Alias: 'Core',
|
9
|
+
'@Core.DefaultNamespace': true,
|
10
|
+
},
|
11
|
+
],
|
12
|
+
},
|
13
|
+
'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Measures.V1.json': {
|
14
|
+
$Include: [
|
15
|
+
{
|
16
|
+
$Namespace: 'Org.OData.Measures.V1',
|
17
|
+
$Alias: 'Measures',
|
18
|
+
},
|
19
|
+
],
|
20
|
+
},
|
21
|
+
'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Aggregation.V1.json': {
|
22
|
+
$Include: [
|
23
|
+
{
|
24
|
+
$Namespace: 'Org.OData.Aggregation.V1',
|
25
|
+
$Alias: 'Aggregation',
|
26
|
+
},
|
27
|
+
],
|
28
|
+
},
|
29
|
+
'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.json': {
|
30
|
+
$Include: [
|
31
|
+
{
|
32
|
+
$Namespace: 'Org.OData.Capabilities.V1',
|
33
|
+
$Alias: 'Capabilities',
|
34
|
+
},
|
35
|
+
],
|
36
|
+
},
|
37
|
+
};
|
38
|
+
const restrictionsLookup = (method, value) => {
|
39
|
+
const lookup = {
|
40
|
+
update: {
|
41
|
+
'@Capabilities.UpdateRestrictions': {
|
42
|
+
Updatable: value,
|
43
|
+
},
|
44
|
+
'@Capabilities.FilterRestrictions': {
|
45
|
+
Filterable: true,
|
46
|
+
},
|
47
|
+
},
|
48
|
+
delete: {
|
49
|
+
'@Capabilities.DeleteRestrictions': {
|
50
|
+
Deletable: value,
|
51
|
+
},
|
52
|
+
'@Capabilities.FilterRestrictions': {
|
53
|
+
Filterable: true,
|
54
|
+
},
|
55
|
+
},
|
56
|
+
create: {
|
57
|
+
'@Capabilities.InsertRestrictions': {
|
58
|
+
Insertable: value,
|
59
|
+
},
|
60
|
+
},
|
61
|
+
read: {
|
62
|
+
'@Capabilities.ReadRestrictions': {
|
63
|
+
Readable: value,
|
64
|
+
},
|
65
|
+
'@Capabilities.FilterRestrictions': {
|
66
|
+
Filterable: true,
|
67
|
+
},
|
68
|
+
},
|
69
|
+
};
|
70
|
+
if (method === 'all') {
|
71
|
+
return {
|
72
|
+
...lookup['update'],
|
73
|
+
...lookup['delete'],
|
74
|
+
...lookup['create'],
|
75
|
+
...lookup['read'],
|
76
|
+
};
|
77
|
+
}
|
78
|
+
else {
|
79
|
+
return lookup[method] ?? {};
|
80
|
+
}
|
81
|
+
};
|
3
82
|
const getResourceName = (resourceName) => resourceName
|
4
83
|
.split('-')
|
5
84
|
.map((namePart) => namePart.split(' ').join('_'))
|
@@ -7,17 +86,47 @@ const getResourceName = (resourceName) => resourceName
|
|
7
86
|
const forEachUniqueTable = (model, callback) => {
|
8
87
|
const usedTableNames = {};
|
9
88
|
const result = [];
|
10
|
-
for (const
|
89
|
+
for (const key of Object.keys(model.abstractSqlModel.tables).sort()) {
|
90
|
+
const table = model.abstractSqlModel.tables[key];
|
11
91
|
if (typeof table !== 'string' &&
|
12
92
|
!table.primitive &&
|
13
|
-
!usedTableNames[table.name]
|
93
|
+
!usedTableNames[table.name] &&
|
94
|
+
model.preparedPermissionLookup) {
|
14
95
|
usedTableNames[table.name] = true;
|
15
96
|
result.push(callback(key, table));
|
16
97
|
}
|
17
98
|
}
|
18
99
|
return result;
|
19
100
|
};
|
20
|
-
|
101
|
+
const preparePermissionsLookup = (permissionLookup) => {
|
102
|
+
const resourcesAndOps = {};
|
103
|
+
for (const resourceOpsAuths of Object.keys(permissionLookup)) {
|
104
|
+
const [vocabulary, resource, rule] = resourceOpsAuths.split('.');
|
105
|
+
resourcesAndOps[vocabulary] ??= {};
|
106
|
+
resourcesAndOps[vocabulary][resource] ??= {
|
107
|
+
['read']: false,
|
108
|
+
['create']: false,
|
109
|
+
['update']: false,
|
110
|
+
['delete']: false,
|
111
|
+
};
|
112
|
+
if (rule === 'all' || (resource === 'all' && rule === undefined)) {
|
113
|
+
resourcesAndOps[vocabulary][resource] = {
|
114
|
+
['read']: true,
|
115
|
+
['create']: true,
|
116
|
+
['update']: true,
|
117
|
+
['delete']: true,
|
118
|
+
};
|
119
|
+
}
|
120
|
+
else if (rule === 'read' ||
|
121
|
+
rule === 'create' ||
|
122
|
+
rule === 'update' ||
|
123
|
+
rule === 'delete') {
|
124
|
+
resourcesAndOps[vocabulary][resource][rule] = true;
|
125
|
+
}
|
126
|
+
}
|
127
|
+
return resourcesAndOps;
|
128
|
+
};
|
129
|
+
export const generateODataMetadata = (vocabulary, abstractSqlModel, permissionsLookup) => {
|
21
130
|
const complexTypes = {};
|
22
131
|
const resolveDataType = (fieldType) => {
|
23
132
|
if (sbvrTypes[fieldType] == null) {
|
@@ -30,103 +139,83 @@ export const generateODataMetadata = (vocabulary, abstractSqlModel) => {
|
|
30
139
|
}
|
31
140
|
return sbvrTypes[fieldType].types.odata.name;
|
32
141
|
};
|
33
|
-
const
|
34
|
-
|
35
|
-
|
142
|
+
const prepPermissionsLookup = permissionsLookup
|
143
|
+
? preparePermissionsLookup(permissionsLookup)
|
144
|
+
: {};
|
145
|
+
const model = {
|
146
|
+
vocabulary,
|
147
|
+
abstractSqlModel,
|
148
|
+
preparedPermissionLookup: prepPermissionsLookup,
|
149
|
+
};
|
150
|
+
const metaBalenaEntries = {};
|
151
|
+
const entityContainer = {
|
152
|
+
$Kind: 'EntityContainer',
|
153
|
+
'@Capabilities.KeyAsSegmentSupported': false,
|
154
|
+
};
|
155
|
+
forEachUniqueTable(model, (_key, { idField, name: resourceName, fields }) => {
|
36
156
|
resourceName = getResourceName(resourceName);
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
157
|
+
const permissions = model?.preparedPermissionLookup?.['resource']?.['all'] ??
|
158
|
+
model?.preparedPermissionLookup?.[model.vocabulary]?.['all'] ??
|
159
|
+
model?.preparedPermissionLookup?.[model.vocabulary]?.[resourceName];
|
160
|
+
if (!permissions) {
|
161
|
+
return;
|
162
|
+
}
|
163
|
+
const uniqueTable = {
|
164
|
+
$Kind: 'EntityType',
|
165
|
+
$Key: [idField],
|
166
|
+
};
|
167
|
+
fields
|
168
|
+
.filter(({ dataType }) => dataType !== 'ForeignKey')
|
169
|
+
.map(({ dataType, fieldName, required }) => {
|
170
|
+
dataType = resolveDataType(dataType);
|
171
|
+
fieldName = getResourceName(fieldName);
|
172
|
+
uniqueTable[fieldName] = {
|
173
|
+
$Type: dataType,
|
174
|
+
$Nullable: !required,
|
175
|
+
'@Core.Computed': fieldName === 'created_at' || fieldName === 'modified_at'
|
176
|
+
? true
|
177
|
+
: false,
|
178
|
+
};
|
179
|
+
});
|
180
|
+
fields
|
181
|
+
.filter(({ dataType, references }) => dataType === 'ForeignKey' && references != null)
|
182
|
+
.map(({ fieldName, references, required }) => {
|
183
|
+
const { resourceName: referencedResource } = references;
|
184
|
+
const referencedResourceName = model.abstractSqlModel.tables[referencedResource]?.name;
|
185
|
+
const typeReference = referencedResourceName || referencedResource;
|
186
|
+
fieldName = getResourceName(fieldName);
|
187
|
+
uniqueTable[fieldName] = {
|
188
|
+
$Kind: 'NavigationProperty',
|
189
|
+
$Partner: resourceName,
|
190
|
+
$Nullable: !required,
|
191
|
+
$Type: vocabulary + '.' + getResourceName(typeReference),
|
192
|
+
};
|
193
|
+
});
|
194
|
+
metaBalenaEntries[resourceName] = uniqueTable;
|
195
|
+
let entityCon = {
|
196
|
+
$Collection: true,
|
197
|
+
$Type: vocabulary + '.' + resourceName,
|
198
|
+
};
|
199
|
+
for (const [resKey, resValue] of Object.entries(permissions)) {
|
200
|
+
entityCon = { ...entityCon, ...restrictionsLookup(resKey, resValue) };
|
48
201
|
}
|
202
|
+
entityContainer[resourceName] = entityCon;
|
49
203
|
});
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
<PropertyRef Name="${idField}" />
|
66
|
-
</Key>
|
67
|
-
|
68
|
-
` +
|
69
|
-
fields
|
70
|
-
.filter(({ dataType }) => dataType !== 'ForeignKey')
|
71
|
-
.map(({ dataType, fieldName, required }) => {
|
72
|
-
dataType = resolveDataType(dataType);
|
73
|
-
fieldName = getResourceName(fieldName);
|
74
|
-
return `<Property Name="${fieldName}" Type="${dataType}" Nullable="${!required}" />`;
|
75
|
-
})
|
76
|
-
.join('\n') +
|
77
|
-
'\n' +
|
78
|
-
fields
|
79
|
-
.filter(({ dataType, references }) => dataType === 'ForeignKey' && references != null)
|
80
|
-
.map(({ fieldName, references }) => {
|
81
|
-
const { resourceName: referencedResource } = references;
|
82
|
-
fieldName = getResourceName(fieldName);
|
83
|
-
return `<NavigationProperty Name="${fieldName}" Relationship="${vocabulary}.${resourceName + referencedResource}" FromRole="${resourceName}" ToRole="${referencedResource}" />`;
|
84
|
-
})
|
85
|
-
.join('\n') +
|
86
|
-
'\n' +
|
87
|
-
`
|
88
|
-
</EntityType>`);
|
89
|
-
}).join('\n\n') +
|
90
|
-
associations
|
91
|
-
.map(({ name, ends }) => {
|
92
|
-
name = getResourceName(name);
|
93
|
-
return (`<Association Name="${name}">` +
|
94
|
-
'\n\t' +
|
95
|
-
ends
|
96
|
-
.map(({ resourceName, cardinality }) => `<End Role="${resourceName}" Type="${vocabulary}.${resourceName}" Multiplicity="${cardinality}" />`)
|
97
|
-
.join('\n\t') +
|
98
|
-
'\n' +
|
99
|
-
`</Association>`);
|
100
|
-
})
|
101
|
-
.join('\n') +
|
102
|
-
`
|
103
|
-
<EntityContainer Name="${vocabulary}Service" m:IsDefaultEntityContainer="true">
|
104
|
-
|
105
|
-
` +
|
106
|
-
forEachUniqueTable(model, (_key, { name: resourceName }) => {
|
107
|
-
resourceName = getResourceName(resourceName);
|
108
|
-
return `<EntitySet Name="${resourceName}" EntityType="${vocabulary}.${resourceName}" />`;
|
109
|
-
}).join('\n') +
|
110
|
-
'\n' +
|
111
|
-
associations
|
112
|
-
.map(({ name, ends }) => {
|
113
|
-
name = getResourceName(name);
|
114
|
-
return (`<AssociationSet Name="${name}" Association="${vocabulary}.${name}">` +
|
115
|
-
'\n\t' +
|
116
|
-
ends
|
117
|
-
.map(({ resourceName }) => `<End Role="${resourceName}" EntitySet="${vocabulary}.${resourceName}" />`)
|
118
|
-
.join('\n\t') +
|
119
|
-
`
|
120
|
-
</AssociationSet>`);
|
121
|
-
})
|
122
|
-
.join('\n') +
|
123
|
-
`
|
124
|
-
</EntityContainer>` +
|
125
|
-
Object.values(complexTypes).join('\n') +
|
126
|
-
`
|
127
|
-
</Schema>
|
128
|
-
</edmx:DataServices>
|
129
|
-
</edmx:Edmx>`);
|
204
|
+
const odataCsdl = {
|
205
|
+
$Version: '3.0',
|
206
|
+
$EntityContainer: vocabulary + '.ODataApi',
|
207
|
+
$Reference: odataVocabularyReferences,
|
208
|
+
[vocabulary]: {
|
209
|
+
$Alias: vocabulary,
|
210
|
+
'@Core.DefaultNamespace': true,
|
211
|
+
'@Core.Description': `OpenAPI specification for PineJS served SBVR datamodel: ${vocabulary}`,
|
212
|
+
'@Core.LongDescription': 'Auto-Genrated OpenAPI specification by utilizing OData CSDL to OpenAPI spec transformer.',
|
213
|
+
'@Core.SchemaVersion': version,
|
214
|
+
...metaBalenaEntries,
|
215
|
+
['ODataApi']: entityContainer,
|
216
|
+
},
|
217
|
+
};
|
218
|
+
return odataCsdl;
|
130
219
|
};
|
131
220
|
generateODataMetadata.version = version;
|
132
221
|
//# sourceMappingURL=odata-metadata-generator.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"odata-metadata-generator.js","sourceRoot":"","sources":["../../src/odata-metadata/odata-metadata-generator.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;
|
1
|
+
{"version":3,"file":"odata-metadata-generator.js","sourceRoot":"","sources":["../../src/odata-metadata/odata-metadata-generator.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,50 @@
|
|
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
|
@@ -0,0 +1 @@
|
|
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"}
|
@@ -1,8 +1,10 @@
|
|
1
1
|
import type AuthModel from './user.js';
|
2
2
|
import './express-extension.js';
|
3
3
|
import type Express from 'express';
|
4
|
+
import type { ODataBinds } from '@balena/odata-parser';
|
4
5
|
import type { Tx } from '../database-layer/db.js';
|
5
6
|
import type { ApiKey, User } from '../sbvr-api/sbvr-utils.js';
|
7
|
+
import type { Dictionary } from './common-types.js';
|
6
8
|
import { type HookReq } from './hooks.js';
|
7
9
|
import { PermissionError, PermissionParsingError } from './errors.js';
|
8
10
|
import { type ODataRequest } from './uri-parser.js';
|
@@ -27,6 +29,7 @@ type MappedNestedCheck<T extends NestedCheck<I>, I, O> = T extends NestedCheckOr
|
|
27
29
|
export declare function nestedCheck<I extends string, O>(check: I, stringCallback: (s: string) => O): O;
|
28
30
|
export declare function nestedCheck<I extends boolean, O>(check: I, stringCallback: (s: string) => O): boolean;
|
29
31
|
export declare function nestedCheck<I extends NonNullable<unknown>, O>(check: NestedCheck<I>, stringCallback: (s: string) => O): Exclude<I, string> | O | MappedNestedCheck<typeof check, I, O>;
|
32
|
+
export type PermissionLookup = Dictionary<true | string[]>;
|
30
33
|
export declare const checkPassword: (username: string, password: string) => Promise<{
|
31
34
|
id: number;
|
32
35
|
actor: number;
|
@@ -50,6 +53,7 @@ export declare const customApiKeyMiddleware: (paramName?: string) => (req: HookR
|
|
50
53
|
export declare const apiKeyMiddleware: (req: HookReq | Express.Request, _res?: Express.Response, next?: Express.NextFunction) => Promise<void>;
|
51
54
|
export declare const checkPermissions: (req: PermissionReq, actionList: PermissionCheck, resourceName?: string, vocabulary?: string) => Promise<boolean | NestedCheck<string>>;
|
52
55
|
export declare const checkPermissionsMiddleware: (action: PermissionCheck) => Express.RequestHandler;
|
56
|
+
export declare const getReqPermissions: (req: PermissionReq, odataBinds?: ODataBinds) => Promise<PermissionLookup>;
|
53
57
|
export declare const addPermissions: (req: PermissionReq, request: ODataRequest & {
|
54
58
|
permissionType?: PermissionCheck;
|
55
59
|
}) => Promise<void>;
|
@@ -1055,7 +1055,7 @@ const getGuestPermissions = memoize(async () => {
|
|
1055
1055
|
guestPermissionsInitialized = true;
|
1056
1056
|
return guestPermissions;
|
1057
1057
|
}, { promise: true });
|
1058
|
-
const getReqPermissions = async (req, odataBinds = []) => {
|
1058
|
+
export const getReqPermissions = async (req, odataBinds = []) => {
|
1059
1059
|
const guestPermissions = await (async () => {
|
1060
1060
|
if (guestPermissionsInitialized === false &&
|
1061
1061
|
(req.user === root.user || req.user === rootRead.user)) {
|