@reventlessdev/reventless-aws 3.0.0-alpha.231 → 3.0.0-alpha.233
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/CHANGELOG.md +23 -0
- package/package.json +7 -7
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res +137 -0
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res.mjs +121 -0
- package/src/adapter/Api/Platform_AdminScan_Ops.res +136 -0
- package/src/adapter/Api/Platform_AdminScan_Ops.res.mjs +98 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +12 -90
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +11 -84
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +86 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +79 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +9 -79
- package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +7 -143
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +48 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs +63 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +21 -80
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +23 -68
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +70 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs +68 -0
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +1 -1
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +1 -1
- package/src/adapter/Postgres/PgMigration_Builder.res +1 -1
- package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
- package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +42 -126
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +246 -0
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +145 -0
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +27 -2
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +10 -3
- package/src/adapter/Runtime/CounterEntryPoint.mjs +19 -73
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res +145 -0
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res.mjs +129 -0
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -3
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +11 -6
- package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +52 -510
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +816 -0
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +651 -0
- package/src/adapter/Runtime/EventMapperEntryPoint.mjs +23 -67
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +126 -0
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res.mjs +83 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +16 -63
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +150 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res.mjs +111 -0
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +96 -0
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs +70 -0
- package/src/adapter/Runtime/PgMigrationEntryPoint.res +54 -0
- package/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs +37 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +20 -55
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +155 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +94 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +80 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +39 -0
- package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res +4 -4
- package/src/adapter/Runtime/ReadModelEntryPoint.mjs +34 -153
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +114 -0
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res.mjs +83 -0
- package/src/adapter/Runtime/SideEffectEntryPoint.mjs +23 -64
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +71 -0
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res.mjs +57 -0
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +34 -165
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +206 -0
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +105 -0
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +156 -0
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res.mjs +123 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +18 -138
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +198 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res.mjs +155 -0
- package/src/adapter/StateTopic/StateTopic_AppSync.res +12 -170
- package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +7 -167
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +208 -0
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs +183 -0
- package/src/components/AutomationSlice_Builder.res +2 -0
- package/src/components/AutomationSlice_Builder.res.mjs +1 -1
- package/src/components/OutboundTranslationSlice_Builder.res +1 -0
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
- package/tests/AppSyncEventsSigner_OpsTest.res +75 -0
- package/tests/AppSyncEventsSigner_OpsTest.res.mjs +44 -0
- package/tests/AppSyncEventsSigner_ParityFixture.mjs +36 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res +165 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +280 -0
- package/tests/CounterEntryPoint_OpsTest.res +115 -0
- package/tests/CounterEntryPoint_OpsTest.res.mjs +150 -0
- package/tests/EventCollectorEntryPoint_OpsTest.res +216 -0
- package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +339 -0
- package/tests/EventMapperEntryPoint_OpsTest.res +103 -0
- package/tests/EventMapperEntryPoint_OpsTest.res.mjs +120 -0
- package/tests/ExtensionPointEntryPoint_OpsTest.res +57 -0
- package/tests/ExtensionPointEntryPoint_OpsTest.res.mjs +54 -0
- package/tests/PgMigrationEntryPoint_IntegrationTest.res +22 -31
- package/tests/PgMigrationEntryPoint_IntegrationTest.res.mjs +20 -23
- package/tests/PgQueryResolverEntryPoint_OpsTest.res +38 -0
- package/tests/PgQueryResolverEntryPoint_OpsTest.res.mjs +31 -0
- package/tests/Platform_AdminScan_OpsTest.res +80 -0
- package/tests/Platform_AdminScan_OpsTest.res.mjs +127 -0
- package/tests/ReadModelEntryPoint_OpsTest.res +141 -0
- package/tests/ReadModelEntryPoint_OpsTest.res.mjs +267 -0
- package/tests/SideEffectEntryPoint_OpsTest.res +79 -0
- package/tests/SideEffectEntryPoint_OpsTest.res.mjs +105 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res +167 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +296 -0
- package/tests/TaskBucketEntryPoint_OpsTest.res +117 -0
- package/tests/TaskBucketEntryPoint_OpsTest.res.mjs +165 -0
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +42 -13
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res.mjs +29 -6
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +0 -57
- package/src/adapter/Runtime/PgMigrationEntryPoint.mjs +0 -40
|
@@ -15,81 +15,6 @@ import * as AppSync_Resolver_Native$ReventlessAws from "./AppSync_Resolver_Nativ
|
|
|
15
15
|
import * as Platform_Admin_Structure$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_Admin_Structure.res.mjs";
|
|
16
16
|
import * as Platform_ComponentDefinitionsApi$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_ComponentDefinitionsApi.res.mjs";
|
|
17
17
|
|
|
18
|
-
function makeHandlerCode(param, adminEntryJson) {
|
|
19
|
-
return `
|
|
20
|
-
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
21
|
-
import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
|
|
22
|
-
|
|
23
|
-
const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
|
|
24
|
-
const TABLE = process.env.PLUGIN_RM_TABLE;
|
|
25
|
-
const ADMIN_ENTRY = ` + adminEntryJson + `;
|
|
26
|
-
|
|
27
|
-
export async function handler() {
|
|
28
|
-
if (!TABLE) {
|
|
29
|
-
console.error("Platform_ComponentDefinitions: PLUGIN_RM_TABLE env var not set");
|
|
30
|
-
return [ADMIN_ENTRY];
|
|
31
|
-
}
|
|
32
|
-
const items = [];
|
|
33
|
-
let exclusiveStartKey;
|
|
34
|
-
do {
|
|
35
|
-
const out = await client.send(new ScanCommand({
|
|
36
|
-
TableName: TABLE,
|
|
37
|
-
FilterExpression: "contains(#status, :connected)",
|
|
38
|
-
ExpressionAttributeNames: { "#status": "status" },
|
|
39
|
-
ExpressionAttributeValues: { ":connected": "Connected" },
|
|
40
|
-
Limit: 1000,
|
|
41
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
42
|
-
}));
|
|
43
|
-
if (out.Items) items.push(...out.Items);
|
|
44
|
-
exclusiveStartKey = out.LastEvaluatedKey;
|
|
45
|
-
} while (exclusiveStartKey);
|
|
46
|
-
|
|
47
|
-
// Platform invariant: one version per plugin at a time, so the UI sees just
|
|
48
|
-
// the bare plugin name (mirrors ReventlessCore.Plugin.name). The Connected
|
|
49
|
-
// filter above does not enforce single-version: during a rolling deploy or a
|
|
50
|
-
// missed retire, several versions of the same plugin sit in Connected at once
|
|
51
|
-
// and each would otherwise emit a full duplicate set of AutoUI menu entries.
|
|
52
|
-
// Collapse to the highest version per plugin name (mirrors
|
|
53
|
-
// ReventlessCore.Plugin.compareVersions).
|
|
54
|
-
const cmpVer = (a, b) => {
|
|
55
|
-
const pa = String(a).replace(/[-+]/g, ".").split(".");
|
|
56
|
-
const pb = String(b).replace(/[-+]/g, ".").split(".");
|
|
57
|
-
const len = Math.max(pa.length, pb.length);
|
|
58
|
-
for (let i = 0; i < len; i++) {
|
|
59
|
-
const sa = pa[i] ?? "", sb = pb[i] ?? "";
|
|
60
|
-
const na = Number(sa), nb = Number(sb);
|
|
61
|
-
const bothNum = sa !== "" && sb !== "" && !Number.isNaN(na) && !Number.isNaN(nb);
|
|
62
|
-
if (bothNum) { if (na !== nb) return na > nb ? 1 : -1; }
|
|
63
|
-
else if (sa !== sb) return sa > sb ? 1 : -1;
|
|
64
|
-
}
|
|
65
|
-
return 0;
|
|
66
|
-
};
|
|
67
|
-
// The persisted structure carries Internal ReadModels / StateViewSlices for
|
|
68
|
-
// developer tooling; they must stay out of the deployed AutoUI. Mirror
|
|
69
|
-
// ReventlessCore.Platform_ComponentDefinitionsApi.isPublicQueryable here on the
|
|
70
|
-
// read path (the persisted structure is pre-filter — it is NOT re-encoded).
|
|
71
|
-
const isPublicQueryable = (q) => q?.visibility !== "Internal";
|
|
72
|
-
const filterStructure = (s) => ({
|
|
73
|
-
...s,
|
|
74
|
-
readModels: (s.readModels ?? []).filter(isPublicQueryable),
|
|
75
|
-
stateViewSlices: (s.stateViewSlices ?? []).filter(isPublicQueryable),
|
|
76
|
-
});
|
|
77
|
-
const latestByName = new Map();
|
|
78
|
-
for (const item of items) {
|
|
79
|
-
if (!item || !item.structure) continue;
|
|
80
|
-
const name = String(item.name).split("@")[0];
|
|
81
|
-
const version = String(item.name).split("@")[1] ?? "";
|
|
82
|
-
const prev = latestByName.get(name);
|
|
83
|
-
if (!prev || cmpVer(version, prev.version) > 0) {
|
|
84
|
-
latestByName.set(name, { version, entry: { pluginId: name, ...filterStructure(item.structure) } });
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
const userEntries = [...latestByName.values()].map(v => v.entry);
|
|
88
|
-
return [ADMIN_ENTRY, ...userEntries];
|
|
89
|
-
}
|
|
90
|
-
`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
18
|
let resolverCode = `
|
|
94
19
|
import { util } from '@aws-appsync/utils';
|
|
95
20
|
export function request(ctx) {
|
|
@@ -125,17 +50,16 @@ function make(api, pluginReadModelTableName, opts) {
|
|
|
125
50
|
}, opts$1);
|
|
126
51
|
});
|
|
127
52
|
let adminEntryJson = JSON.stringify(Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry(Platform_Admin_Structure$ReventlessCore.pluginId, Platform_Admin_Structure$ReventlessCore.structure));
|
|
128
|
-
let
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
let
|
|
133
|
-
let sourceCodeHash = Util_Bundle$ReventlessAws.hashString(handlerCodeStub + "\n---\n" + loaderHash);
|
|
53
|
+
let packageDirs = Object.fromEntries([[
|
|
54
|
+
"@reventlessdev/reventless-aws",
|
|
55
|
+
Util_Bundle$ReventlessAws.resolvePackageRoot("@reventlessdev/reventless-aws")
|
|
56
|
+
]]);
|
|
57
|
+
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs", packageDirs, undefined);
|
|
134
58
|
let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
|
|
135
59
|
let lambda = new (Aws.lambda.Function)(name + "Lambda", {
|
|
136
60
|
handler: "index.handler",
|
|
137
61
|
runtime: "nodejs22.x",
|
|
138
|
-
code: code,
|
|
62
|
+
code: match.code,
|
|
139
63
|
role: lambdaRole.arn,
|
|
140
64
|
memorySize: 512,
|
|
141
65
|
timeout: 30,
|
|
@@ -151,6 +75,10 @@ function make(api, pluginReadModelTableName, opts) {
|
|
|
151
75
|
"PLUGIN_RM_TABLE",
|
|
152
76
|
pluginReadModelTableName
|
|
153
77
|
],
|
|
78
|
+
[
|
|
79
|
+
"ADMIN_ENTRY_JSON",
|
|
80
|
+
adminEntryJson
|
|
81
|
+
],
|
|
154
82
|
[
|
|
155
83
|
"NODE_OPTIONS",
|
|
156
84
|
Util_Bundle$ReventlessAws.esmLoaderNodeOptions
|
|
@@ -161,7 +89,7 @@ function make(api, pluginReadModelTableName, opts) {
|
|
|
161
89
|
]
|
|
162
90
|
])
|
|
163
91
|
},
|
|
164
|
-
sourceCodeHash: sourceCodeHash
|
|
92
|
+
sourceCodeHash: match.sourceCodeHash
|
|
165
93
|
}, opts$1);
|
|
166
94
|
let dataSourceRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "DataSource", Pulumi.output(AWS$ReventlessAws.AppSync.principal), AWS_Tags$ReventlessAws.make(name + "DataSource", "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
|
|
167
95
|
Pulumi.all([
|
|
@@ -190,7 +118,6 @@ function make(api, pluginReadModelTableName, opts) {
|
|
|
190
118
|
}
|
|
191
119
|
|
|
192
120
|
export {
|
|
193
|
-
makeHandlerCode,
|
|
194
121
|
resolverCode,
|
|
195
122
|
make,
|
|
196
123
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Runtime handler for the `Platform_ComponentDefinitions` admin GraphQL resolver
|
|
2
|
+
// — compiled, type-checked ReScript (replaces the inline JS `makeHandlerCode`
|
|
3
|
+
// string in Platform_ComponentDefinitions_Lambda.res). Runtime-pure (see
|
|
4
|
+
// Platform_AdminScan_Ops); the deploy-time module bundles this into an
|
|
5
|
+
// EntryPoint archive and re-exports `handler`.
|
|
6
|
+
//
|
|
7
|
+
// Scans the Plugin read model for Connected plugins carrying a `structure`,
|
|
8
|
+
// collapses to the highest version per plugin name, re-applies the
|
|
9
|
+
// `isPublicQueryable` filter (the persisted structure is PRE-filter — it still
|
|
10
|
+
// carries Internal ReadModels / StateViewSlices for developer tooling), and
|
|
11
|
+
// prepends the built-in Platform_Admin entry. The persisted structure is
|
|
12
|
+
// sury-encoded with the same shape as the in-memory adapter's
|
|
13
|
+
// encodePluginStructureEntry, so it is wrapped with `pluginId` without decoding.
|
|
14
|
+
|
|
15
|
+
@val @scope("process") external processEnv: dict<string> = "env"
|
|
16
|
+
|
|
17
|
+
let str = (item: dict<JSON.t>, key: string): option<string> =>
|
|
18
|
+
item->Dict.get(key)->Option.flatMap(JSON.Decode.string)
|
|
19
|
+
|
|
20
|
+
// Mirror ReventlessCore.Platform_ComponentDefinitionsApi.isPublicQueryable on
|
|
21
|
+
// the read path: a component is public unless its visibility is "Internal".
|
|
22
|
+
let isPublicQueryable = (q: JSON.t): bool =>
|
|
23
|
+
switch q->JSON.Decode.object->Option.flatMap(o => o->Dict.get("visibility"))->Option.flatMap(JSON.Decode.string) {
|
|
24
|
+
| Some("Internal") => false
|
|
25
|
+
| _ => true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Drop Internal ReadModels / StateViewSlices from a persisted structure. A
|
|
29
|
+
// missing/non-array field defaults to `[]` (mirrors the JS `?? []`).
|
|
30
|
+
let filterStructure = (structure: JSON.t): option<dict<JSON.t>> =>
|
|
31
|
+
switch structure->JSON.Decode.object {
|
|
32
|
+
| None => None
|
|
33
|
+
| Some(obj) =>
|
|
34
|
+
let out = Dict.fromArray(obj->Dict.toArray)
|
|
35
|
+
let filterArr = key =>
|
|
36
|
+
out
|
|
37
|
+
->Dict.get(key)
|
|
38
|
+
->Option.flatMap(JSON.Decode.array)
|
|
39
|
+
->Option.getOr([])
|
|
40
|
+
->Array.filter(isPublicQueryable)
|
|
41
|
+
->JSON.Encode.array
|
|
42
|
+
out->Dict.set("readModels", filterArr("readModels"))
|
|
43
|
+
out->Dict.set("stateViewSlices", filterArr("stateViewSlices"))
|
|
44
|
+
Some(out)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// `{pluginId: name, ...filterStructure(structure)}` — pluginId first so the
|
|
48
|
+
// structure's fields win on the (never-expected) key collision, matching the JS
|
|
49
|
+
// spread. Drops a row with no decodable structure (former `!item.structure`).
|
|
50
|
+
let toEntry = (item: dict<JSON.t>, ~name: string): option<JSON.t> =>
|
|
51
|
+
switch item->Dict.get("structure")->Option.flatMap(filterStructure) {
|
|
52
|
+
| None => None
|
|
53
|
+
| Some(structureObj) =>
|
|
54
|
+
let entry = Dict.make()
|
|
55
|
+
entry->Dict.set("pluginId", JSON.Encode.string(name))
|
|
56
|
+
structureObj->Dict.toArray->Array.forEach(((k, v)) => entry->Dict.set(k, v))
|
|
57
|
+
Some(JSON.Encode.object(entry))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The built-in Platform_Admin entry, injected at deploy time as the
|
|
61
|
+
// ADMIN_ENTRY_JSON env var (the admin never Connects to itself, so its structure
|
|
62
|
+
// never enters the Plugin read model).
|
|
63
|
+
let adminEntry: option<JSON.t> =
|
|
64
|
+
switch processEnv->Dict.get("ADMIN_ENTRY_JSON") {
|
|
65
|
+
| Some(s) if s != "" => Some(JSON.parseOrThrow(s))
|
|
66
|
+
| _ => None
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let handler = async (_event: JSON.t): array<JSON.t> => {
|
|
70
|
+
let admin = adminEntry->Option.mapOr([], e => [e])
|
|
71
|
+
switch processEnv->Dict.get("PLUGIN_RM_TABLE") {
|
|
72
|
+
| None | Some("") =>
|
|
73
|
+
Console.error("Platform_ComponentDefinitions: PLUGIN_RM_TABLE env var not set")
|
|
74
|
+
admin
|
|
75
|
+
| Some(table) =>
|
|
76
|
+
let items = await Platform_AdminScan_Ops.scanAll(
|
|
77
|
+
~tableName=table,
|
|
78
|
+
~filterExpression="contains(#status, :connected)",
|
|
79
|
+
~expressionAttributeNames=Dict.fromArray([("#status", "status")]),
|
|
80
|
+
~expressionAttributeValues=Dict.fromArray([(":connected", JSON.Encode.string("Connected"))]),
|
|
81
|
+
)
|
|
82
|
+
let userEntries =
|
|
83
|
+
Platform_AdminScan_Ops.latestByName(items, ~nameVersionOf=item => item->str("name"), ~toEntry)
|
|
84
|
+
Array.concat(admin, userEntries)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as Platform_AdminScan_Ops$ReventlessAws from "./Platform_AdminScan_Ops.res.mjs";
|
|
6
|
+
|
|
7
|
+
function str(item, key) {
|
|
8
|
+
return Stdlib_Option.flatMap(item[key], Stdlib_JSON.Decode.string);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isPublicQueryable(q) {
|
|
12
|
+
let match = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(q), o => o["visibility"]), Stdlib_JSON.Decode.string);
|
|
13
|
+
if (match !== undefined) {
|
|
14
|
+
return match !== "Internal";
|
|
15
|
+
} else {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function filterStructure(structure) {
|
|
21
|
+
let obj = Stdlib_JSON.Decode.object(structure);
|
|
22
|
+
if (obj === undefined) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
let out = Object.fromEntries(Object.entries(obj));
|
|
26
|
+
let filterArr = key => Stdlib_Option.getOr(Stdlib_Option.flatMap(out[key], Stdlib_JSON.Decode.array), []).filter(isPublicQueryable);
|
|
27
|
+
out["readModels"] = filterArr("readModels");
|
|
28
|
+
out["stateViewSlices"] = filterArr("stateViewSlices");
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function toEntry(item, name) {
|
|
33
|
+
let structureObj = Stdlib_Option.flatMap(item["structure"], filterStructure);
|
|
34
|
+
if (structureObj === undefined) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let entry = {};
|
|
38
|
+
entry["pluginId"] = name;
|
|
39
|
+
Object.entries(structureObj).forEach(param => {
|
|
40
|
+
entry[param[0]] = param[1];
|
|
41
|
+
});
|
|
42
|
+
return entry;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let s = process.env["ADMIN_ENTRY_JSON"];
|
|
46
|
+
|
|
47
|
+
let adminEntry = s !== undefined && s !== "" ? JSON.parse(s) : undefined;
|
|
48
|
+
|
|
49
|
+
async function handler(_event) {
|
|
50
|
+
let admin = Stdlib_Option.mapOr(adminEntry, [], e => [e]);
|
|
51
|
+
let table = process.env["PLUGIN_RM_TABLE"];
|
|
52
|
+
if (table !== undefined) {
|
|
53
|
+
if (table === "") {
|
|
54
|
+
console.error("Platform_ComponentDefinitions: PLUGIN_RM_TABLE env var not set");
|
|
55
|
+
return admin;
|
|
56
|
+
}
|
|
57
|
+
let items = await Platform_AdminScan_Ops$ReventlessAws.scanAll(table, "contains(#status, :connected)", Object.fromEntries([[
|
|
58
|
+
"#status",
|
|
59
|
+
"status"
|
|
60
|
+
]]), Object.fromEntries([[
|
|
61
|
+
":connected",
|
|
62
|
+
"Connected"
|
|
63
|
+
]]));
|
|
64
|
+
let userEntries = Platform_AdminScan_Ops$ReventlessAws.latestByName(items, item => str(item, "name"), toEntry);
|
|
65
|
+
return admin.concat(userEntries);
|
|
66
|
+
}
|
|
67
|
+
console.error("Platform_ComponentDefinitions: PLUGIN_RM_TABLE env var not set");
|
|
68
|
+
return admin;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
str,
|
|
73
|
+
isPublicQueryable,
|
|
74
|
+
filterStructure,
|
|
75
|
+
toEntry,
|
|
76
|
+
adminEntry,
|
|
77
|
+
handler,
|
|
78
|
+
}
|
|
79
|
+
/* s Not a pure module */
|
|
@@ -8,74 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
open PulumiAws
|
|
10
10
|
|
|
11
|
-
let makeHandlerCode = (~tableName as _: string): string =>
|
|
12
|
-
`
|
|
13
|
-
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
14
|
-
import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
|
|
15
|
-
|
|
16
|
-
const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
|
|
17
|
-
const TABLE = process.env.UI_FRAGMENT_RM_TABLE;
|
|
18
|
-
|
|
19
|
-
export async function handler() {
|
|
20
|
-
if (!TABLE) {
|
|
21
|
-
console.error("Platform_UIFragments: UI_FRAGMENT_RM_TABLE env var not set");
|
|
22
|
-
return [];
|
|
23
|
-
}
|
|
24
|
-
const items = [];
|
|
25
|
-
let exclusiveStartKey;
|
|
26
|
-
do {
|
|
27
|
-
const out = await client.send(new ScanCommand({
|
|
28
|
-
TableName: TABLE,
|
|
29
|
-
Limit: 1000,
|
|
30
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
31
|
-
}));
|
|
32
|
-
if (out.Items) items.push(...out.Items);
|
|
33
|
-
exclusiveStartKey = out.LastEvaluatedKey;
|
|
34
|
-
} while (exclusiveStartKey);
|
|
35
|
-
|
|
36
|
-
// Platform invariant: one version per plugin at a time, so the UI sees just
|
|
37
|
-
// the bare plugin name (mirrors ReventlessCore.Plugin.name). The registry is
|
|
38
|
-
// keyed by bare plugin name (a no-op for the split below), but rows persisted
|
|
39
|
-
// by the pre-slice registry were keyed name@version — keep the collapse to the
|
|
40
|
-
// highest version per plugin name (mirrors ReventlessCore.Plugin.compareVersions)
|
|
41
|
-
// so a mixed table never surfaces duplicate fragments.
|
|
42
|
-
const cmpVer = (a, b) => {
|
|
43
|
-
const pa = String(a).replace(/[-+]/g, ".").split(".");
|
|
44
|
-
const pb = String(b).replace(/[-+]/g, ".").split(".");
|
|
45
|
-
const len = Math.max(pa.length, pb.length);
|
|
46
|
-
for (let i = 0; i < len; i++) {
|
|
47
|
-
const sa = pa[i] ?? "", sb = pb[i] ?? "";
|
|
48
|
-
const na = Number(sa), nb = Number(sb);
|
|
49
|
-
const bothNum = sa !== "" && sb !== "" && !Number.isNaN(na) && !Number.isNaN(nb);
|
|
50
|
-
if (bothNum) { if (na !== nb) return na > nb ? 1 : -1; }
|
|
51
|
-
else if (sa !== sb) return sa > sb ? 1 : -1;
|
|
52
|
-
}
|
|
53
|
-
return 0;
|
|
54
|
-
};
|
|
55
|
-
const latestByName = new Map();
|
|
56
|
-
for (const item of items) {
|
|
57
|
-
if (!item || !item.pluginId || !item.remoteEntryUrl) continue;
|
|
58
|
-
const name = String(item.pluginId).split("@")[0];
|
|
59
|
-
const version = String(item.pluginId).split("@")[1] ?? "";
|
|
60
|
-
const prev = latestByName.get(name);
|
|
61
|
-
if (!prev || cmpVer(version, prev.version) > 0) {
|
|
62
|
-
latestByName.set(name, {
|
|
63
|
-
version,
|
|
64
|
-
entry: {
|
|
65
|
-
pluginId: name,
|
|
66
|
-
remoteEntryUrl: item.remoteEntryUrl,
|
|
67
|
-
panels: item.panels || [],
|
|
68
|
-
pages: item.pages || [],
|
|
69
|
-
registeredAt: item.registeredAt || "",
|
|
70
|
-
updatedAt: item.updatedAt || "",
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return [...latestByName.values()].map(v => v.entry);
|
|
76
|
-
}
|
|
77
|
-
`
|
|
78
|
-
|
|
79
11
|
let resolverCode = `
|
|
80
12
|
import { util } from '@aws-appsync/utils';
|
|
81
13
|
export function request(ctx) {
|
|
@@ -143,18 +75,16 @@ let make = (
|
|
|
143
75
|
)
|
|
144
76
|
})
|
|
145
77
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
78
|
+
// Bundle reventless-aws (the compiled `_Ops` handler lives inside it) and
|
|
79
|
+
// re-export its `handler`; buildCodeArchive also ships the ESM resolve-hook so
|
|
80
|
+
// the handler's bare @aws-sdk/* specifiers resolve from the managed runtime.
|
|
81
|
+
let packageDirs = Dict.fromArray([
|
|
82
|
+
("@reventlessdev/reventless-aws", Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-aws")),
|
|
83
|
+
])
|
|
84
|
+
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
85
|
+
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs",
|
|
86
|
+
~packageDirs,
|
|
151
87
|
)
|
|
152
|
-
// ESM self-containment: the handler imports @aws-sdk/* bare specifiers the
|
|
153
|
-
// nodejs22.x runtime provides only under /var/runtime — unreachable from
|
|
154
|
-
// /var/task ESM without the resolver hook. Ship the loader + set its env vars.
|
|
155
|
-
let loaderHash = Util_Bundle.addEsmLoaderAssets(archiveContents)
|
|
156
|
-
let code = Pulumi.Archive.assetArchive(archiveContents)
|
|
157
|
-
let sourceCodeHash = Util_Bundle.hashString(handlerCodeStub ++ "\n---\n" ++ loaderHash)
|
|
158
88
|
|
|
159
89
|
let layers =
|
|
160
90
|
Lambda.reventlessLayerArn
|
|
@@ -13,75 +13,6 @@ import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
|
13
13
|
import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
|
|
14
14
|
import * as AppSync_Resolver_Native$ReventlessAws from "./AppSync_Resolver_Native.res.mjs";
|
|
15
15
|
|
|
16
|
-
function makeHandlerCode(param) {
|
|
17
|
-
return `
|
|
18
|
-
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
19
|
-
import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
|
|
20
|
-
|
|
21
|
-
const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
|
|
22
|
-
const TABLE = process.env.UI_FRAGMENT_RM_TABLE;
|
|
23
|
-
|
|
24
|
-
export async function handler() {
|
|
25
|
-
if (!TABLE) {
|
|
26
|
-
console.error("Platform_UIFragments: UI_FRAGMENT_RM_TABLE env var not set");
|
|
27
|
-
return [];
|
|
28
|
-
}
|
|
29
|
-
const items = [];
|
|
30
|
-
let exclusiveStartKey;
|
|
31
|
-
do {
|
|
32
|
-
const out = await client.send(new ScanCommand({
|
|
33
|
-
TableName: TABLE,
|
|
34
|
-
Limit: 1000,
|
|
35
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
36
|
-
}));
|
|
37
|
-
if (out.Items) items.push(...out.Items);
|
|
38
|
-
exclusiveStartKey = out.LastEvaluatedKey;
|
|
39
|
-
} while (exclusiveStartKey);
|
|
40
|
-
|
|
41
|
-
// Platform invariant: one version per plugin at a time, so the UI sees just
|
|
42
|
-
// the bare plugin name (mirrors ReventlessCore.Plugin.name). The registry is
|
|
43
|
-
// keyed by bare plugin name (a no-op for the split below), but rows persisted
|
|
44
|
-
// by the pre-slice registry were keyed name@version — keep the collapse to the
|
|
45
|
-
// highest version per plugin name (mirrors ReventlessCore.Plugin.compareVersions)
|
|
46
|
-
// so a mixed table never surfaces duplicate fragments.
|
|
47
|
-
const cmpVer = (a, b) => {
|
|
48
|
-
const pa = String(a).replace(/[-+]/g, ".").split(".");
|
|
49
|
-
const pb = String(b).replace(/[-+]/g, ".").split(".");
|
|
50
|
-
const len = Math.max(pa.length, pb.length);
|
|
51
|
-
for (let i = 0; i < len; i++) {
|
|
52
|
-
const sa = pa[i] ?? "", sb = pb[i] ?? "";
|
|
53
|
-
const na = Number(sa), nb = Number(sb);
|
|
54
|
-
const bothNum = sa !== "" && sb !== "" && !Number.isNaN(na) && !Number.isNaN(nb);
|
|
55
|
-
if (bothNum) { if (na !== nb) return na > nb ? 1 : -1; }
|
|
56
|
-
else if (sa !== sb) return sa > sb ? 1 : -1;
|
|
57
|
-
}
|
|
58
|
-
return 0;
|
|
59
|
-
};
|
|
60
|
-
const latestByName = new Map();
|
|
61
|
-
for (const item of items) {
|
|
62
|
-
if (!item || !item.pluginId || !item.remoteEntryUrl) continue;
|
|
63
|
-
const name = String(item.pluginId).split("@")[0];
|
|
64
|
-
const version = String(item.pluginId).split("@")[1] ?? "";
|
|
65
|
-
const prev = latestByName.get(name);
|
|
66
|
-
if (!prev || cmpVer(version, prev.version) > 0) {
|
|
67
|
-
latestByName.set(name, {
|
|
68
|
-
version,
|
|
69
|
-
entry: {
|
|
70
|
-
pluginId: name,
|
|
71
|
-
remoteEntryUrl: item.remoteEntryUrl,
|
|
72
|
-
panels: item.panels || [],
|
|
73
|
-
pages: item.pages || [],
|
|
74
|
-
registeredAt: item.registeredAt || "",
|
|
75
|
-
updatedAt: item.updatedAt || "",
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return [...latestByName.values()].map(v => v.entry);
|
|
81
|
-
}
|
|
82
|
-
`;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
16
|
let resolverCode = `
|
|
86
17
|
import { util } from '@aws-appsync/utils';
|
|
87
18
|
export function request(ctx) {
|
|
@@ -116,82 +47,16 @@ function make(api, uiFragmentRegistryTableName, schemaReady, opts) {
|
|
|
116
47
|
role: lambdaRole.id
|
|
117
48
|
}, opts$1);
|
|
118
49
|
});
|
|
119
|
-
let
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
|
|
125
|
-
const TABLE = process.env.UI_FRAGMENT_RM_TABLE;
|
|
126
|
-
|
|
127
|
-
export async function handler() {
|
|
128
|
-
if (!TABLE) {
|
|
129
|
-
console.error("Platform_UIFragments: UI_FRAGMENT_RM_TABLE env var not set");
|
|
130
|
-
return [];
|
|
131
|
-
}
|
|
132
|
-
const items = [];
|
|
133
|
-
let exclusiveStartKey;
|
|
134
|
-
do {
|
|
135
|
-
const out = await client.send(new ScanCommand({
|
|
136
|
-
TableName: TABLE,
|
|
137
|
-
Limit: 1000,
|
|
138
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
139
|
-
}));
|
|
140
|
-
if (out.Items) items.push(...out.Items);
|
|
141
|
-
exclusiveStartKey = out.LastEvaluatedKey;
|
|
142
|
-
} while (exclusiveStartKey);
|
|
143
|
-
|
|
144
|
-
// Platform invariant: one version per plugin at a time, so the UI sees just
|
|
145
|
-
// the bare plugin name (mirrors ReventlessCore.Plugin.name). The registry is
|
|
146
|
-
// keyed by bare plugin name (a no-op for the split below), but rows persisted
|
|
147
|
-
// by the pre-slice registry were keyed name@version — keep the collapse to the
|
|
148
|
-
// highest version per plugin name (mirrors ReventlessCore.Plugin.compareVersions)
|
|
149
|
-
// so a mixed table never surfaces duplicate fragments.
|
|
150
|
-
const cmpVer = (a, b) => {
|
|
151
|
-
const pa = String(a).replace(/[-+]/g, ".").split(".");
|
|
152
|
-
const pb = String(b).replace(/[-+]/g, ".").split(".");
|
|
153
|
-
const len = Math.max(pa.length, pb.length);
|
|
154
|
-
for (let i = 0; i < len; i++) {
|
|
155
|
-
const sa = pa[i] ?? "", sb = pb[i] ?? "";
|
|
156
|
-
const na = Number(sa), nb = Number(sb);
|
|
157
|
-
const bothNum = sa !== "" && sb !== "" && !Number.isNaN(na) && !Number.isNaN(nb);
|
|
158
|
-
if (bothNum) { if (na !== nb) return na > nb ? 1 : -1; }
|
|
159
|
-
else if (sa !== sb) return sa > sb ? 1 : -1;
|
|
160
|
-
}
|
|
161
|
-
return 0;
|
|
162
|
-
};
|
|
163
|
-
const latestByName = new Map();
|
|
164
|
-
for (const item of items) {
|
|
165
|
-
if (!item || !item.pluginId || !item.remoteEntryUrl) continue;
|
|
166
|
-
const name = String(item.pluginId).split("@")[0];
|
|
167
|
-
const version = String(item.pluginId).split("@")[1] ?? "";
|
|
168
|
-
const prev = latestByName.get(name);
|
|
169
|
-
if (!prev || cmpVer(version, prev.version) > 0) {
|
|
170
|
-
latestByName.set(name, {
|
|
171
|
-
version,
|
|
172
|
-
entry: {
|
|
173
|
-
pluginId: name,
|
|
174
|
-
remoteEntryUrl: item.remoteEntryUrl,
|
|
175
|
-
panels: item.panels || [],
|
|
176
|
-
pages: item.pages || [],
|
|
177
|
-
registeredAt: item.registeredAt || "",
|
|
178
|
-
updatedAt: item.updatedAt || "",
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return [...latestByName.values()].map(v => v.entry);
|
|
184
|
-
}
|
|
185
|
-
`;
|
|
186
|
-
archiveContents["index.mjs"] = new (Pulumi.asset.StringAsset)(handlerCodeStub);
|
|
187
|
-
let loaderHash = Util_Bundle$ReventlessAws.addEsmLoaderAssets(archiveContents);
|
|
188
|
-
let code = new (Pulumi.asset.AssetArchive)(archiveContents);
|
|
189
|
-
let sourceCodeHash = Util_Bundle$ReventlessAws.hashString(handlerCodeStub + "\n---\n" + loaderHash);
|
|
50
|
+
let packageDirs = Object.fromEntries([[
|
|
51
|
+
"@reventlessdev/reventless-aws",
|
|
52
|
+
Util_Bundle$ReventlessAws.resolvePackageRoot("@reventlessdev/reventless-aws")
|
|
53
|
+
]]);
|
|
54
|
+
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs", packageDirs, undefined);
|
|
190
55
|
let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
|
|
191
56
|
let lambda = new (Aws.lambda.Function)(name + "Lambda", {
|
|
192
57
|
handler: "index.handler",
|
|
193
58
|
runtime: "nodejs22.x",
|
|
194
|
-
code: code,
|
|
59
|
+
code: match.code,
|
|
195
60
|
role: lambdaRole.arn,
|
|
196
61
|
memorySize: 512,
|
|
197
62
|
timeout: 30,
|
|
@@ -217,7 +82,7 @@ export async function handler() {
|
|
|
217
82
|
]
|
|
218
83
|
])
|
|
219
84
|
},
|
|
220
|
-
sourceCodeHash: sourceCodeHash
|
|
85
|
+
sourceCodeHash: match.sourceCodeHash
|
|
221
86
|
}, opts$1);
|
|
222
87
|
let dataSourceRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "DataSource", Pulumi.output(AWS$ReventlessAws.AppSync.principal), AWS_Tags$ReventlessAws.make(name + "DataSource", "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
|
|
223
88
|
Pulumi.all([
|
|
@@ -246,7 +111,6 @@ export async function handler() {
|
|
|
246
111
|
}
|
|
247
112
|
|
|
248
113
|
export {
|
|
249
|
-
makeHandlerCode,
|
|
250
114
|
resolverCode,
|
|
251
115
|
make,
|
|
252
116
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Runtime handler for the `Platform_UIFragments` admin GraphQL resolver —
|
|
2
|
+
// compiled, type-checked ReScript (replaces the inline JS `makeHandlerCode`
|
|
3
|
+
// string in Platform_UIFragments_Lambda.res). Runtime-pure (see
|
|
4
|
+
// Platform_AdminScan_Ops); the deploy-time module bundles this into an
|
|
5
|
+
// EntryPoint archive and re-exports `handler`.
|
|
6
|
+
//
|
|
7
|
+
// Scans the UiFragments StateViewSlice table (one item per plugin with a
|
|
8
|
+
// registered UI-fragment manifest), collapses to the highest version per plugin
|
|
9
|
+
// name (platform invariant: one version per plugin), and returns the entries.
|
|
10
|
+
// The persisted state is sury-encoded with the same shape the in-memory
|
|
11
|
+
// adapter's Platform_UIFragmentsApi.encodeUIFragmentEntry produces, so rows are
|
|
12
|
+
// returned as-is (bar the name/version collapse).
|
|
13
|
+
|
|
14
|
+
@val @scope("process") external processEnv: dict<string> = "env"
|
|
15
|
+
|
|
16
|
+
let str = (item: dict<JSON.t>, key: string): option<string> =>
|
|
17
|
+
item->Dict.get(key)->Option.flatMap(JSON.Decode.string)
|
|
18
|
+
|
|
19
|
+
// Build one entry, dropping a row missing pluginId or remoteEntryUrl (mirrors
|
|
20
|
+
// the former `!item.pluginId || !item.remoteEntryUrl` guard). `name` is the bare
|
|
21
|
+
// plugin name; panels/pages/timestamps default like the JS `|| []` / `|| ""`.
|
|
22
|
+
let toEntry = (item: dict<JSON.t>, ~name: string): option<JSON.t> =>
|
|
23
|
+
switch (item->str("pluginId"), item->str("remoteEntryUrl")) {
|
|
24
|
+
| (Some(_), Some(remoteEntryUrl)) if remoteEntryUrl != "" =>
|
|
25
|
+
Some(
|
|
26
|
+
JSON.Encode.object(
|
|
27
|
+
Dict.fromArray([
|
|
28
|
+
("pluginId", JSON.Encode.string(name)),
|
|
29
|
+
("remoteEntryUrl", JSON.Encode.string(remoteEntryUrl)),
|
|
30
|
+
("panels", item->Dict.get("panels")->Option.getOr(JSON.Encode.array([]))),
|
|
31
|
+
("pages", item->Dict.get("pages")->Option.getOr(JSON.Encode.array([]))),
|
|
32
|
+
("registeredAt", item->Dict.get("registeredAt")->Option.getOr(JSON.Encode.string(""))),
|
|
33
|
+
("updatedAt", item->Dict.get("updatedAt")->Option.getOr(JSON.Encode.string(""))),
|
|
34
|
+
]),
|
|
35
|
+
),
|
|
36
|
+
)
|
|
37
|
+
| _ => None
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let handler = async (_event: JSON.t): array<JSON.t> =>
|
|
41
|
+
switch processEnv->Dict.get("UI_FRAGMENT_RM_TABLE") {
|
|
42
|
+
| None | Some("") =>
|
|
43
|
+
Console.error("Platform_UIFragments: UI_FRAGMENT_RM_TABLE env var not set")
|
|
44
|
+
[]
|
|
45
|
+
| Some(table) =>
|
|
46
|
+
let items = await Platform_AdminScan_Ops.scanAll(~tableName=table)
|
|
47
|
+
Platform_AdminScan_Ops.latestByName(items, ~nameVersionOf=item => item->str("pluginId"), ~toEntry)
|
|
48
|
+
}
|