@api-client/core 0.18.57 → 0.18.59
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/build/src/decorators/observed.d.ts.map +1 -1
- package/build/src/decorators/observed.js +15 -1
- package/build/src/decorators/observed.js.map +1 -1
- package/build/src/modeling/ApiModel.d.ts +7 -5
- package/build/src/modeling/ApiModel.d.ts.map +1 -1
- package/build/src/modeling/ApiModel.js +35 -16
- package/build/src/modeling/ApiModel.js.map +1 -1
- package/build/src/modeling/ExposedEntity.d.ts +5 -2
- package/build/src/modeling/ExposedEntity.d.ts.map +1 -1
- package/build/src/modeling/ExposedEntity.js +11 -8
- package/build/src/modeling/ExposedEntity.js.map +1 -1
- package/build/src/modeling/actions/Action.d.ts +41 -0
- package/build/src/modeling/actions/Action.d.ts.map +1 -0
- package/build/src/modeling/actions/Action.js +64 -0
- package/build/src/modeling/actions/Action.js.map +1 -0
- package/build/src/modeling/actions/CreateAction.d.ts +20 -0
- package/build/src/modeling/actions/CreateAction.d.ts.map +1 -0
- package/build/src/modeling/actions/CreateAction.js +43 -0
- package/build/src/modeling/actions/CreateAction.js.map +1 -0
- package/build/src/modeling/actions/DeleteAction.d.ts +36 -0
- package/build/src/modeling/actions/DeleteAction.d.ts.map +1 -0
- package/build/src/modeling/actions/DeleteAction.js +63 -0
- package/build/src/modeling/actions/DeleteAction.js.map +1 -0
- package/build/src/modeling/actions/ListAction.d.ts +39 -0
- package/build/src/modeling/actions/ListAction.d.ts.map +1 -0
- package/build/src/modeling/actions/ListAction.js +76 -0
- package/build/src/modeling/actions/ListAction.js.map +1 -0
- package/build/src/modeling/actions/ReadAction.d.ts +20 -0
- package/build/src/modeling/actions/ReadAction.d.ts.map +1 -0
- package/build/src/modeling/actions/ReadAction.js +43 -0
- package/build/src/modeling/actions/ReadAction.js.map +1 -0
- package/build/src/modeling/actions/SearchAction.d.ts +26 -0
- package/build/src/modeling/actions/SearchAction.d.ts.map +1 -0
- package/build/src/modeling/actions/SearchAction.js +53 -0
- package/build/src/modeling/actions/SearchAction.js.map +1 -0
- package/build/src/modeling/actions/UpdateAction.d.ts +29 -0
- package/build/src/modeling/actions/UpdateAction.d.ts.map +1 -0
- package/build/src/modeling/actions/UpdateAction.js +53 -0
- package/build/src/modeling/actions/UpdateAction.js.map +1 -0
- package/build/src/modeling/actions/index.d.ts +24 -0
- package/build/src/modeling/actions/index.d.ts.map +1 -0
- package/build/src/modeling/actions/index.js +9 -0
- package/build/src/modeling/actions/index.js.map +1 -0
- package/build/src/modeling/index.d.ts +12 -0
- package/build/src/modeling/index.d.ts.map +1 -0
- package/build/src/modeling/index.js +12 -0
- package/build/src/modeling/index.js.map +1 -0
- package/build/src/modeling/rules/AccessRule.d.ts +17 -0
- package/build/src/modeling/rules/AccessRule.d.ts.map +1 -0
- package/build/src/modeling/rules/AccessRule.js +19 -0
- package/build/src/modeling/rules/AccessRule.js.map +1 -0
- package/build/src/modeling/rules/AllowAuthenticated.d.ts +19 -0
- package/build/src/modeling/rules/AllowAuthenticated.d.ts.map +1 -0
- package/build/src/modeling/rules/AllowAuthenticated.js +14 -0
- package/build/src/modeling/rules/AllowAuthenticated.js.map +1 -0
- package/build/src/modeling/rules/AllowPublic.d.ts +19 -0
- package/build/src/modeling/rules/AllowPublic.d.ts.map +1 -0
- package/build/src/modeling/rules/AllowPublic.js +14 -0
- package/build/src/modeling/rules/AllowPublic.js.map +1 -0
- package/build/src/modeling/rules/MatchEmailDomain.d.ts +25 -0
- package/build/src/modeling/rules/MatchEmailDomain.d.ts.map +1 -0
- package/build/src/modeling/rules/MatchEmailDomain.js +40 -0
- package/build/src/modeling/rules/MatchEmailDomain.js.map +1 -0
- package/build/src/modeling/rules/MatchResourceOwner.d.ts +29 -0
- package/build/src/modeling/rules/MatchResourceOwner.d.ts.map +1 -0
- package/build/src/modeling/rules/MatchResourceOwner.js +40 -0
- package/build/src/modeling/rules/MatchResourceOwner.js.map +1 -0
- package/build/src/modeling/rules/MatchUserProperty.d.ts +28 -0
- package/build/src/modeling/rules/MatchUserProperty.d.ts.map +1 -0
- package/build/src/modeling/rules/MatchUserProperty.js +49 -0
- package/build/src/modeling/rules/MatchUserProperty.js.map +1 -0
- package/build/src/modeling/rules/MatchUserRole.d.ts +29 -0
- package/build/src/modeling/rules/MatchUserRole.d.ts.map +1 -0
- package/build/src/modeling/rules/MatchUserRole.js +40 -0
- package/build/src/modeling/rules/MatchUserRole.js.map +1 -0
- package/build/src/modeling/rules/RateLimitRule.d.ts +61 -0
- package/build/src/modeling/rules/RateLimitRule.d.ts.map +1 -0
- package/build/src/modeling/rules/RateLimitRule.js +101 -0
- package/build/src/modeling/rules/RateLimitRule.js.map +1 -0
- package/build/src/modeling/rules/RateLimitingConfiguration.d.ts +18 -0
- package/build/src/modeling/rules/RateLimitingConfiguration.d.ts.map +1 -0
- package/build/src/modeling/rules/RateLimitingConfiguration.js +35 -0
- package/build/src/modeling/rules/RateLimitingConfiguration.js.map +1 -0
- package/build/src/modeling/rules/index.d.ts +14 -0
- package/build/src/modeling/rules/index.d.ts.map +1 -0
- package/build/src/modeling/rules/index.js +11 -0
- package/build/src/modeling/rules/index.js.map +1 -0
- package/build/src/modeling/types.d.ts +6 -257
- package/build/src/modeling/types.d.ts.map +1 -1
- package/build/src/modeling/types.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/example-generator-api.json +6 -6
- package/package.json +1 -1
- package/src/decorators/observed.ts +15 -1
- package/src/modeling/ApiModel.ts +21 -19
- package/src/modeling/ExposedEntity.ts +13 -18
- package/src/modeling/actions/Action.ts +64 -0
- package/src/modeling/actions/CreateAction.ts +38 -0
- package/src/modeling/actions/DeleteAction.ts +59 -0
- package/src/modeling/actions/ListAction.ts +66 -0
- package/src/modeling/actions/ReadAction.ts +40 -0
- package/src/modeling/actions/SearchAction.ts +46 -0
- package/src/modeling/actions/UpdateAction.ts +49 -0
- package/src/modeling/rules/AccessRule.ts +29 -0
- package/src/modeling/rules/AllowAuthenticated.ts +24 -0
- package/src/modeling/rules/AllowPublic.ts +24 -0
- package/src/modeling/rules/MatchEmailDomain.ts +39 -0
- package/src/modeling/rules/MatchResourceOwner.ts +43 -0
- package/src/modeling/rules/MatchUserProperty.ts +44 -0
- package/src/modeling/rules/MatchUserRole.ts +43 -0
- package/src/modeling/rules/RateLimitRule.ts +104 -0
- package/src/modeling/rules/RateLimitingConfiguration.ts +32 -0
- package/src/modeling/types.ts +6 -276
- package/tests/unit/decorators/observed.spec.ts +42 -0
- package/tests/unit/modeling/actions/Action.spec.ts +109 -0
- package/tests/unit/modeling/actions/CreateAction.spec.ts +65 -0
- package/tests/unit/modeling/actions/DeleteAction.spec.ts +78 -0
- package/tests/unit/modeling/actions/ListAction.spec.ts +106 -0
- package/tests/unit/modeling/actions/ReadAction.spec.ts +77 -0
- package/tests/unit/modeling/actions/SearchAction.spec.ts +73 -0
- package/tests/unit/modeling/actions/UpdateAction.spec.ts +73 -0
- package/tests/unit/modeling/api_model.spec.ts +48 -3
- package/tests/unit/modeling/exposed_entity.spec.ts +73 -0
- package/tests/unit/modeling/rules/AccessRule.spec.ts +42 -0
- package/tests/unit/modeling/rules/AllowAuthenticated.spec.ts +28 -0
- package/tests/unit/modeling/rules/AllowPublic.spec.ts +28 -0
- package/tests/unit/modeling/rules/MatchEmailDomain.spec.ts +52 -0
- package/tests/unit/modeling/rules/MatchResourceOwner.spec.ts +37 -0
- package/tests/unit/modeling/rules/MatchUserProperty.spec.ts +58 -0
- package/tests/unit/modeling/rules/MatchUserRole.spec.ts +52 -0
- package/tests/unit/modeling/rules/RateLimitRule.spec.ts +70 -0
- package/tests/unit/modeling/rules/RateLimitingConfiguration.spec.ts +61 -0
|
@@ -42062,10 +42062,10 @@
|
|
|
42062
42062
|
"@id": "#209"
|
|
42063
42063
|
},
|
|
42064
42064
|
{
|
|
42065
|
-
"@id": "#
|
|
42065
|
+
"@id": "#191"
|
|
42066
42066
|
},
|
|
42067
42067
|
{
|
|
42068
|
-
"@id": "#
|
|
42068
|
+
"@id": "#194"
|
|
42069
42069
|
},
|
|
42070
42070
|
{
|
|
42071
42071
|
"@id": "#197"
|
|
@@ -43436,7 +43436,7 @@
|
|
|
43436
43436
|
"doc:ExternalDomainElement",
|
|
43437
43437
|
"doc:DomainElement"
|
|
43438
43438
|
],
|
|
43439
|
-
"doc:raw": "
|
|
43439
|
+
"doc:raw": "countryCode: \"BE\"\ngraydonEnterpriseId: 1057155523\nregistrationId: \"0422319093\"\nvatNumber: \"BE0422319093\"\ngraydonCompanyId: \"0422319093\"\nisBranchOffice: false\n",
|
|
43440
43440
|
"core:mediaType": "application/yaml",
|
|
43441
43441
|
"sourcemaps:sources": [
|
|
43442
43442
|
{
|
|
@@ -43457,7 +43457,7 @@
|
|
|
43457
43457
|
"doc:ExternalDomainElement",
|
|
43458
43458
|
"doc:DomainElement"
|
|
43459
43459
|
],
|
|
43460
|
-
"doc:raw": "
|
|
43460
|
+
"doc:raw": "addressType: 'REGISTERED-OFFICE-ADDRESS'\nstreetName: 'UITBREIDINGSTRAAT'\nhouseNumber: '84'\nhouseNumberAddition: '/1'\npostalCode: '2600'\ncity: 'BERCHEM (ANTWERPEN)'\ncountry: 'Belgium'\ncountryCode: 'BE'\nfullFormatedAddress: \"UITBREIDINGSTRAAT 84 /1, 2600 BERCHEM (ANTWERPEN), BELIUM\"\n",
|
|
43461
43461
|
"core:mediaType": "application/yaml",
|
|
43462
43462
|
"sourcemaps:sources": [
|
|
43463
43463
|
{
|
|
@@ -44756,12 +44756,12 @@
|
|
|
44756
44756
|
{
|
|
44757
44757
|
"@id": "#193/source-map/lexical/element_0",
|
|
44758
44758
|
"sourcemaps:element": "amf://id#193",
|
|
44759
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44759
|
+
"sourcemaps:value": "[(1,0)-(7,0)]"
|
|
44760
44760
|
},
|
|
44761
44761
|
{
|
|
44762
44762
|
"@id": "#196/source-map/lexical/element_0",
|
|
44763
44763
|
"sourcemaps:element": "amf://id#196",
|
|
44764
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44764
|
+
"sourcemaps:value": "[(1,0)-(10,0)]"
|
|
44765
44765
|
},
|
|
44766
44766
|
{
|
|
44767
44767
|
"@id": "#199/source-map/lexical/element_0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
const reactiveSymbol = Symbol('reactive')
|
|
3
3
|
const proxySymbol = Symbol('proxy')
|
|
4
|
+
const rawSymbol = Symbol('raw')
|
|
4
5
|
|
|
5
6
|
interface DomainInstance {
|
|
6
7
|
domain?: { notifyChange(): void }
|
|
@@ -38,6 +39,13 @@ export interface ObserveConfig {
|
|
|
38
39
|
* @returns The not proxied object or undefined
|
|
39
40
|
*/
|
|
40
41
|
export function toRaw<T extends object = object>(source: object, target: T): T | undefined {
|
|
42
|
+
if (target && (target as any)[rawSymbol]) {
|
|
43
|
+
let raw = (target as any)[rawSymbol]
|
|
44
|
+
while (raw && (raw as any)[rawSymbol]) {
|
|
45
|
+
raw = (raw as any)[rawSymbol]
|
|
46
|
+
}
|
|
47
|
+
return raw as T
|
|
48
|
+
}
|
|
41
49
|
const proxies = Reflect.get(source, proxySymbol) as Map<object, object>
|
|
42
50
|
if (!proxies) {
|
|
43
51
|
return undefined
|
|
@@ -101,7 +109,7 @@ export function observed(config: ObserveConfig = {}): PropertyDecorator {
|
|
|
101
109
|
}
|
|
102
110
|
|
|
103
111
|
if (proxies.has(obj)) {
|
|
104
|
-
return obj
|
|
112
|
+
return proxies.get(obj)
|
|
105
113
|
}
|
|
106
114
|
|
|
107
115
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
@@ -109,6 +117,12 @@ export function observed(config: ObserveConfig = {}): PropertyDecorator {
|
|
|
109
117
|
|
|
110
118
|
const proxy = new Proxy(obj, {
|
|
111
119
|
get(target, prop) {
|
|
120
|
+
if (prop === proxySymbol) {
|
|
121
|
+
return Reflect.get(target, prop)
|
|
122
|
+
}
|
|
123
|
+
if (prop === rawSymbol) {
|
|
124
|
+
return target
|
|
125
|
+
}
|
|
112
126
|
const value = Reflect.get(target, prop)
|
|
113
127
|
return createDeepProxy.bind(targetObject)(value, notifyChange)
|
|
114
128
|
},
|
package/src/modeling/ApiModel.ts
CHANGED
|
@@ -2,12 +2,10 @@ import { nanoid } from '../nanoid.js'
|
|
|
2
2
|
import { ApiModelKind, DataDomainKind, ExposedEntityKind } from '../models/kinds.js'
|
|
3
3
|
import { type IThing, Thing } from '../models/Thing.js'
|
|
4
4
|
import type {
|
|
5
|
-
AccessRule,
|
|
6
5
|
AssociationTarget,
|
|
7
6
|
AuthenticationConfiguration,
|
|
8
7
|
AuthorizationConfiguration,
|
|
9
8
|
ExposedEntitySchema,
|
|
10
|
-
RateLimitingConfiguration,
|
|
11
9
|
RolesBasedAccessControl,
|
|
12
10
|
SessionConfiguration,
|
|
13
11
|
UsernamePasswordConfiguration,
|
|
@@ -19,6 +17,8 @@ import { observed, toRaw } from '../decorators/observed.js'
|
|
|
19
17
|
import pluralize from '@jarrodek/pluralize'
|
|
20
18
|
import { createDomainKey } from './helpers/keying.js'
|
|
21
19
|
import { ExposedEntity } from './ExposedEntity.js'
|
|
20
|
+
import { AccessRule, AccessRuleSchema } from './rules/AccessRule.js'
|
|
21
|
+
import { RateLimitingConfiguration, RateLimitingConfigurationSchema } from './rules/RateLimitingConfiguration.js'
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Contact information for the exposed API.
|
|
@@ -106,12 +106,12 @@ export interface ApiModelSchema extends DependentModelSchema {
|
|
|
106
106
|
* These rules apply to all exposed entities and actions. An API action
|
|
107
107
|
* can declare its own access rules, which will override these.
|
|
108
108
|
*/
|
|
109
|
-
accessRule?:
|
|
109
|
+
accessRule?: AccessRuleSchema[]
|
|
110
110
|
/**
|
|
111
111
|
* Optional configuration for API-wide rate limiting and throttling.
|
|
112
112
|
* Defines rules to protect the API from overuse.
|
|
113
113
|
*/
|
|
114
|
-
rateLimiting?:
|
|
114
|
+
rateLimiting?: RateLimitingConfigurationSchema
|
|
115
115
|
/**
|
|
116
116
|
* A URL to the Terms of Service for the API.
|
|
117
117
|
*/
|
|
@@ -179,12 +179,12 @@ export class ApiModel extends DependentModel {
|
|
|
179
179
|
* These rules apply to all exposed entities and actions. An API action
|
|
180
180
|
* can declare its own access rules, which will override these.
|
|
181
181
|
*/
|
|
182
|
-
accessRule
|
|
182
|
+
@observed({ deep: true }) accessor accessRule: AccessRule[]
|
|
183
183
|
/**
|
|
184
184
|
* Optional configuration for API-wide rate limiting and throttling.
|
|
185
185
|
* Defines rules to protect the API from overuse.
|
|
186
186
|
*/
|
|
187
|
-
rateLimiting
|
|
187
|
+
@observed() accessor rateLimiting: RateLimitingConfiguration | undefined
|
|
188
188
|
/**
|
|
189
189
|
* A URL to the Terms of Service for the API.
|
|
190
190
|
*/
|
|
@@ -238,25 +238,25 @@ export class ApiModel extends DependentModel {
|
|
|
238
238
|
exposes,
|
|
239
239
|
}
|
|
240
240
|
if (input.user) {
|
|
241
|
-
result.user =
|
|
241
|
+
result.user = structuredClone(input.user)
|
|
242
242
|
}
|
|
243
243
|
if (input.dependencyList) {
|
|
244
244
|
result.dependencyList = structuredClone(input.dependencyList)
|
|
245
245
|
}
|
|
246
246
|
if (input.authentication) {
|
|
247
|
-
result.authentication = input.authentication
|
|
247
|
+
result.authentication = structuredClone(input.authentication)
|
|
248
248
|
}
|
|
249
249
|
if (input.authorization) {
|
|
250
|
-
result.authorization = input.authorization
|
|
250
|
+
result.authorization = structuredClone(input.authorization)
|
|
251
251
|
}
|
|
252
252
|
if (input.session) {
|
|
253
|
-
result.session = input.session
|
|
253
|
+
result.session = structuredClone(input.session)
|
|
254
254
|
}
|
|
255
|
-
if (input.accessRule) {
|
|
256
|
-
result.accessRule = input.accessRule
|
|
255
|
+
if (Array.isArray(input.accessRule)) {
|
|
256
|
+
result.accessRule = structuredClone(input.accessRule)
|
|
257
257
|
}
|
|
258
258
|
if (input.rateLimiting) {
|
|
259
|
-
result.rateLimiting = input.rateLimiting
|
|
259
|
+
result.rateLimiting = structuredClone(input.rateLimiting)
|
|
260
260
|
}
|
|
261
261
|
if (input.termsOfService) {
|
|
262
262
|
result.termsOfService = input.termsOfService
|
|
@@ -309,11 +309,13 @@ export class ApiModel extends DependentModel {
|
|
|
309
309
|
} else {
|
|
310
310
|
this.exposes = []
|
|
311
311
|
}
|
|
312
|
-
if (init.accessRule) {
|
|
313
|
-
this.accessRule =
|
|
312
|
+
if (Array.isArray(init.accessRule)) {
|
|
313
|
+
this.accessRule = init.accessRule.map((rule) => new AccessRule(rule))
|
|
314
|
+
} else {
|
|
315
|
+
this.accessRule = []
|
|
314
316
|
}
|
|
315
317
|
if (init.rateLimiting) {
|
|
316
|
-
this.rateLimiting =
|
|
318
|
+
this.rateLimiting = new RateLimitingConfiguration(init.rateLimiting)
|
|
317
319
|
}
|
|
318
320
|
if (init.termsOfService) {
|
|
319
321
|
this.termsOfService = init.termsOfService
|
|
@@ -352,11 +354,11 @@ export class ApiModel extends DependentModel {
|
|
|
352
354
|
if (this.session) {
|
|
353
355
|
result.session = structuredClone(this.session)
|
|
354
356
|
}
|
|
355
|
-
if (this.accessRule) {
|
|
356
|
-
result.accessRule =
|
|
357
|
+
if (Array.isArray(this.accessRule) && this.accessRule.length > 0) {
|
|
358
|
+
result.accessRule = this.accessRule.map((rule) => rule.toJSON())
|
|
357
359
|
}
|
|
358
360
|
if (this.rateLimiting) {
|
|
359
|
-
result.rateLimiting =
|
|
361
|
+
result.rateLimiting = this.rateLimiting.toJSON()
|
|
360
362
|
}
|
|
361
363
|
if (this.termsOfService) {
|
|
362
364
|
result.termsOfService = this.termsOfService
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { observed } from '../decorators/observed.js'
|
|
2
2
|
import { ExposedEntityKind } from '../models/kinds.js'
|
|
3
3
|
import { nanoid } from '../nanoid.js'
|
|
4
|
+
import { Action } from './actions/Action.js'
|
|
4
5
|
import type { ApiModel } from './ApiModel.js'
|
|
5
6
|
import { ensureLeadingSlash, joinPaths } from './helpers/endpointHelpers.js'
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
AssociationTarget,
|
|
10
|
-
ExposeOptions,
|
|
11
|
-
ExposeParentRef,
|
|
12
|
-
RateLimitingConfiguration,
|
|
13
|
-
ExposedEntitySchema,
|
|
14
|
-
} from './types.js'
|
|
7
|
+
import { AccessRule } from './rules/AccessRule.js'
|
|
8
|
+
import { RateLimitingConfiguration } from './rules/RateLimitingConfiguration.js'
|
|
9
|
+
import type { AssociationTarget, ExposeOptions, ExposeParentRef, ExposedEntitySchema } from './types.js'
|
|
15
10
|
|
|
16
11
|
/**
|
|
17
12
|
* A class that specializes in representing an exposed Data Entity within an API Model.
|
|
@@ -91,7 +86,7 @@ export class ExposedEntity extends EventTarget {
|
|
|
91
86
|
/**
|
|
92
87
|
* The list of enabled API actions for this exposure (List/Read/Create/etc.)
|
|
93
88
|
*/
|
|
94
|
-
@observed({ deep: true }) accessor actions:
|
|
89
|
+
@observed({ deep: true }) accessor actions: Action[]
|
|
95
90
|
|
|
96
91
|
/**
|
|
97
92
|
* Optional array of access rules that define the access control policies for this exposure.
|
|
@@ -162,7 +157,7 @@ export class ExposedEntity extends EventTarget {
|
|
|
162
157
|
if (exposeOptions !== undefined) {
|
|
163
158
|
result.exposeOptions = { ...exposeOptions }
|
|
164
159
|
}
|
|
165
|
-
if (accessRule
|
|
160
|
+
if (Array.isArray(accessRule)) {
|
|
166
161
|
result.accessRule = accessRule.map((ar) => ({ ...ar }))
|
|
167
162
|
}
|
|
168
163
|
if (rateLimiting !== undefined) {
|
|
@@ -187,9 +182,9 @@ export class ExposedEntity extends EventTarget {
|
|
|
187
182
|
this.isRoot = init.isRoot
|
|
188
183
|
this.parent = init.parent
|
|
189
184
|
this.exposeOptions = init.exposeOptions
|
|
190
|
-
this.actions = init.actions
|
|
191
|
-
this.accessRule = init.accessRule
|
|
192
|
-
this.rateLimiting = init.rateLimiting
|
|
185
|
+
this.actions = init.actions ? init.actions.map((a) => new Action(a)) : []
|
|
186
|
+
this.accessRule = init.accessRule ? init.accessRule.map((ar) => new AccessRule(ar)) : []
|
|
187
|
+
this.rateLimiting = init.rateLimiting ? new RateLimitingConfiguration(init.rateLimiting) : undefined
|
|
193
188
|
this.truncated = init.truncated
|
|
194
189
|
this.#initializing = false
|
|
195
190
|
}
|
|
@@ -212,7 +207,7 @@ export class ExposedEntity extends EventTarget {
|
|
|
212
207
|
key: this.key,
|
|
213
208
|
entity: { ...this.entity },
|
|
214
209
|
resourcePath: this.resourcePath,
|
|
215
|
-
actions: this.actions.map((a) => (
|
|
210
|
+
actions: this.actions.map((a) => a.toJSON()),
|
|
216
211
|
hasCollection: this.hasCollection,
|
|
217
212
|
}
|
|
218
213
|
if (this.collectionPath !== undefined) {
|
|
@@ -227,11 +222,11 @@ export class ExposedEntity extends EventTarget {
|
|
|
227
222
|
if (this.exposeOptions !== undefined) {
|
|
228
223
|
result.exposeOptions = { ...this.exposeOptions }
|
|
229
224
|
}
|
|
230
|
-
if (this.accessRule
|
|
231
|
-
result.accessRule = this.accessRule.map((ar) => (
|
|
225
|
+
if (Array.isArray(this.accessRule) && this.accessRule.length > 0) {
|
|
226
|
+
result.accessRule = this.accessRule.map((ar) => ar.toJSON())
|
|
232
227
|
}
|
|
233
228
|
if (this.rateLimiting !== undefined) {
|
|
234
|
-
result.rateLimiting =
|
|
229
|
+
result.rateLimiting = this.rateLimiting.toJSON()
|
|
235
230
|
}
|
|
236
231
|
if (this.truncated !== undefined) {
|
|
237
232
|
result.truncated = this.truncated
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { AccessRule, AccessRuleSchema } from '../rules/AccessRule.js'
|
|
2
|
+
import { RateLimitingConfiguration, RateLimitingConfigurationSchema } from '../rules/RateLimitingConfiguration.js'
|
|
3
|
+
import { observed } from '../../decorators/observed.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A base interface for common properties across all actions.
|
|
7
|
+
*/
|
|
8
|
+
export interface ActionSchema {
|
|
9
|
+
/**
|
|
10
|
+
* The specific kind of action (e.g., 'List', 'Read', etc.)
|
|
11
|
+
*/
|
|
12
|
+
kind: string
|
|
13
|
+
/**
|
|
14
|
+
* Access control rules defining who can perform this action. It is only applied if the
|
|
15
|
+
* authorization strategy is set to 'RBAC'.
|
|
16
|
+
* If no rules grant access, it's denied by default making it essentially private.
|
|
17
|
+
*
|
|
18
|
+
* Note, the API can defined top level access rules that apply to all actions. If this property is set,
|
|
19
|
+
* it overrides the top level access rules for this specific action.
|
|
20
|
+
*
|
|
21
|
+
* It is an ordered list, meaning the first rule that matches the user will be applied.
|
|
22
|
+
* If multiple rules match, the first one in the list takes precedence.
|
|
23
|
+
* If no rules match, the action is denied.
|
|
24
|
+
*/
|
|
25
|
+
accessRule?: AccessRuleSchema[]
|
|
26
|
+
/**
|
|
27
|
+
* Optional configuration for action-wide rate limiting and throttling.
|
|
28
|
+
* Defines rules to protect the action from overuse.
|
|
29
|
+
*/
|
|
30
|
+
rateLimiting?: RateLimitingConfigurationSchema
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A base class for common properties across all actions.
|
|
35
|
+
*/
|
|
36
|
+
export class Action extends EventTarget implements ActionSchema {
|
|
37
|
+
@observed() accessor kind: string
|
|
38
|
+
@observed({ deep: true }) accessor accessRule: AccessRule[]
|
|
39
|
+
@observed({ deep: true }) accessor rateLimiting: RateLimitingConfiguration | undefined
|
|
40
|
+
|
|
41
|
+
constructor(state: Partial<ActionSchema> = {}) {
|
|
42
|
+
super()
|
|
43
|
+
this.kind = state.kind || ''
|
|
44
|
+
this.accessRule = state.accessRule ? state.accessRule.map((rule) => new AccessRule(rule)) : []
|
|
45
|
+
this.rateLimiting = state.rateLimiting ? new RateLimitingConfiguration(state.rateLimiting) : undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
notifyChange() {
|
|
49
|
+
this.dispatchEvent(new Event('change'))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
toJSON(): ActionSchema {
|
|
53
|
+
const result: ActionSchema = {
|
|
54
|
+
kind: this.kind,
|
|
55
|
+
}
|
|
56
|
+
if (this.accessRule.length) {
|
|
57
|
+
result.accessRule = this.accessRule.map((rule) => rule.toJSON())
|
|
58
|
+
}
|
|
59
|
+
if (this.rateLimiting) {
|
|
60
|
+
result.rateLimiting = this.rateLimiting.toJSON()
|
|
61
|
+
}
|
|
62
|
+
return result
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Action, type ActionSchema } from './Action.js'
|
|
2
|
+
import { observed } from '../../decorators/observed.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Enables adding a new resource to a collection.
|
|
6
|
+
* Endpoint: POST /[entity-collection-name]
|
|
7
|
+
*/
|
|
8
|
+
export interface CreateActionSchema extends ActionSchema {
|
|
9
|
+
kind: 'create'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Enables adding a new resource to a collection.
|
|
14
|
+
* Endpoint: POST /[entity-collection-name]
|
|
15
|
+
*/
|
|
16
|
+
export class CreateAction extends Action implements CreateActionSchema {
|
|
17
|
+
@observed() override accessor kind: 'create'
|
|
18
|
+
|
|
19
|
+
constructor(state: Partial<CreateActionSchema> = {}) {
|
|
20
|
+
super(state)
|
|
21
|
+
this.kind = 'create'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
override toJSON(): CreateActionSchema {
|
|
25
|
+
return {
|
|
26
|
+
...(super.toJSON() as CreateActionSchema),
|
|
27
|
+
kind: 'create',
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static isCreateAction(action: Action): action is CreateAction {
|
|
32
|
+
return action.kind === 'create'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static isCreateActionSchema(schema: ActionSchema): schema is CreateActionSchema {
|
|
36
|
+
return schema.kind === 'create'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Action, type ActionSchema } from './Action.js'
|
|
2
|
+
import { observed } from '../../decorators/observed.js'
|
|
3
|
+
|
|
4
|
+
export type DeleteStrategy = 'soft' | 'hard'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Enables removing an existing resource.
|
|
8
|
+
* Endpoint: DELETE /[entity-collection-name]/{id}
|
|
9
|
+
*/
|
|
10
|
+
export interface DeleteActionSchema extends ActionSchema {
|
|
11
|
+
kind: 'delete'
|
|
12
|
+
/**
|
|
13
|
+
* The strategy for deletion. Default: Soft Delete.
|
|
14
|
+
*
|
|
15
|
+
* @default 'soft'
|
|
16
|
+
*/
|
|
17
|
+
strategy?: DeleteStrategy
|
|
18
|
+
/**
|
|
19
|
+
* The data retention period (in days) for soft-deleted resources.
|
|
20
|
+
* This defines how long the data should be kept before it is permanently deleted.
|
|
21
|
+
*
|
|
22
|
+
* @default 30
|
|
23
|
+
*/
|
|
24
|
+
retentionPeriod?: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Enables removing an existing resource.
|
|
29
|
+
* Endpoint: DELETE /[entity-collection-name]/{id}
|
|
30
|
+
*/
|
|
31
|
+
export class DeleteAction extends Action implements DeleteActionSchema {
|
|
32
|
+
@observed() override accessor kind: 'delete'
|
|
33
|
+
@observed() accessor strategy: DeleteStrategy
|
|
34
|
+
@observed() accessor retentionPeriod: number
|
|
35
|
+
|
|
36
|
+
constructor(state: Partial<DeleteActionSchema> = {}) {
|
|
37
|
+
super(state)
|
|
38
|
+
this.kind = 'delete'
|
|
39
|
+
this.strategy = state.strategy || 'soft'
|
|
40
|
+
this.retentionPeriod = state.retentionPeriod ?? 30
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
override toJSON(): DeleteActionSchema {
|
|
44
|
+
return {
|
|
45
|
+
...(super.toJSON() as DeleteActionSchema),
|
|
46
|
+
kind: 'delete',
|
|
47
|
+
strategy: this.strategy,
|
|
48
|
+
retentionPeriod: this.retentionPeriod,
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static isDeleteAction(action: Action): action is DeleteAction {
|
|
53
|
+
return action.kind === 'delete'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static isDeleteActionSchema(schema: ActionSchema): schema is DeleteActionSchema {
|
|
57
|
+
return schema.kind === 'delete'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { PaginationStrategy } from '../types.js'
|
|
2
|
+
import { Action, type ActionSchema } from './Action.js'
|
|
3
|
+
import { observed, toRaw } from '../../decorators/observed.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Enables retrieving a collection of resources.
|
|
7
|
+
* Endpoint: GET /[entity-collection-name]
|
|
8
|
+
*/
|
|
9
|
+
export interface ListActionSchema extends ActionSchema {
|
|
10
|
+
kind: 'list'
|
|
11
|
+
/**
|
|
12
|
+
* The pagination strategy used for this action.
|
|
13
|
+
* This defines how the results are paginated when retrieving a collection of resources.
|
|
14
|
+
* It can be either 'cursor' or 'offset'.
|
|
15
|
+
*/
|
|
16
|
+
pagination: PaginationStrategy
|
|
17
|
+
/**
|
|
18
|
+
* Fields from the entity that can be used for filtering.
|
|
19
|
+
* Must be marked as "indexable" in the Data Model.
|
|
20
|
+
*/
|
|
21
|
+
filterableFields: string[]
|
|
22
|
+
/**
|
|
23
|
+
* Fields from the entity that can be used for sorting.
|
|
24
|
+
*/
|
|
25
|
+
sortableFields: string[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Enables retrieving a collection of resources.
|
|
30
|
+
* Endpoint: GET /[entity-collection-name]
|
|
31
|
+
*/
|
|
32
|
+
export class ListAction extends Action implements ListActionSchema {
|
|
33
|
+
@observed() override accessor kind: 'list'
|
|
34
|
+
@observed({ deep: true }) accessor pagination: PaginationStrategy
|
|
35
|
+
@observed({ deep: true }) accessor filterableFields: string[] = []
|
|
36
|
+
@observed({ deep: true }) accessor sortableFields: string[] = []
|
|
37
|
+
|
|
38
|
+
constructor(state: Partial<ListActionSchema> = {}) {
|
|
39
|
+
super(state)
|
|
40
|
+
this.kind = state.kind || 'list'
|
|
41
|
+
this.pagination = state.pagination
|
|
42
|
+
? { ...state.pagination }
|
|
43
|
+
: {
|
|
44
|
+
kind: 'offset',
|
|
45
|
+
}
|
|
46
|
+
this.filterableFields = state.filterableFields ? [...state.filterableFields] : []
|
|
47
|
+
this.sortableFields = state.sortableFields ? [...state.sortableFields] : []
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
override toJSON(): ListActionSchema {
|
|
51
|
+
return {
|
|
52
|
+
...(super.toJSON() as ListActionSchema),
|
|
53
|
+
pagination: structuredClone(toRaw(this, this.pagination)) as PaginationStrategy,
|
|
54
|
+
filterableFields: structuredClone(toRaw(this, this.filterableFields)) as string[],
|
|
55
|
+
sortableFields: structuredClone(toRaw(this, this.sortableFields)) as string[],
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static isListAction(action: Action): action is ListAction {
|
|
60
|
+
return action.kind === 'list'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static isListActionSchema(schema: ActionSchema): schema is ListActionSchema {
|
|
64
|
+
return schema.kind === 'list'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Action, type ActionSchema } from './Action.js'
|
|
2
|
+
import { observed } from '../../decorators/observed.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Enables retrieving a single resource by its ID.
|
|
6
|
+
* Endpoint: GET /[entity-collection-name]/{id}
|
|
7
|
+
*/
|
|
8
|
+
export interface ReadActionSchema extends ActionSchema {
|
|
9
|
+
kind: 'read'
|
|
10
|
+
// Association handling (Link IDs vs. Embed) is defined on the
|
|
11
|
+
// data association itself in the Data Modeler.
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Enables retrieving a single resource by its ID.
|
|
16
|
+
* Endpoint: GET /[entity-collection-name]/{id}
|
|
17
|
+
*/
|
|
18
|
+
export class ReadAction extends Action implements ReadActionSchema {
|
|
19
|
+
@observed() override accessor kind: 'read'
|
|
20
|
+
|
|
21
|
+
constructor(state: Partial<ReadActionSchema> = {}) {
|
|
22
|
+
super(state)
|
|
23
|
+
this.kind = 'read'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override toJSON(): ReadActionSchema {
|
|
27
|
+
return {
|
|
28
|
+
...(super.toJSON() as ReadActionSchema),
|
|
29
|
+
kind: 'read',
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static isReadAction(action: Action): action is ReadAction {
|
|
34
|
+
return action.kind === 'read'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static isReadActionSchema(schema: ActionSchema): schema is ReadActionSchema {
|
|
38
|
+
return schema.kind === 'read'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Action, type ActionSchema } from './Action.js'
|
|
2
|
+
import { observed, toRaw } from '../../decorators/observed.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Enables keyword-based search across specified fields.
|
|
6
|
+
* Endpoint: GET /[entity-collection-name]/search
|
|
7
|
+
*/
|
|
8
|
+
export interface SearchActionSchema extends ActionSchema {
|
|
9
|
+
kind: 'search'
|
|
10
|
+
/**
|
|
11
|
+
* The fields within the entity to be included in the search scope.
|
|
12
|
+
* Must be "indexable" and typically text-based.
|
|
13
|
+
*/
|
|
14
|
+
fields: string[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Enables keyword-based search across specified fields.
|
|
19
|
+
* Endpoint: GET /[entity-collection-name]/search
|
|
20
|
+
*/
|
|
21
|
+
export class SearchAction extends Action implements SearchActionSchema {
|
|
22
|
+
@observed() override accessor kind: 'search'
|
|
23
|
+
@observed({ deep: true }) accessor fields: string[]
|
|
24
|
+
|
|
25
|
+
constructor(state: Partial<SearchActionSchema> = {}) {
|
|
26
|
+
super(state)
|
|
27
|
+
this.kind = 'search'
|
|
28
|
+
this.fields = state.fields ? [...state.fields] : []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
override toJSON(): SearchActionSchema {
|
|
32
|
+
return {
|
|
33
|
+
...(super.toJSON() as SearchActionSchema),
|
|
34
|
+
kind: 'search',
|
|
35
|
+
fields: structuredClone(toRaw(this, this.fields)) as string[],
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static isSearchActionSchema(schema: ActionSchema): schema is SearchActionSchema {
|
|
40
|
+
return schema.kind === 'search'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static isSearchAction(action: Action): action is SearchAction {
|
|
44
|
+
return action.kind === 'search'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Action, type ActionSchema } from './Action.js'
|
|
2
|
+
import { observed, toRaw } from '../../decorators/observed.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Enables modifying an existing resource.
|
|
6
|
+
* Endpoints: PUT or PATCH /[entity-collection-name]/{id}
|
|
7
|
+
*/
|
|
8
|
+
export interface UpdateActionSchema extends ActionSchema {
|
|
9
|
+
kind: 'update'
|
|
10
|
+
/**
|
|
11
|
+
* The allowed HTTP methods for updates. Default: PATCH only.
|
|
12
|
+
*
|
|
13
|
+
* These two methods represent the two common ways to update a resource:
|
|
14
|
+
* - PUT: Replaces the entire resource with the provided data.
|
|
15
|
+
* - PATCH: Applies a partial update to the resource, allowing for specific fields to be modified.
|
|
16
|
+
*/
|
|
17
|
+
allowedMethods: ('PUT' | 'PATCH')[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Enables modifying an existing resource.
|
|
22
|
+
* Endpoints: PUT or PATCH /[entity-collection-name]/{id}
|
|
23
|
+
*/
|
|
24
|
+
export class UpdateAction extends Action implements UpdateActionSchema {
|
|
25
|
+
@observed() override accessor kind: 'update'
|
|
26
|
+
@observed({ deep: true }) accessor allowedMethods: ('PUT' | 'PATCH')[]
|
|
27
|
+
|
|
28
|
+
constructor(state: Partial<UpdateActionSchema> = {}) {
|
|
29
|
+
super(state)
|
|
30
|
+
this.kind = 'update'
|
|
31
|
+
this.allowedMethods = state.allowedMethods ? [...state.allowedMethods] : ['PATCH']
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override toJSON(): UpdateActionSchema {
|
|
35
|
+
return {
|
|
36
|
+
...(super.toJSON() as UpdateActionSchema),
|
|
37
|
+
kind: 'update',
|
|
38
|
+
allowedMethods: structuredClone(toRaw(this, this.allowedMethods)) as ('PUT' | 'PATCH')[],
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static isUpdateAction(action: Action): action is UpdateAction {
|
|
43
|
+
return action.kind === 'update'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static isUpdateActionSchema(schema: ActionSchema): schema is UpdateActionSchema {
|
|
47
|
+
return schema.kind === 'update'
|
|
48
|
+
}
|
|
49
|
+
}
|