space-data-module-sdk 0.2.6 → 0.2.8
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 +73 -0
- package/package.json +5 -1
- package/schemas/PluginManifest.fbs +10 -0
- package/src/bundle/constants.js +4 -1
- package/src/bundle/wasm.js +30 -2
- package/src/compliance/index.js +2 -1
- package/src/compliance/pluginCompliance.js +391 -2
- package/src/deployment/index.d.ts +224 -0
- package/src/deployment/index.js +1552 -0
- package/src/generated/orbpro/manifest/plugin-manifest.d.ts +10 -3
- package/src/generated/orbpro/manifest/plugin-manifest.js +32 -6
- package/src/generated/orbpro/manifest/plugin-manifest.ts +42 -5
- package/src/generated/orbpro/manifest/protocol-spec.d.ts +35 -3
- package/src/generated/orbpro/manifest/protocol-spec.js +120 -6
- package/src/generated/orbpro/manifest/protocol-spec.ts +191 -1
- package/src/index.d.ts +138 -3
- package/src/index.js +4 -0
- package/src/manifest/index.js +7 -0
- package/src/manifest/normalize.js +82 -11
- package/src/manifest/typeRefs.js +143 -0
- package/src/runtime/constants.js +14 -0
- package/src/runtime/index.d.ts +2 -0
- package/src/testing/index.d.ts +86 -0
- package/src/testing/index.js +473 -0
|
@@ -0,0 +1,1552 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decodeModuleBundleEntryPayload,
|
|
3
|
+
findModuleBundleEntry,
|
|
4
|
+
} from "../bundle/codec.js";
|
|
5
|
+
import {
|
|
6
|
+
SDS_DEPLOYMENT_ENTRY_ID,
|
|
7
|
+
SDS_DEPLOYMENT_MEDIA_TYPE,
|
|
8
|
+
SDS_DEPLOYMENT_SECTION_NAME,
|
|
9
|
+
} from "../bundle/constants.js";
|
|
10
|
+
import { ProtocolRole, ProtocolTransportKind } from "../runtime/constants.js";
|
|
11
|
+
|
|
12
|
+
export const DEPLOYMENT_PLAN_FORMAT_VERSION = 1;
|
|
13
|
+
|
|
14
|
+
export const InputBindingSourceKind = Object.freeze({
|
|
15
|
+
PUBSUB: "pubsub",
|
|
16
|
+
PROTOCOL_STREAM: "protocol-stream",
|
|
17
|
+
CATALOG_SYNC: "catalog-sync",
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const DeploymentBindingMode = Object.freeze({
|
|
21
|
+
LOCAL: "local",
|
|
22
|
+
DELEGATED: "delegated",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const ScheduleBindingKind = Object.freeze({
|
|
26
|
+
INTERVAL: "interval",
|
|
27
|
+
CRON: "cron",
|
|
28
|
+
ONCE: "once",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const InputBindingSourceKindSet = new Set(Object.values(InputBindingSourceKind));
|
|
32
|
+
const DeploymentBindingModeSet = new Set(Object.values(DeploymentBindingMode));
|
|
33
|
+
const ProtocolRoleSet = new Set(Object.values(ProtocolRole));
|
|
34
|
+
const ProtocolTransportKindSet = new Set(Object.values(ProtocolTransportKind));
|
|
35
|
+
const ScheduleBindingKindSet = new Set(Object.values(ScheduleBindingKind));
|
|
36
|
+
|
|
37
|
+
function normalizeString(value) {
|
|
38
|
+
if (value === undefined || value === null) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const normalized = String(value).trim();
|
|
42
|
+
return normalized.length > 0 ? normalized : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function normalizeBoolean(value, fallback = false) {
|
|
46
|
+
if (value === undefined || value === null) {
|
|
47
|
+
return fallback;
|
|
48
|
+
}
|
|
49
|
+
return value === true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function normalizeInteger(value, fallback = 0) {
|
|
53
|
+
const normalized = Number(value ?? fallback);
|
|
54
|
+
if (!Number.isFinite(normalized)) {
|
|
55
|
+
return fallback;
|
|
56
|
+
}
|
|
57
|
+
return Math.max(0, Math.trunc(normalized));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeStringArray(value) {
|
|
61
|
+
if (!Array.isArray(value)) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
return value
|
|
65
|
+
.map((entry) => normalizeString(entry))
|
|
66
|
+
.filter((entry) => entry !== null);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function normalizeRecord(value) {
|
|
70
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
return { ...value };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function normalizeProtocolTransportKindName(value) {
|
|
77
|
+
if (value === undefined || value === null) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const normalized = String(value)
|
|
81
|
+
.trim()
|
|
82
|
+
.toLowerCase()
|
|
83
|
+
.replace(/_/g, "-");
|
|
84
|
+
if (normalized === "websocket") {
|
|
85
|
+
return ProtocolTransportKind.WS;
|
|
86
|
+
}
|
|
87
|
+
if (normalized === "pipe") {
|
|
88
|
+
return ProtocolTransportKind.WASI_PIPE;
|
|
89
|
+
}
|
|
90
|
+
return normalized.length > 0 ? normalized : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function normalizeProtocolRoleName(value) {
|
|
94
|
+
if (value === undefined || value === null) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const normalized = String(value)
|
|
98
|
+
.trim()
|
|
99
|
+
.toLowerCase()
|
|
100
|
+
.replace(/_/g, "-");
|
|
101
|
+
if (normalized === "handler") {
|
|
102
|
+
return ProtocolRole.HANDLE;
|
|
103
|
+
}
|
|
104
|
+
return normalized.length > 0 ? normalized : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function normalizeInputBindingSourceKindName(value) {
|
|
108
|
+
if (value === undefined || value === null) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const normalized = String(value)
|
|
112
|
+
.trim()
|
|
113
|
+
.toLowerCase()
|
|
114
|
+
.replace(/_/g, "-");
|
|
115
|
+
return normalized.length > 0 ? normalized : null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function normalizeDeploymentBindingModeName(value) {
|
|
119
|
+
if (value === undefined || value === null) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const normalized = String(value)
|
|
123
|
+
.trim()
|
|
124
|
+
.toLowerCase()
|
|
125
|
+
.replace(/_/g, "-");
|
|
126
|
+
if (normalized === "remote") {
|
|
127
|
+
return DeploymentBindingMode.DELEGATED;
|
|
128
|
+
}
|
|
129
|
+
return normalized.length > 0 ? normalized : null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function normalizeScheduleBindingKindName(value) {
|
|
133
|
+
if (value === undefined || value === null) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
const normalized = String(value)
|
|
137
|
+
.trim()
|
|
138
|
+
.toLowerCase()
|
|
139
|
+
.replace(/_/g, "-");
|
|
140
|
+
if (normalized === "startup") {
|
|
141
|
+
return ScheduleBindingKind.ONCE;
|
|
142
|
+
}
|
|
143
|
+
return normalized.length > 0 ? normalized : null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function normalizeProtocolInstallation(value = {}) {
|
|
147
|
+
return {
|
|
148
|
+
protocolId: normalizeString(value.protocolId),
|
|
149
|
+
wireId: normalizeString(value.wireId),
|
|
150
|
+
transportKind: normalizeProtocolTransportKindName(value.transportKind),
|
|
151
|
+
role: normalizeProtocolRoleName(value.role),
|
|
152
|
+
peerId: normalizeString(value.peerId),
|
|
153
|
+
listenMultiaddrs: normalizeStringArray(value.listenMultiaddrs),
|
|
154
|
+
advertisedMultiaddrs: normalizeStringArray(value.advertisedMultiaddrs),
|
|
155
|
+
nodeInfoUrl: normalizeString(value.nodeInfoUrl),
|
|
156
|
+
serviceName: normalizeString(value.serviceName),
|
|
157
|
+
resolvedPort: normalizeInteger(value.resolvedPort),
|
|
158
|
+
artifactCid: normalizeString(value.artifactCid),
|
|
159
|
+
description: normalizeString(value.description),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function normalizeInputBinding(value = {}) {
|
|
164
|
+
return {
|
|
165
|
+
bindingId: normalizeString(value.bindingId),
|
|
166
|
+
targetPluginId: normalizeString(value.targetPluginId),
|
|
167
|
+
targetMethodId: normalizeString(value.targetMethodId),
|
|
168
|
+
targetInputPortId: normalizeString(value.targetInputPortId),
|
|
169
|
+
sourceKind: normalizeInputBindingSourceKindName(value.sourceKind),
|
|
170
|
+
topic: normalizeString(value.topic),
|
|
171
|
+
wireId: normalizeString(value.wireId),
|
|
172
|
+
nodeInfoUrl: normalizeString(value.nodeInfoUrl),
|
|
173
|
+
multiaddrs: normalizeStringArray(value.multiaddrs),
|
|
174
|
+
allowPeerIds: normalizeStringArray(value.allowPeerIds),
|
|
175
|
+
allowServerKeys: normalizeStringArray(value.allowServerKeys),
|
|
176
|
+
deliveryMode: normalizeString(value.deliveryMode),
|
|
177
|
+
description: normalizeString(value.description),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function normalizeScheduleBinding(value = {}) {
|
|
182
|
+
return {
|
|
183
|
+
scheduleId: normalizeString(value.scheduleId),
|
|
184
|
+
bindingMode: normalizeDeploymentBindingModeName(value.bindingMode),
|
|
185
|
+
triggerId: normalizeString(value.triggerId),
|
|
186
|
+
targetMethodId: normalizeString(value.targetMethodId),
|
|
187
|
+
targetInputPortId: normalizeString(value.targetInputPortId),
|
|
188
|
+
scheduleKind: normalizeScheduleBindingKindName(value.scheduleKind),
|
|
189
|
+
cron: normalizeString(value.cron),
|
|
190
|
+
intervalMs: normalizeInteger(value.intervalMs),
|
|
191
|
+
runAtStartup: normalizeBoolean(value.runAtStartup),
|
|
192
|
+
startupDelayMs: normalizeInteger(value.startupDelayMs),
|
|
193
|
+
timezone: normalizeString(value.timezone),
|
|
194
|
+
description: normalizeString(value.description),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function normalizeServiceBinding(value = {}) {
|
|
199
|
+
return {
|
|
200
|
+
serviceId: normalizeString(value.serviceId),
|
|
201
|
+
bindingMode: normalizeDeploymentBindingModeName(value.bindingMode),
|
|
202
|
+
serviceKind: normalizeString(value.serviceKind),
|
|
203
|
+
triggerId: normalizeString(value.triggerId),
|
|
204
|
+
protocolId: normalizeString(value.protocolId),
|
|
205
|
+
routePath: normalizeString(value.routePath),
|
|
206
|
+
method: normalizeString(value.method),
|
|
207
|
+
transportKind: normalizeProtocolTransportKindName(value.transportKind),
|
|
208
|
+
adapter: normalizeString(value.adapter),
|
|
209
|
+
listenHost: normalizeString(value.listenHost),
|
|
210
|
+
listenPort: normalizeInteger(value.listenPort),
|
|
211
|
+
remoteUrl: normalizeString(value.remoteUrl),
|
|
212
|
+
allowTransports: normalizeStringArray(value.allowTransports),
|
|
213
|
+
authPolicyId: normalizeString(value.authPolicyId),
|
|
214
|
+
description: normalizeString(value.description),
|
|
215
|
+
properties: normalizeRecord(value.properties),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function normalizeAuthPolicy(value = {}) {
|
|
220
|
+
return {
|
|
221
|
+
policyId: normalizeString(value.policyId),
|
|
222
|
+
bindingMode: normalizeDeploymentBindingModeName(value.bindingMode),
|
|
223
|
+
targetKind: normalizeString(value.targetKind),
|
|
224
|
+
targetId: normalizeString(value.targetId),
|
|
225
|
+
adapter: normalizeString(value.adapter),
|
|
226
|
+
walletProfileId: normalizeString(value.walletProfileId),
|
|
227
|
+
trustMapId: normalizeString(value.trustMapId),
|
|
228
|
+
allowPeerIds: normalizeStringArray(value.allowPeerIds),
|
|
229
|
+
allowServerKeys: normalizeStringArray(value.allowServerKeys),
|
|
230
|
+
allowEntityIds: normalizeStringArray(value.allowEntityIds),
|
|
231
|
+
requireSignedRequests: normalizeBoolean(value.requireSignedRequests),
|
|
232
|
+
requireEncryptedTransport: normalizeBoolean(value.requireEncryptedTransport),
|
|
233
|
+
description: normalizeString(value.description),
|
|
234
|
+
properties: normalizeRecord(value.properties),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function normalizePublicationBinding(value = {}) {
|
|
239
|
+
return {
|
|
240
|
+
publicationId: normalizeString(value.publicationId),
|
|
241
|
+
bindingMode: normalizeDeploymentBindingModeName(value.bindingMode),
|
|
242
|
+
sourceKind: normalizeString(value.sourceKind),
|
|
243
|
+
sourceMethodId: normalizeString(value.sourceMethodId),
|
|
244
|
+
sourceOutputPortId: normalizeString(value.sourceOutputPortId),
|
|
245
|
+
sourceNodeId: normalizeString(value.sourceNodeId),
|
|
246
|
+
sourceTriggerId: normalizeString(value.sourceTriggerId),
|
|
247
|
+
topic: normalizeString(value.topic),
|
|
248
|
+
wireId: normalizeString(value.wireId),
|
|
249
|
+
schemaName: normalizeString(value.schemaName),
|
|
250
|
+
mediaType: normalizeString(value.mediaType),
|
|
251
|
+
archivePath: normalizeString(value.archivePath),
|
|
252
|
+
queryServiceId: normalizeString(value.queryServiceId),
|
|
253
|
+
emitPnm: normalizeBoolean(value.emitPnm),
|
|
254
|
+
emitFlatbufferArchive: normalizeBoolean(value.emitFlatbufferArchive),
|
|
255
|
+
pinPolicy: normalizeString(value.pinPolicy),
|
|
256
|
+
maxRecords: normalizeInteger(value.maxRecords),
|
|
257
|
+
maxBytes: normalizeInteger(value.maxBytes),
|
|
258
|
+
minLivelinessSeconds: normalizeInteger(value.minLivelinessSeconds),
|
|
259
|
+
recordRangeStartField: normalizeString(value.recordRangeStartField),
|
|
260
|
+
recordRangeStopField: normalizeString(value.recordRangeStopField),
|
|
261
|
+
description: normalizeString(value.description),
|
|
262
|
+
properties: normalizeRecord(value.properties),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function normalizeDeploymentPlan(value = {}) {
|
|
267
|
+
return {
|
|
268
|
+
formatVersion: normalizeInteger(
|
|
269
|
+
value.formatVersion,
|
|
270
|
+
DEPLOYMENT_PLAN_FORMAT_VERSION,
|
|
271
|
+
),
|
|
272
|
+
pluginId: normalizeString(value.pluginId),
|
|
273
|
+
version: normalizeString(value.version),
|
|
274
|
+
artifactCid: normalizeString(value.artifactCid),
|
|
275
|
+
bundleCid: normalizeString(value.bundleCid),
|
|
276
|
+
environmentId: normalizeString(value.environmentId),
|
|
277
|
+
protocolInstallations: Array.isArray(value.protocolInstallations)
|
|
278
|
+
? value.protocolInstallations.map((entry) =>
|
|
279
|
+
normalizeProtocolInstallation(entry),
|
|
280
|
+
)
|
|
281
|
+
: [],
|
|
282
|
+
inputBindings: Array.isArray(value.inputBindings)
|
|
283
|
+
? value.inputBindings.map((entry) => normalizeInputBinding(entry))
|
|
284
|
+
: [],
|
|
285
|
+
scheduleBindings: Array.isArray(value.scheduleBindings)
|
|
286
|
+
? value.scheduleBindings.map((entry) => normalizeScheduleBinding(entry))
|
|
287
|
+
: [],
|
|
288
|
+
serviceBindings: Array.isArray(value.serviceBindings)
|
|
289
|
+
? value.serviceBindings.map((entry) => normalizeServiceBinding(entry))
|
|
290
|
+
: [],
|
|
291
|
+
authPolicies: Array.isArray(value.authPolicies)
|
|
292
|
+
? value.authPolicies.map((entry) => normalizeAuthPolicy(entry))
|
|
293
|
+
: [],
|
|
294
|
+
publicationBindings: Array.isArray(value.publicationBindings)
|
|
295
|
+
? value.publicationBindings.map((entry) =>
|
|
296
|
+
normalizePublicationBinding(entry),
|
|
297
|
+
)
|
|
298
|
+
: [],
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function createIssue(severity, code, message, location) {
|
|
303
|
+
return { severity, code, message, location };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function pushIssue(issues, severity, code, message, location) {
|
|
307
|
+
issues.push(createIssue(severity, code, message, location));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function validateStringField(issues, value, location, label) {
|
|
311
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
312
|
+
pushIssue(
|
|
313
|
+
issues,
|
|
314
|
+
"error",
|
|
315
|
+
"missing-string",
|
|
316
|
+
`${label} must be a non-empty string.`,
|
|
317
|
+
location,
|
|
318
|
+
);
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function validateOptionalStringField(issues, value, location, label) {
|
|
325
|
+
if (value === undefined || value === null) {
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
return validateStringField(issues, value, location, label);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function validateStringArrayField(issues, value, location, label) {
|
|
332
|
+
if (!Array.isArray(value)) {
|
|
333
|
+
pushIssue(
|
|
334
|
+
issues,
|
|
335
|
+
"error",
|
|
336
|
+
"invalid-string-array",
|
|
337
|
+
`${label} must be an array of non-empty strings.`,
|
|
338
|
+
location,
|
|
339
|
+
);
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
let valid = true;
|
|
343
|
+
value.forEach((entry, index) => {
|
|
344
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
345
|
+
valid = false;
|
|
346
|
+
pushIssue(
|
|
347
|
+
issues,
|
|
348
|
+
"error",
|
|
349
|
+
"invalid-string-array-entry",
|
|
350
|
+
`${label} entries must be non-empty strings.`,
|
|
351
|
+
`${location}[${index}]`,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
return valid;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function validatePortField(issues, value, location, label) {
|
|
359
|
+
if (value === undefined || value === null) {
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
if (!Number.isInteger(value) || value < 0 || value > 65535) {
|
|
363
|
+
pushIssue(
|
|
364
|
+
issues,
|
|
365
|
+
"error",
|
|
366
|
+
"invalid-port",
|
|
367
|
+
`${label} must be an integer between 0 and 65535 when present.`,
|
|
368
|
+
location,
|
|
369
|
+
);
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function roleIncludesRole(declaredRole, runtimeRole) {
|
|
376
|
+
if (declaredRole === runtimeRole) {
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
return declaredRole === ProtocolRole.BOTH;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function buildMethodLookup(manifest) {
|
|
383
|
+
const lookup = new Map();
|
|
384
|
+
if (!Array.isArray(manifest?.methods)) {
|
|
385
|
+
return lookup;
|
|
386
|
+
}
|
|
387
|
+
for (const method of manifest.methods) {
|
|
388
|
+
if (typeof method?.methodId === "string" && method.methodId.trim().length > 0) {
|
|
389
|
+
lookup.set(method.methodId, method);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return lookup;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function validateProtocolInstallation(
|
|
396
|
+
installation,
|
|
397
|
+
issues,
|
|
398
|
+
location,
|
|
399
|
+
manifestProtocolLookup,
|
|
400
|
+
) {
|
|
401
|
+
if (!installation || typeof installation !== "object" || Array.isArray(installation)) {
|
|
402
|
+
pushIssue(
|
|
403
|
+
issues,
|
|
404
|
+
"error",
|
|
405
|
+
"invalid-protocol-installation",
|
|
406
|
+
"Protocol installation entries must be objects.",
|
|
407
|
+
location,
|
|
408
|
+
);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
const protocolIdValid = validateStringField(
|
|
412
|
+
issues,
|
|
413
|
+
installation.protocolId,
|
|
414
|
+
`${location}.protocolId`,
|
|
415
|
+
"Protocol installation protocolId",
|
|
416
|
+
);
|
|
417
|
+
validateStringField(
|
|
418
|
+
issues,
|
|
419
|
+
installation.wireId,
|
|
420
|
+
`${location}.wireId`,
|
|
421
|
+
"Protocol installation wireId",
|
|
422
|
+
);
|
|
423
|
+
const transportKind = normalizeProtocolTransportKindName(
|
|
424
|
+
installation.transportKind,
|
|
425
|
+
);
|
|
426
|
+
if (
|
|
427
|
+
validateStringField(
|
|
428
|
+
issues,
|
|
429
|
+
installation.transportKind,
|
|
430
|
+
`${location}.transportKind`,
|
|
431
|
+
"Protocol installation transportKind",
|
|
432
|
+
) &&
|
|
433
|
+
!ProtocolTransportKindSet.has(transportKind)
|
|
434
|
+
) {
|
|
435
|
+
pushIssue(
|
|
436
|
+
issues,
|
|
437
|
+
"error",
|
|
438
|
+
"unknown-protocol-transport-kind",
|
|
439
|
+
`Protocol installation transportKind must be one of: ${Array.from(
|
|
440
|
+
ProtocolTransportKindSet,
|
|
441
|
+
).join(", ")}.`,
|
|
442
|
+
`${location}.transportKind`,
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
const role = normalizeProtocolRoleName(installation.role);
|
|
446
|
+
if (
|
|
447
|
+
validateStringField(
|
|
448
|
+
issues,
|
|
449
|
+
installation.role,
|
|
450
|
+
`${location}.role`,
|
|
451
|
+
"Protocol installation role",
|
|
452
|
+
) &&
|
|
453
|
+
!ProtocolRoleSet.has(role)
|
|
454
|
+
) {
|
|
455
|
+
pushIssue(
|
|
456
|
+
issues,
|
|
457
|
+
"error",
|
|
458
|
+
"unknown-protocol-role",
|
|
459
|
+
`Protocol installation role must be one of: ${Array.from(
|
|
460
|
+
ProtocolRoleSet,
|
|
461
|
+
).join(", ")}.`,
|
|
462
|
+
`${location}.role`,
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
validateOptionalStringField(
|
|
466
|
+
issues,
|
|
467
|
+
installation.peerId,
|
|
468
|
+
`${location}.peerId`,
|
|
469
|
+
"Protocol installation peerId",
|
|
470
|
+
);
|
|
471
|
+
validateStringArrayField(
|
|
472
|
+
issues,
|
|
473
|
+
installation.listenMultiaddrs,
|
|
474
|
+
`${location}.listenMultiaddrs`,
|
|
475
|
+
"Protocol installation listenMultiaddrs",
|
|
476
|
+
);
|
|
477
|
+
validateStringArrayField(
|
|
478
|
+
issues,
|
|
479
|
+
installation.advertisedMultiaddrs,
|
|
480
|
+
`${location}.advertisedMultiaddrs`,
|
|
481
|
+
"Protocol installation advertisedMultiaddrs",
|
|
482
|
+
);
|
|
483
|
+
validateOptionalStringField(
|
|
484
|
+
issues,
|
|
485
|
+
installation.nodeInfoUrl,
|
|
486
|
+
`${location}.nodeInfoUrl`,
|
|
487
|
+
"Protocol installation nodeInfoUrl",
|
|
488
|
+
);
|
|
489
|
+
validateOptionalStringField(
|
|
490
|
+
issues,
|
|
491
|
+
installation.serviceName,
|
|
492
|
+
`${location}.serviceName`,
|
|
493
|
+
"Protocol installation serviceName",
|
|
494
|
+
);
|
|
495
|
+
validateOptionalStringField(
|
|
496
|
+
issues,
|
|
497
|
+
installation.artifactCid,
|
|
498
|
+
`${location}.artifactCid`,
|
|
499
|
+
"Protocol installation artifactCid",
|
|
500
|
+
);
|
|
501
|
+
validateOptionalStringField(
|
|
502
|
+
issues,
|
|
503
|
+
installation.description,
|
|
504
|
+
`${location}.description`,
|
|
505
|
+
"Protocol installation description",
|
|
506
|
+
);
|
|
507
|
+
validatePortField(
|
|
508
|
+
issues,
|
|
509
|
+
installation.resolvedPort,
|
|
510
|
+
`${location}.resolvedPort`,
|
|
511
|
+
"Protocol installation resolvedPort",
|
|
512
|
+
);
|
|
513
|
+
if (
|
|
514
|
+
role === ProtocolRole.DIAL &&
|
|
515
|
+
Array.isArray(installation.advertisedMultiaddrs) &&
|
|
516
|
+
installation.advertisedMultiaddrs.length > 0
|
|
517
|
+
) {
|
|
518
|
+
pushIssue(
|
|
519
|
+
issues,
|
|
520
|
+
"warning",
|
|
521
|
+
"dial-installation-advertises",
|
|
522
|
+
"Dial-only protocol installations should not advertise inbound multiaddrs.",
|
|
523
|
+
`${location}.advertisedMultiaddrs`,
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
if (protocolIdValid) {
|
|
527
|
+
const manifestProtocol = manifestProtocolLookup.get(installation.protocolId);
|
|
528
|
+
if (!manifestProtocol) {
|
|
529
|
+
pushIssue(
|
|
530
|
+
issues,
|
|
531
|
+
"warning",
|
|
532
|
+
"unknown-installation-protocol-id",
|
|
533
|
+
`Protocol installation "${installation.protocolId}" does not match a protocol declared in the manifest.`,
|
|
534
|
+
`${location}.protocolId`,
|
|
535
|
+
);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (
|
|
539
|
+
manifestProtocol.wireId &&
|
|
540
|
+
installation.wireId &&
|
|
541
|
+
manifestProtocol.wireId !== installation.wireId
|
|
542
|
+
) {
|
|
543
|
+
pushIssue(
|
|
544
|
+
issues,
|
|
545
|
+
"error",
|
|
546
|
+
"installation-wire-id-mismatch",
|
|
547
|
+
`Protocol installation "${installation.protocolId}" wireId does not match the manifest.`,
|
|
548
|
+
`${location}.wireId`,
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
if (
|
|
552
|
+
manifestProtocol.transportKind &&
|
|
553
|
+
transportKind &&
|
|
554
|
+
manifestProtocol.transportKind !== transportKind
|
|
555
|
+
) {
|
|
556
|
+
pushIssue(
|
|
557
|
+
issues,
|
|
558
|
+
"error",
|
|
559
|
+
"installation-transport-mismatch",
|
|
560
|
+
`Protocol installation "${installation.protocolId}" transportKind does not match the manifest.`,
|
|
561
|
+
`${location}.transportKind`,
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
if (
|
|
565
|
+
manifestProtocol.role &&
|
|
566
|
+
role &&
|
|
567
|
+
!roleIncludesRole(manifestProtocol.role, role)
|
|
568
|
+
) {
|
|
569
|
+
pushIssue(
|
|
570
|
+
issues,
|
|
571
|
+
"error",
|
|
572
|
+
"installation-role-mismatch",
|
|
573
|
+
`Protocol installation "${installation.protocolId}" role "${role}" is not allowed by the manifest role "${manifestProtocol.role}".`,
|
|
574
|
+
`${location}.role`,
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function validateInputBinding(
|
|
581
|
+
binding,
|
|
582
|
+
issues,
|
|
583
|
+
location,
|
|
584
|
+
manifest,
|
|
585
|
+
manifestMethodLookup,
|
|
586
|
+
) {
|
|
587
|
+
if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
|
|
588
|
+
pushIssue(
|
|
589
|
+
issues,
|
|
590
|
+
"error",
|
|
591
|
+
"invalid-input-binding",
|
|
592
|
+
"Input binding entries must be objects.",
|
|
593
|
+
location,
|
|
594
|
+
);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
validateStringField(
|
|
598
|
+
issues,
|
|
599
|
+
binding.bindingId,
|
|
600
|
+
`${location}.bindingId`,
|
|
601
|
+
"Input binding bindingId",
|
|
602
|
+
);
|
|
603
|
+
const targetPluginId = normalizeString(binding.targetPluginId);
|
|
604
|
+
const targetMethodIdValid = validateStringField(
|
|
605
|
+
issues,
|
|
606
|
+
binding.targetMethodId,
|
|
607
|
+
`${location}.targetMethodId`,
|
|
608
|
+
"Input binding targetMethodId",
|
|
609
|
+
);
|
|
610
|
+
const targetInputPortIdValid = validateStringField(
|
|
611
|
+
issues,
|
|
612
|
+
binding.targetInputPortId,
|
|
613
|
+
`${location}.targetInputPortId`,
|
|
614
|
+
"Input binding targetInputPortId",
|
|
615
|
+
);
|
|
616
|
+
const sourceKind = normalizeInputBindingSourceKindName(binding.sourceKind);
|
|
617
|
+
if (
|
|
618
|
+
validateStringField(
|
|
619
|
+
issues,
|
|
620
|
+
binding.sourceKind,
|
|
621
|
+
`${location}.sourceKind`,
|
|
622
|
+
"Input binding sourceKind",
|
|
623
|
+
) &&
|
|
624
|
+
!InputBindingSourceKindSet.has(sourceKind)
|
|
625
|
+
) {
|
|
626
|
+
pushIssue(
|
|
627
|
+
issues,
|
|
628
|
+
"error",
|
|
629
|
+
"unknown-input-binding-source-kind",
|
|
630
|
+
`Input binding sourceKind must be one of: ${Array.from(
|
|
631
|
+
InputBindingSourceKindSet,
|
|
632
|
+
).join(", ")}.`,
|
|
633
|
+
`${location}.sourceKind`,
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
validateOptionalStringField(
|
|
637
|
+
issues,
|
|
638
|
+
binding.targetPluginId,
|
|
639
|
+
`${location}.targetPluginId`,
|
|
640
|
+
"Input binding targetPluginId",
|
|
641
|
+
);
|
|
642
|
+
validateOptionalStringField(
|
|
643
|
+
issues,
|
|
644
|
+
binding.topic,
|
|
645
|
+
`${location}.topic`,
|
|
646
|
+
"Input binding topic",
|
|
647
|
+
);
|
|
648
|
+
validateOptionalStringField(
|
|
649
|
+
issues,
|
|
650
|
+
binding.wireId,
|
|
651
|
+
`${location}.wireId`,
|
|
652
|
+
"Input binding wireId",
|
|
653
|
+
);
|
|
654
|
+
validateOptionalStringField(
|
|
655
|
+
issues,
|
|
656
|
+
binding.nodeInfoUrl,
|
|
657
|
+
`${location}.nodeInfoUrl`,
|
|
658
|
+
"Input binding nodeInfoUrl",
|
|
659
|
+
);
|
|
660
|
+
validateStringArrayField(
|
|
661
|
+
issues,
|
|
662
|
+
binding.multiaddrs,
|
|
663
|
+
`${location}.multiaddrs`,
|
|
664
|
+
"Input binding multiaddrs",
|
|
665
|
+
);
|
|
666
|
+
validateStringArrayField(
|
|
667
|
+
issues,
|
|
668
|
+
binding.allowPeerIds,
|
|
669
|
+
`${location}.allowPeerIds`,
|
|
670
|
+
"Input binding allowPeerIds",
|
|
671
|
+
);
|
|
672
|
+
validateStringArrayField(
|
|
673
|
+
issues,
|
|
674
|
+
binding.allowServerKeys,
|
|
675
|
+
`${location}.allowServerKeys`,
|
|
676
|
+
"Input binding allowServerKeys",
|
|
677
|
+
);
|
|
678
|
+
validateOptionalStringField(
|
|
679
|
+
issues,
|
|
680
|
+
binding.deliveryMode,
|
|
681
|
+
`${location}.deliveryMode`,
|
|
682
|
+
"Input binding deliveryMode",
|
|
683
|
+
);
|
|
684
|
+
validateOptionalStringField(
|
|
685
|
+
issues,
|
|
686
|
+
binding.description,
|
|
687
|
+
`${location}.description`,
|
|
688
|
+
"Input binding description",
|
|
689
|
+
);
|
|
690
|
+
if (
|
|
691
|
+
sourceKind === InputBindingSourceKind.PUBSUB &&
|
|
692
|
+
!validateStringField(
|
|
693
|
+
issues,
|
|
694
|
+
binding.topic,
|
|
695
|
+
`${location}.topic`,
|
|
696
|
+
"Pubsub input binding topic",
|
|
697
|
+
)
|
|
698
|
+
) {
|
|
699
|
+
// error already recorded
|
|
700
|
+
}
|
|
701
|
+
if (
|
|
702
|
+
sourceKind === InputBindingSourceKind.PROTOCOL_STREAM &&
|
|
703
|
+
!validateStringField(
|
|
704
|
+
issues,
|
|
705
|
+
binding.wireId,
|
|
706
|
+
`${location}.wireId`,
|
|
707
|
+
"Protocol-stream input binding wireId",
|
|
708
|
+
)
|
|
709
|
+
) {
|
|
710
|
+
// error already recorded
|
|
711
|
+
}
|
|
712
|
+
const targetsCurrentManifest =
|
|
713
|
+
!targetPluginId ||
|
|
714
|
+
(typeof manifest?.pluginId === "string" && targetPluginId === manifest.pluginId);
|
|
715
|
+
if (!targetsCurrentManifest) {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
const method = targetMethodIdValid
|
|
719
|
+
? manifestMethodLookup.get(binding.targetMethodId)
|
|
720
|
+
: null;
|
|
721
|
+
if (targetMethodIdValid && !method) {
|
|
722
|
+
pushIssue(
|
|
723
|
+
issues,
|
|
724
|
+
"error",
|
|
725
|
+
"unknown-input-binding-method",
|
|
726
|
+
`Input binding "${binding.bindingId ?? "binding"}" targets unknown method "${binding.targetMethodId}".`,
|
|
727
|
+
`${location}.targetMethodId`,
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
if (
|
|
731
|
+
targetInputPortIdValid &&
|
|
732
|
+
method &&
|
|
733
|
+
!Array.isArray(method.inputPorts)
|
|
734
|
+
) {
|
|
735
|
+
pushIssue(
|
|
736
|
+
issues,
|
|
737
|
+
"error",
|
|
738
|
+
"unknown-input-binding-port",
|
|
739
|
+
`Input binding "${binding.bindingId ?? "binding"}" targets method "${binding.targetMethodId}" without declared input ports.`,
|
|
740
|
+
`${location}.targetInputPortId`,
|
|
741
|
+
);
|
|
742
|
+
} else if (
|
|
743
|
+
targetInputPortIdValid &&
|
|
744
|
+
method &&
|
|
745
|
+
!method.inputPorts.some((port) => port?.portId === binding.targetInputPortId)
|
|
746
|
+
) {
|
|
747
|
+
pushIssue(
|
|
748
|
+
issues,
|
|
749
|
+
"error",
|
|
750
|
+
"unknown-input-binding-port",
|
|
751
|
+
`Input binding "${binding.bindingId ?? "binding"}" targets unknown input port "${binding.targetInputPortId}" on method "${binding.targetMethodId}".`,
|
|
752
|
+
`${location}.targetInputPortId`,
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function validateBindingModeField(issues, value, location, label) {
|
|
758
|
+
const normalized = normalizeDeploymentBindingModeName(value);
|
|
759
|
+
if (
|
|
760
|
+
validateStringField(issues, value, location, label) &&
|
|
761
|
+
!DeploymentBindingModeSet.has(normalized)
|
|
762
|
+
) {
|
|
763
|
+
pushIssue(
|
|
764
|
+
issues,
|
|
765
|
+
"error",
|
|
766
|
+
"unknown-binding-mode",
|
|
767
|
+
`${label} must be one of: ${Array.from(DeploymentBindingModeSet).join(
|
|
768
|
+
", ",
|
|
769
|
+
)}.`,
|
|
770
|
+
location,
|
|
771
|
+
);
|
|
772
|
+
return false;
|
|
773
|
+
}
|
|
774
|
+
return true;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function validateScheduleBinding(
|
|
778
|
+
binding,
|
|
779
|
+
issues,
|
|
780
|
+
location,
|
|
781
|
+
manifest,
|
|
782
|
+
manifestMethodLookup,
|
|
783
|
+
) {
|
|
784
|
+
if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
|
|
785
|
+
pushIssue(
|
|
786
|
+
issues,
|
|
787
|
+
"error",
|
|
788
|
+
"invalid-schedule-binding",
|
|
789
|
+
"Schedule binding entries must be objects.",
|
|
790
|
+
location,
|
|
791
|
+
);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
validateStringField(
|
|
795
|
+
issues,
|
|
796
|
+
binding.scheduleId,
|
|
797
|
+
`${location}.scheduleId`,
|
|
798
|
+
"Schedule binding scheduleId",
|
|
799
|
+
);
|
|
800
|
+
validateBindingModeField(
|
|
801
|
+
issues,
|
|
802
|
+
binding.bindingMode,
|
|
803
|
+
`${location}.bindingMode`,
|
|
804
|
+
"Schedule binding bindingMode",
|
|
805
|
+
);
|
|
806
|
+
const scheduleKind = normalizeScheduleBindingKindName(binding.scheduleKind);
|
|
807
|
+
if (
|
|
808
|
+
validateStringField(
|
|
809
|
+
issues,
|
|
810
|
+
binding.scheduleKind,
|
|
811
|
+
`${location}.scheduleKind`,
|
|
812
|
+
"Schedule binding scheduleKind",
|
|
813
|
+
) &&
|
|
814
|
+
!ScheduleBindingKindSet.has(scheduleKind)
|
|
815
|
+
) {
|
|
816
|
+
pushIssue(
|
|
817
|
+
issues,
|
|
818
|
+
"error",
|
|
819
|
+
"unknown-schedule-kind",
|
|
820
|
+
`Schedule binding scheduleKind must be one of: ${Array.from(
|
|
821
|
+
ScheduleBindingKindSet,
|
|
822
|
+
).join(", ")}.`,
|
|
823
|
+
`${location}.scheduleKind`,
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
validateOptionalStringField(
|
|
827
|
+
issues,
|
|
828
|
+
binding.triggerId,
|
|
829
|
+
`${location}.triggerId`,
|
|
830
|
+
"Schedule binding triggerId",
|
|
831
|
+
);
|
|
832
|
+
const hasTriggerId = typeof binding.triggerId === "string" && binding.triggerId.length > 0;
|
|
833
|
+
const targetMethodIdValid =
|
|
834
|
+
validateOptionalStringField(
|
|
835
|
+
issues,
|
|
836
|
+
binding.targetMethodId,
|
|
837
|
+
`${location}.targetMethodId`,
|
|
838
|
+
"Schedule binding targetMethodId",
|
|
839
|
+
) &&
|
|
840
|
+
typeof binding.targetMethodId === "string" &&
|
|
841
|
+
binding.targetMethodId.length > 0;
|
|
842
|
+
const targetInputPortIdPresent =
|
|
843
|
+
typeof binding.targetInputPortId === "string" &&
|
|
844
|
+
binding.targetInputPortId.length > 0;
|
|
845
|
+
validateOptionalStringField(
|
|
846
|
+
issues,
|
|
847
|
+
binding.targetInputPortId,
|
|
848
|
+
`${location}.targetInputPortId`,
|
|
849
|
+
"Schedule binding targetInputPortId",
|
|
850
|
+
);
|
|
851
|
+
validateOptionalStringField(
|
|
852
|
+
issues,
|
|
853
|
+
binding.cron,
|
|
854
|
+
`${location}.cron`,
|
|
855
|
+
"Schedule binding cron",
|
|
856
|
+
);
|
|
857
|
+
validateOptionalStringField(
|
|
858
|
+
issues,
|
|
859
|
+
binding.timezone,
|
|
860
|
+
`${location}.timezone`,
|
|
861
|
+
"Schedule binding timezone",
|
|
862
|
+
);
|
|
863
|
+
validateOptionalStringField(
|
|
864
|
+
issues,
|
|
865
|
+
binding.description,
|
|
866
|
+
`${location}.description`,
|
|
867
|
+
"Schedule binding description",
|
|
868
|
+
);
|
|
869
|
+
if (!hasTriggerId && !targetMethodIdValid) {
|
|
870
|
+
pushIssue(
|
|
871
|
+
issues,
|
|
872
|
+
"error",
|
|
873
|
+
"missing-schedule-target",
|
|
874
|
+
"Schedule bindings must target either a triggerId or a targetMethodId.",
|
|
875
|
+
location,
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
if (
|
|
879
|
+
scheduleKind === ScheduleBindingKind.CRON &&
|
|
880
|
+
!validateStringField(
|
|
881
|
+
issues,
|
|
882
|
+
binding.cron,
|
|
883
|
+
`${location}.cron`,
|
|
884
|
+
"Cron schedule binding cron",
|
|
885
|
+
)
|
|
886
|
+
) {
|
|
887
|
+
// error already recorded
|
|
888
|
+
}
|
|
889
|
+
if (
|
|
890
|
+
scheduleKind === ScheduleBindingKind.INTERVAL &&
|
|
891
|
+
(!Number.isInteger(binding.intervalMs) || binding.intervalMs <= 0)
|
|
892
|
+
) {
|
|
893
|
+
pushIssue(
|
|
894
|
+
issues,
|
|
895
|
+
"error",
|
|
896
|
+
"invalid-interval-ms",
|
|
897
|
+
"Interval schedule bindings must define intervalMs greater than 0.",
|
|
898
|
+
`${location}.intervalMs`,
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
if (
|
|
902
|
+
scheduleKind === ScheduleBindingKind.ONCE &&
|
|
903
|
+
binding.runAtStartup !== true &&
|
|
904
|
+
(!Number.isInteger(binding.startupDelayMs) || binding.startupDelayMs <= 0)
|
|
905
|
+
) {
|
|
906
|
+
pushIssue(
|
|
907
|
+
issues,
|
|
908
|
+
"warning",
|
|
909
|
+
"once-schedule-without-startup",
|
|
910
|
+
"Once schedule bindings should either runAtStartup or define startupDelayMs.",
|
|
911
|
+
location,
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
if (targetMethodIdValid && manifest) {
|
|
915
|
+
const method = manifestMethodLookup.get(binding.targetMethodId);
|
|
916
|
+
if (!method) {
|
|
917
|
+
pushIssue(
|
|
918
|
+
issues,
|
|
919
|
+
"error",
|
|
920
|
+
"unknown-schedule-binding-method",
|
|
921
|
+
`Schedule binding "${binding.scheduleId ?? "schedule"}" targets unknown method "${binding.targetMethodId}".`,
|
|
922
|
+
`${location}.targetMethodId`,
|
|
923
|
+
);
|
|
924
|
+
} else if (
|
|
925
|
+
targetInputPortIdPresent &&
|
|
926
|
+
!Array.isArray(method.inputPorts)
|
|
927
|
+
) {
|
|
928
|
+
pushIssue(
|
|
929
|
+
issues,
|
|
930
|
+
"error",
|
|
931
|
+
"unknown-schedule-binding-port",
|
|
932
|
+
`Schedule binding "${binding.scheduleId ?? "schedule"}" targets method "${binding.targetMethodId}" without declared input ports.`,
|
|
933
|
+
`${location}.targetInputPortId`,
|
|
934
|
+
);
|
|
935
|
+
} else if (
|
|
936
|
+
targetInputPortIdPresent &&
|
|
937
|
+
!method.inputPorts.some((port) => port?.portId === binding.targetInputPortId)
|
|
938
|
+
) {
|
|
939
|
+
pushIssue(
|
|
940
|
+
issues,
|
|
941
|
+
"error",
|
|
942
|
+
"unknown-schedule-binding-port",
|
|
943
|
+
`Schedule binding "${binding.scheduleId ?? "schedule"}" targets unknown input port "${binding.targetInputPortId}" on method "${binding.targetMethodId}".`,
|
|
944
|
+
`${location}.targetInputPortId`,
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
function validateServiceBinding(binding, issues, location) {
|
|
951
|
+
if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
|
|
952
|
+
pushIssue(
|
|
953
|
+
issues,
|
|
954
|
+
"error",
|
|
955
|
+
"invalid-service-binding",
|
|
956
|
+
"Service binding entries must be objects.",
|
|
957
|
+
location,
|
|
958
|
+
);
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
validateStringField(
|
|
962
|
+
issues,
|
|
963
|
+
binding.serviceId,
|
|
964
|
+
`${location}.serviceId`,
|
|
965
|
+
"Service binding serviceId",
|
|
966
|
+
);
|
|
967
|
+
validateBindingModeField(
|
|
968
|
+
issues,
|
|
969
|
+
binding.bindingMode,
|
|
970
|
+
`${location}.bindingMode`,
|
|
971
|
+
"Service binding bindingMode",
|
|
972
|
+
);
|
|
973
|
+
validateStringField(
|
|
974
|
+
issues,
|
|
975
|
+
binding.serviceKind,
|
|
976
|
+
`${location}.serviceKind`,
|
|
977
|
+
"Service binding serviceKind",
|
|
978
|
+
);
|
|
979
|
+
validateOptionalStringField(
|
|
980
|
+
issues,
|
|
981
|
+
binding.triggerId,
|
|
982
|
+
`${location}.triggerId`,
|
|
983
|
+
"Service binding triggerId",
|
|
984
|
+
);
|
|
985
|
+
validateOptionalStringField(
|
|
986
|
+
issues,
|
|
987
|
+
binding.protocolId,
|
|
988
|
+
`${location}.protocolId`,
|
|
989
|
+
"Service binding protocolId",
|
|
990
|
+
);
|
|
991
|
+
validateOptionalStringField(
|
|
992
|
+
issues,
|
|
993
|
+
binding.routePath,
|
|
994
|
+
`${location}.routePath`,
|
|
995
|
+
"Service binding routePath",
|
|
996
|
+
);
|
|
997
|
+
validateOptionalStringField(
|
|
998
|
+
issues,
|
|
999
|
+
binding.method,
|
|
1000
|
+
`${location}.method`,
|
|
1001
|
+
"Service binding method",
|
|
1002
|
+
);
|
|
1003
|
+
if (
|
|
1004
|
+
binding.transportKind !== null &&
|
|
1005
|
+
binding.transportKind !== undefined &&
|
|
1006
|
+
!ProtocolTransportKindSet.has(
|
|
1007
|
+
normalizeProtocolTransportKindName(binding.transportKind),
|
|
1008
|
+
)
|
|
1009
|
+
) {
|
|
1010
|
+
pushIssue(
|
|
1011
|
+
issues,
|
|
1012
|
+
"error",
|
|
1013
|
+
"unknown-service-transport-kind",
|
|
1014
|
+
`Service binding transportKind must be one of: ${Array.from(
|
|
1015
|
+
ProtocolTransportKindSet,
|
|
1016
|
+
).join(", ")}.`,
|
|
1017
|
+
`${location}.transportKind`,
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
validateOptionalStringField(
|
|
1021
|
+
issues,
|
|
1022
|
+
binding.adapter,
|
|
1023
|
+
`${location}.adapter`,
|
|
1024
|
+
"Service binding adapter",
|
|
1025
|
+
);
|
|
1026
|
+
validateOptionalStringField(
|
|
1027
|
+
issues,
|
|
1028
|
+
binding.listenHost,
|
|
1029
|
+
`${location}.listenHost`,
|
|
1030
|
+
"Service binding listenHost",
|
|
1031
|
+
);
|
|
1032
|
+
validatePortField(
|
|
1033
|
+
issues,
|
|
1034
|
+
binding.listenPort,
|
|
1035
|
+
`${location}.listenPort`,
|
|
1036
|
+
"Service binding listenPort",
|
|
1037
|
+
);
|
|
1038
|
+
validateOptionalStringField(
|
|
1039
|
+
issues,
|
|
1040
|
+
binding.remoteUrl,
|
|
1041
|
+
`${location}.remoteUrl`,
|
|
1042
|
+
"Service binding remoteUrl",
|
|
1043
|
+
);
|
|
1044
|
+
validateStringArrayField(
|
|
1045
|
+
issues,
|
|
1046
|
+
binding.allowTransports,
|
|
1047
|
+
`${location}.allowTransports`,
|
|
1048
|
+
"Service binding allowTransports",
|
|
1049
|
+
);
|
|
1050
|
+
validateOptionalStringField(
|
|
1051
|
+
issues,
|
|
1052
|
+
binding.authPolicyId,
|
|
1053
|
+
`${location}.authPolicyId`,
|
|
1054
|
+
"Service binding authPolicyId",
|
|
1055
|
+
);
|
|
1056
|
+
validateOptionalStringField(
|
|
1057
|
+
issues,
|
|
1058
|
+
binding.description,
|
|
1059
|
+
`${location}.description`,
|
|
1060
|
+
"Service binding description",
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function validateAuthPolicy(binding, issues, location) {
|
|
1065
|
+
if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
|
|
1066
|
+
pushIssue(
|
|
1067
|
+
issues,
|
|
1068
|
+
"error",
|
|
1069
|
+
"invalid-auth-policy",
|
|
1070
|
+
"Auth policy entries must be objects.",
|
|
1071
|
+
location,
|
|
1072
|
+
);
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
validateStringField(
|
|
1076
|
+
issues,
|
|
1077
|
+
binding.policyId,
|
|
1078
|
+
`${location}.policyId`,
|
|
1079
|
+
"Auth policy policyId",
|
|
1080
|
+
);
|
|
1081
|
+
validateBindingModeField(
|
|
1082
|
+
issues,
|
|
1083
|
+
binding.bindingMode,
|
|
1084
|
+
`${location}.bindingMode`,
|
|
1085
|
+
"Auth policy bindingMode",
|
|
1086
|
+
);
|
|
1087
|
+
validateStringField(
|
|
1088
|
+
issues,
|
|
1089
|
+
binding.targetKind,
|
|
1090
|
+
`${location}.targetKind`,
|
|
1091
|
+
"Auth policy targetKind",
|
|
1092
|
+
);
|
|
1093
|
+
validateOptionalStringField(
|
|
1094
|
+
issues,
|
|
1095
|
+
binding.targetId,
|
|
1096
|
+
`${location}.targetId`,
|
|
1097
|
+
"Auth policy targetId",
|
|
1098
|
+
);
|
|
1099
|
+
validateOptionalStringField(
|
|
1100
|
+
issues,
|
|
1101
|
+
binding.adapter,
|
|
1102
|
+
`${location}.adapter`,
|
|
1103
|
+
"Auth policy adapter",
|
|
1104
|
+
);
|
|
1105
|
+
validateOptionalStringField(
|
|
1106
|
+
issues,
|
|
1107
|
+
binding.walletProfileId,
|
|
1108
|
+
`${location}.walletProfileId`,
|
|
1109
|
+
"Auth policy walletProfileId",
|
|
1110
|
+
);
|
|
1111
|
+
validateOptionalStringField(
|
|
1112
|
+
issues,
|
|
1113
|
+
binding.trustMapId,
|
|
1114
|
+
`${location}.trustMapId`,
|
|
1115
|
+
"Auth policy trustMapId",
|
|
1116
|
+
);
|
|
1117
|
+
validateStringArrayField(
|
|
1118
|
+
issues,
|
|
1119
|
+
binding.allowPeerIds,
|
|
1120
|
+
`${location}.allowPeerIds`,
|
|
1121
|
+
"Auth policy allowPeerIds",
|
|
1122
|
+
);
|
|
1123
|
+
validateStringArrayField(
|
|
1124
|
+
issues,
|
|
1125
|
+
binding.allowServerKeys,
|
|
1126
|
+
`${location}.allowServerKeys`,
|
|
1127
|
+
"Auth policy allowServerKeys",
|
|
1128
|
+
);
|
|
1129
|
+
validateStringArrayField(
|
|
1130
|
+
issues,
|
|
1131
|
+
binding.allowEntityIds,
|
|
1132
|
+
`${location}.allowEntityIds`,
|
|
1133
|
+
"Auth policy allowEntityIds",
|
|
1134
|
+
);
|
|
1135
|
+
validateOptionalStringField(
|
|
1136
|
+
issues,
|
|
1137
|
+
binding.description,
|
|
1138
|
+
`${location}.description`,
|
|
1139
|
+
"Auth policy description",
|
|
1140
|
+
);
|
|
1141
|
+
if (
|
|
1142
|
+
binding.allowPeerIds.length === 0 &&
|
|
1143
|
+
binding.allowServerKeys.length === 0 &&
|
|
1144
|
+
binding.allowEntityIds.length === 0 &&
|
|
1145
|
+
!binding.walletProfileId &&
|
|
1146
|
+
!binding.trustMapId
|
|
1147
|
+
) {
|
|
1148
|
+
pushIssue(
|
|
1149
|
+
issues,
|
|
1150
|
+
"warning",
|
|
1151
|
+
"open-auth-policy",
|
|
1152
|
+
"Auth policies should declare at least one allow-list or trust map.",
|
|
1153
|
+
location,
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
function validatePublicationBinding(
|
|
1159
|
+
binding,
|
|
1160
|
+
issues,
|
|
1161
|
+
location,
|
|
1162
|
+
manifest,
|
|
1163
|
+
manifestMethodLookup,
|
|
1164
|
+
) {
|
|
1165
|
+
if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
|
|
1166
|
+
pushIssue(
|
|
1167
|
+
issues,
|
|
1168
|
+
"error",
|
|
1169
|
+
"invalid-publication-binding",
|
|
1170
|
+
"Publication binding entries must be objects.",
|
|
1171
|
+
location,
|
|
1172
|
+
);
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
validateStringField(
|
|
1176
|
+
issues,
|
|
1177
|
+
binding.publicationId,
|
|
1178
|
+
`${location}.publicationId`,
|
|
1179
|
+
"Publication binding publicationId",
|
|
1180
|
+
);
|
|
1181
|
+
validateBindingModeField(
|
|
1182
|
+
issues,
|
|
1183
|
+
binding.bindingMode,
|
|
1184
|
+
`${location}.bindingMode`,
|
|
1185
|
+
"Publication binding bindingMode",
|
|
1186
|
+
);
|
|
1187
|
+
validateStringField(
|
|
1188
|
+
issues,
|
|
1189
|
+
binding.sourceKind,
|
|
1190
|
+
`${location}.sourceKind`,
|
|
1191
|
+
"Publication binding sourceKind",
|
|
1192
|
+
);
|
|
1193
|
+
validateOptionalStringField(
|
|
1194
|
+
issues,
|
|
1195
|
+
binding.sourceMethodId,
|
|
1196
|
+
`${location}.sourceMethodId`,
|
|
1197
|
+
"Publication binding sourceMethodId",
|
|
1198
|
+
);
|
|
1199
|
+
validateOptionalStringField(
|
|
1200
|
+
issues,
|
|
1201
|
+
binding.sourceOutputPortId,
|
|
1202
|
+
`${location}.sourceOutputPortId`,
|
|
1203
|
+
"Publication binding sourceOutputPortId",
|
|
1204
|
+
);
|
|
1205
|
+
validateOptionalStringField(
|
|
1206
|
+
issues,
|
|
1207
|
+
binding.sourceNodeId,
|
|
1208
|
+
`${location}.sourceNodeId`,
|
|
1209
|
+
"Publication binding sourceNodeId",
|
|
1210
|
+
);
|
|
1211
|
+
validateOptionalStringField(
|
|
1212
|
+
issues,
|
|
1213
|
+
binding.sourceTriggerId,
|
|
1214
|
+
`${location}.sourceTriggerId`,
|
|
1215
|
+
"Publication binding sourceTriggerId",
|
|
1216
|
+
);
|
|
1217
|
+
validateOptionalStringField(
|
|
1218
|
+
issues,
|
|
1219
|
+
binding.topic,
|
|
1220
|
+
`${location}.topic`,
|
|
1221
|
+
"Publication binding topic",
|
|
1222
|
+
);
|
|
1223
|
+
validateOptionalStringField(
|
|
1224
|
+
issues,
|
|
1225
|
+
binding.wireId,
|
|
1226
|
+
`${location}.wireId`,
|
|
1227
|
+
"Publication binding wireId",
|
|
1228
|
+
);
|
|
1229
|
+
validateOptionalStringField(
|
|
1230
|
+
issues,
|
|
1231
|
+
binding.schemaName,
|
|
1232
|
+
`${location}.schemaName`,
|
|
1233
|
+
"Publication binding schemaName",
|
|
1234
|
+
);
|
|
1235
|
+
validateOptionalStringField(
|
|
1236
|
+
issues,
|
|
1237
|
+
binding.mediaType,
|
|
1238
|
+
`${location}.mediaType`,
|
|
1239
|
+
"Publication binding mediaType",
|
|
1240
|
+
);
|
|
1241
|
+
validateOptionalStringField(
|
|
1242
|
+
issues,
|
|
1243
|
+
binding.archivePath,
|
|
1244
|
+
`${location}.archivePath`,
|
|
1245
|
+
"Publication binding archivePath",
|
|
1246
|
+
);
|
|
1247
|
+
validateOptionalStringField(
|
|
1248
|
+
issues,
|
|
1249
|
+
binding.queryServiceId,
|
|
1250
|
+
`${location}.queryServiceId`,
|
|
1251
|
+
"Publication binding queryServiceId",
|
|
1252
|
+
);
|
|
1253
|
+
validateOptionalStringField(
|
|
1254
|
+
issues,
|
|
1255
|
+
binding.pinPolicy,
|
|
1256
|
+
`${location}.pinPolicy`,
|
|
1257
|
+
"Publication binding pinPolicy",
|
|
1258
|
+
);
|
|
1259
|
+
validateOptionalStringField(
|
|
1260
|
+
issues,
|
|
1261
|
+
binding.recordRangeStartField,
|
|
1262
|
+
`${location}.recordRangeStartField`,
|
|
1263
|
+
"Publication binding recordRangeStartField",
|
|
1264
|
+
);
|
|
1265
|
+
validateOptionalStringField(
|
|
1266
|
+
issues,
|
|
1267
|
+
binding.recordRangeStopField,
|
|
1268
|
+
`${location}.recordRangeStopField`,
|
|
1269
|
+
"Publication binding recordRangeStopField",
|
|
1270
|
+
);
|
|
1271
|
+
validateOptionalStringField(
|
|
1272
|
+
issues,
|
|
1273
|
+
binding.description,
|
|
1274
|
+
`${location}.description`,
|
|
1275
|
+
"Publication binding description",
|
|
1276
|
+
);
|
|
1277
|
+
if (
|
|
1278
|
+
!binding.sourceMethodId &&
|
|
1279
|
+
!binding.sourceNodeId &&
|
|
1280
|
+
!binding.sourceTriggerId
|
|
1281
|
+
) {
|
|
1282
|
+
pushIssue(
|
|
1283
|
+
issues,
|
|
1284
|
+
"error",
|
|
1285
|
+
"missing-publication-source",
|
|
1286
|
+
"Publication bindings must target a sourceMethodId, sourceNodeId, or sourceTriggerId.",
|
|
1287
|
+
location,
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
if (
|
|
1291
|
+
binding.emitPnm === true &&
|
|
1292
|
+
!binding.topic &&
|
|
1293
|
+
!binding.wireId &&
|
|
1294
|
+
!binding.schemaName
|
|
1295
|
+
) {
|
|
1296
|
+
pushIssue(
|
|
1297
|
+
issues,
|
|
1298
|
+
"warning",
|
|
1299
|
+
"pnm-without-routing-hint",
|
|
1300
|
+
"PNM-emitting publication bindings should declare a topic, wireId, or schemaName.",
|
|
1301
|
+
location,
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
if (binding.sourceMethodId && manifest) {
|
|
1305
|
+
const method = manifestMethodLookup.get(binding.sourceMethodId);
|
|
1306
|
+
if (!method) {
|
|
1307
|
+
pushIssue(
|
|
1308
|
+
issues,
|
|
1309
|
+
"error",
|
|
1310
|
+
"unknown-publication-binding-method",
|
|
1311
|
+
`Publication binding "${binding.publicationId ?? "publication"}" targets unknown method "${binding.sourceMethodId}".`,
|
|
1312
|
+
`${location}.sourceMethodId`,
|
|
1313
|
+
);
|
|
1314
|
+
} else if (
|
|
1315
|
+
binding.sourceOutputPortId &&
|
|
1316
|
+
!Array.isArray(method.outputPorts)
|
|
1317
|
+
) {
|
|
1318
|
+
pushIssue(
|
|
1319
|
+
issues,
|
|
1320
|
+
"error",
|
|
1321
|
+
"unknown-publication-binding-port",
|
|
1322
|
+
`Publication binding "${binding.publicationId ?? "publication"}" targets method "${binding.sourceMethodId}" without declared output ports.`,
|
|
1323
|
+
`${location}.sourceOutputPortId`,
|
|
1324
|
+
);
|
|
1325
|
+
} else if (
|
|
1326
|
+
binding.sourceOutputPortId &&
|
|
1327
|
+
!method.outputPorts.some(
|
|
1328
|
+
(port) => port?.portId === binding.sourceOutputPortId,
|
|
1329
|
+
)
|
|
1330
|
+
) {
|
|
1331
|
+
pushIssue(
|
|
1332
|
+
issues,
|
|
1333
|
+
"error",
|
|
1334
|
+
"unknown-publication-binding-port",
|
|
1335
|
+
`Publication binding "${binding.publicationId ?? "publication"}" targets unknown output port "${binding.sourceOutputPortId}" on method "${binding.sourceMethodId}".`,
|
|
1336
|
+
`${location}.sourceOutputPortId`,
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
export function validateDeploymentPlan(plan, options = {}) {
|
|
1343
|
+
const normalizedPlan = normalizeDeploymentPlan(plan);
|
|
1344
|
+
const issues = [];
|
|
1345
|
+
const manifest = options.manifest ?? null;
|
|
1346
|
+
const methodLookup = buildMethodLookup(manifest);
|
|
1347
|
+
const protocolLookup = new Map(
|
|
1348
|
+
Array.isArray(manifest?.protocols)
|
|
1349
|
+
? manifest.protocols
|
|
1350
|
+
.filter((entry) => typeof entry?.protocolId === "string")
|
|
1351
|
+
.map((entry) => [entry.protocolId, entry])
|
|
1352
|
+
: [],
|
|
1353
|
+
);
|
|
1354
|
+
|
|
1355
|
+
if (!Number.isInteger(normalizedPlan.formatVersion) || normalizedPlan.formatVersion < 1) {
|
|
1356
|
+
pushIssue(
|
|
1357
|
+
issues,
|
|
1358
|
+
"error",
|
|
1359
|
+
"invalid-format-version",
|
|
1360
|
+
"Deployment plan formatVersion must be an integer greater than or equal to 1.",
|
|
1361
|
+
"deploymentPlan.formatVersion",
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
if (
|
|
1365
|
+
manifest?.pluginId &&
|
|
1366
|
+
normalizedPlan.pluginId &&
|
|
1367
|
+
manifest.pluginId !== normalizedPlan.pluginId
|
|
1368
|
+
) {
|
|
1369
|
+
pushIssue(
|
|
1370
|
+
issues,
|
|
1371
|
+
"error",
|
|
1372
|
+
"deployment-plan-plugin-id-mismatch",
|
|
1373
|
+
`Deployment plan pluginId "${normalizedPlan.pluginId}" does not match manifest pluginId "${manifest.pluginId}".`,
|
|
1374
|
+
"deploymentPlan.pluginId",
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
if (
|
|
1378
|
+
manifest?.version &&
|
|
1379
|
+
normalizedPlan.version &&
|
|
1380
|
+
manifest.version !== normalizedPlan.version
|
|
1381
|
+
) {
|
|
1382
|
+
pushIssue(
|
|
1383
|
+
issues,
|
|
1384
|
+
"error",
|
|
1385
|
+
"deployment-plan-version-mismatch",
|
|
1386
|
+
`Deployment plan version "${normalizedPlan.version}" does not match manifest version "${manifest.version}".`,
|
|
1387
|
+
"deploymentPlan.version",
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
normalizedPlan.protocolInstallations.forEach((installation, index) => {
|
|
1392
|
+
validateProtocolInstallation(
|
|
1393
|
+
installation,
|
|
1394
|
+
issues,
|
|
1395
|
+
`deploymentPlan.protocolInstallations[${index}]`,
|
|
1396
|
+
protocolLookup,
|
|
1397
|
+
);
|
|
1398
|
+
});
|
|
1399
|
+
normalizedPlan.inputBindings.forEach((binding, index) => {
|
|
1400
|
+
validateInputBinding(
|
|
1401
|
+
binding,
|
|
1402
|
+
issues,
|
|
1403
|
+
`deploymentPlan.inputBindings[${index}]`,
|
|
1404
|
+
manifest,
|
|
1405
|
+
methodLookup,
|
|
1406
|
+
);
|
|
1407
|
+
});
|
|
1408
|
+
normalizedPlan.scheduleBindings.forEach((binding, index) => {
|
|
1409
|
+
validateScheduleBinding(
|
|
1410
|
+
binding,
|
|
1411
|
+
issues,
|
|
1412
|
+
`deploymentPlan.scheduleBindings[${index}]`,
|
|
1413
|
+
manifest,
|
|
1414
|
+
methodLookup,
|
|
1415
|
+
);
|
|
1416
|
+
});
|
|
1417
|
+
normalizedPlan.serviceBindings.forEach((binding, index) => {
|
|
1418
|
+
validateServiceBinding(
|
|
1419
|
+
binding,
|
|
1420
|
+
issues,
|
|
1421
|
+
`deploymentPlan.serviceBindings[${index}]`,
|
|
1422
|
+
);
|
|
1423
|
+
});
|
|
1424
|
+
normalizedPlan.authPolicies.forEach((binding, index) => {
|
|
1425
|
+
validateAuthPolicy(
|
|
1426
|
+
binding,
|
|
1427
|
+
issues,
|
|
1428
|
+
`deploymentPlan.authPolicies[${index}]`,
|
|
1429
|
+
);
|
|
1430
|
+
});
|
|
1431
|
+
normalizedPlan.publicationBindings.forEach((binding, index) => {
|
|
1432
|
+
validatePublicationBinding(
|
|
1433
|
+
binding,
|
|
1434
|
+
issues,
|
|
1435
|
+
`deploymentPlan.publicationBindings[${index}]`,
|
|
1436
|
+
manifest,
|
|
1437
|
+
methodLookup,
|
|
1438
|
+
);
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
const authPolicyIds = new Set(
|
|
1442
|
+
normalizedPlan.authPolicies
|
|
1443
|
+
.map((entry) => entry.policyId)
|
|
1444
|
+
.filter((entry) => typeof entry === "string" && entry.length > 0),
|
|
1445
|
+
);
|
|
1446
|
+
normalizedPlan.serviceBindings.forEach((binding, index) => {
|
|
1447
|
+
if (binding.authPolicyId && !authPolicyIds.has(binding.authPolicyId)) {
|
|
1448
|
+
pushIssue(
|
|
1449
|
+
issues,
|
|
1450
|
+
"error",
|
|
1451
|
+
"unknown-service-auth-policy",
|
|
1452
|
+
`Service binding "${binding.serviceId ?? "service"}" references unknown auth policy "${binding.authPolicyId}".`,
|
|
1453
|
+
`deploymentPlan.serviceBindings[${index}].authPolicyId`,
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
});
|
|
1457
|
+
const serviceIds = new Set(
|
|
1458
|
+
normalizedPlan.serviceBindings
|
|
1459
|
+
.map((entry) => entry.serviceId)
|
|
1460
|
+
.filter((entry) => typeof entry === "string" && entry.length > 0),
|
|
1461
|
+
);
|
|
1462
|
+
normalizedPlan.publicationBindings.forEach((binding, index) => {
|
|
1463
|
+
if (binding.queryServiceId && !serviceIds.has(binding.queryServiceId)) {
|
|
1464
|
+
pushIssue(
|
|
1465
|
+
issues,
|
|
1466
|
+
"error",
|
|
1467
|
+
"unknown-publication-query-service",
|
|
1468
|
+
`Publication binding "${binding.publicationId ?? "publication"}" references unknown query service "${binding.queryServiceId}".`,
|
|
1469
|
+
`deploymentPlan.publicationBindings[${index}].queryServiceId`,
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1474
|
+
const errors = issues.filter((issue) => issue.severity === "error");
|
|
1475
|
+
const warnings = issues.filter((issue) => issue.severity === "warning");
|
|
1476
|
+
return {
|
|
1477
|
+
ok: errors.length === 0,
|
|
1478
|
+
plan: normalizedPlan,
|
|
1479
|
+
issues,
|
|
1480
|
+
errors,
|
|
1481
|
+
warnings,
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
export function createDeploymentPlanBundleEntry(plan, options = {}) {
|
|
1486
|
+
const normalizedPlan = normalizeDeploymentPlan(plan);
|
|
1487
|
+
return {
|
|
1488
|
+
entryId: options.entryId ?? SDS_DEPLOYMENT_ENTRY_ID,
|
|
1489
|
+
role: options.role ?? "auxiliary",
|
|
1490
|
+
sectionName: options.sectionName ?? SDS_DEPLOYMENT_SECTION_NAME,
|
|
1491
|
+
payloadEncoding: "json-utf8",
|
|
1492
|
+
mediaType: options.mediaType ?? SDS_DEPLOYMENT_MEDIA_TYPE,
|
|
1493
|
+
payload: normalizedPlan,
|
|
1494
|
+
description:
|
|
1495
|
+
options.description ??
|
|
1496
|
+
"Resolved deployment plan with protocol installations, bindings, schedules, services, auth policy, and publication policy.",
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
function findDeploymentPlanEntryInEntries(entries) {
|
|
1501
|
+
if (!Array.isArray(entries)) {
|
|
1502
|
+
return null;
|
|
1503
|
+
}
|
|
1504
|
+
return (
|
|
1505
|
+
entries.find((entry) => entry?.entryId === SDS_DEPLOYMENT_ENTRY_ID) ??
|
|
1506
|
+
entries.find((entry) => entry?.sectionName === SDS_DEPLOYMENT_SECTION_NAME) ??
|
|
1507
|
+
null
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
export function findDeploymentPlanEntry(bundleLike) {
|
|
1512
|
+
return (
|
|
1513
|
+
findDeploymentPlanEntryInEntries(bundleLike?.entries) ??
|
|
1514
|
+
findDeploymentPlanEntryInEntries(bundleLike?.bundle?.entries) ??
|
|
1515
|
+
findModuleBundleEntry(bundleLike?.bundle ?? bundleLike, SDS_DEPLOYMENT_ENTRY_ID) ??
|
|
1516
|
+
null
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
function planHasContent(plan) {
|
|
1521
|
+
return (
|
|
1522
|
+
plan.protocolInstallations.length > 0 ||
|
|
1523
|
+
plan.inputBindings.length > 0 ||
|
|
1524
|
+
plan.scheduleBindings.length > 0 ||
|
|
1525
|
+
plan.serviceBindings.length > 0 ||
|
|
1526
|
+
plan.authPolicies.length > 0 ||
|
|
1527
|
+
plan.publicationBindings.length > 0 ||
|
|
1528
|
+
Boolean(plan.pluginId) ||
|
|
1529
|
+
Boolean(plan.version)
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
export function readDeploymentPlanFromBundle(bundleLike) {
|
|
1534
|
+
const directPlan = normalizeDeploymentPlan(bundleLike?.deploymentPlan);
|
|
1535
|
+
if (planHasContent(directPlan)) {
|
|
1536
|
+
return directPlan;
|
|
1537
|
+
}
|
|
1538
|
+
const entry = findDeploymentPlanEntry(bundleLike);
|
|
1539
|
+
if (!entry) {
|
|
1540
|
+
return null;
|
|
1541
|
+
}
|
|
1542
|
+
if (entry.decodedDeploymentPlan) {
|
|
1543
|
+
return normalizeDeploymentPlan(entry.decodedDeploymentPlan);
|
|
1544
|
+
}
|
|
1545
|
+
if (entry.decodedPayload && typeof entry.decodedPayload === "object") {
|
|
1546
|
+
return normalizeDeploymentPlan(entry.decodedPayload);
|
|
1547
|
+
}
|
|
1548
|
+
if (entry.payload !== undefined) {
|
|
1549
|
+
return normalizeDeploymentPlan(decodeModuleBundleEntryPayload(entry));
|
|
1550
|
+
}
|
|
1551
|
+
return null;
|
|
1552
|
+
}
|