@reventlessdev/reventless-aws 3.0.0-alpha.183 → 3.0.0-alpha.184
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
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.184 (2026-07-08)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **reventless-aws:** exclude internal rows from Platform_Plugins connection scan ([df14af2](https://github.com/ReventlessDev/reventless-core/commit/df14af2cbd70d49062f2afc03418cdbf18d151d6))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.183 (2026-07-08)
|
|
7
14
|
|
|
8
15
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.184",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.20",
|
|
12
12
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.25",
|
|
13
13
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.6",
|
|
14
|
-
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.
|
|
14
|
+
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.47",
|
|
15
15
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
|
|
16
16
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.14",
|
|
17
17
|
"@reventlessdev/reventless-core": "3.0.0-alpha.145",
|
|
@@ -57,6 +57,17 @@ export function response(ctx) {
|
|
|
57
57
|
}
|
|
58
58
|
`->Pulumi.Input.make
|
|
59
59
|
|
|
60
|
+
/** The discriminator attribute whose absence marks an internal bookkeeping row for a
|
|
61
|
+
read model whose physical DynamoDB table co-hosts rows written outside the projection.
|
|
62
|
+
Only the Plugins admin RM has this: its table also holds `deploy-schema:*` /
|
|
63
|
+
`plugin-info:*` / `deploy-schema-hash:*` rows (Platform.res preResolversSchemaHook)
|
|
64
|
+
that carry no `name`. Returning `Some(attr)` makes the Connection Scan emit an
|
|
65
|
+
`attribute_exists(#attr)` filter so those rows never reach the non-null GraphQL schema.
|
|
66
|
+
`name` is the already-capitalized read-model name (spec name, e.g. "Plugins").
|
|
67
|
+
See docs/plans/platform-plugins-admin-connection-null-rows.md. */
|
|
68
|
+
let internalRowRequiredAttr = (name: string): option<string> =>
|
|
69
|
+
name == "Plugins" ? Some("name") : None
|
|
70
|
+
|
|
60
71
|
let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
61
72
|
~name: string,
|
|
62
73
|
~api: api,
|
|
@@ -217,6 +228,15 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
217
228
|
)->Array.forEach(msg => log.warn(~comp="QueryDbResolvers_AppSync", msg))
|
|
218
229
|
| None => ()
|
|
219
230
|
}
|
|
231
|
+
// The Plugins admin RM's DynamoDB table co-hosts deploy-time infra rows
|
|
232
|
+
// (`deploy-schema:<name>`, `plugin-info:<name>`, `deploy-schema-hash:<apiId>`)
|
|
233
|
+
// written directly by the platform (Platform.res preResolversSchemaHook), not by
|
|
234
|
+
// the projection. Those rows carry no `name` attribute, so an unfiltered Scan
|
|
235
|
+
// resolves `name: String!` to null → non-null violation that nulls the entire
|
|
236
|
+
// Platform_PluginConnection. Exclude them with an `attribute_exists(#name)` filter
|
|
237
|
+
// (prefix-agnostic: real plugin rows always carry `name`, internal rows never do).
|
|
238
|
+
// See docs/plans/platform-plugins-admin-connection-null-rows.md.
|
|
239
|
+
let requireAttribute = internalRowRequiredAttr(name)
|
|
220
240
|
let resolverAll = makeQueryResolver(
|
|
221
241
|
~resolverName=fieldNameForAll->String.capitalize,
|
|
222
242
|
~field=fieldNameForAll->Pulumi.Input.make,
|
|
@@ -226,6 +246,7 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
226
246
|
~filterFields=filterFieldNames,
|
|
227
247
|
~rangeFields=rangeFieldNames,
|
|
228
248
|
~sortFields=sortFieldNames,
|
|
249
|
+
~requireAttribute?,
|
|
229
250
|
)
|
|
230
251
|
} else {
|
|
231
252
|
Resolver.Functions.listAllItems
|
|
@@ -58,6 +58,12 @@ export function response(ctx) {
|
|
|
58
58
|
`;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
function internalRowRequiredAttr(name) {
|
|
62
|
+
if (name === "Plugins") {
|
|
63
|
+
return "name";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
61
67
|
function make(name, api, apiRole, dataSourceName, indexes, subIdField, idResolverConfigs, idsResolverConfigs, param, opts) {
|
|
62
68
|
let name$1 = Stdlib_String.capitalize(name);
|
|
63
69
|
let registryEntry = Plugin_Helpers$ReventlessCore.queryFieldNamesRegistry[name$1];
|
|
@@ -100,7 +106,8 @@ function make(name, api, apiRole, dataSourceName, indexes, subIdField, idResolve
|
|
|
100
106
|
).concat(Stdlib_Array.filterMap(indexes, param => param.subIdField));
|
|
101
107
|
GraphQL_FragmentGenerator$ReventlessCore.validateScanSortAlignment(stateSchemaOpt, name$1, knownSortFields).forEach(msg => log.warn("QueryDbResolvers_AppSync", undefined, msg));
|
|
102
108
|
}
|
|
103
|
-
let
|
|
109
|
+
let requireAttribute = internalRowRequiredAttr(name$1);
|
|
110
|
+
let resolverAll = makeQueryResolver(Stdlib_String.capitalize(fieldNameForAll), fieldNameForAll, connectionSpec ? AppSync_Resolver_Functions$PulumiAws.listAllItemsConnection(labelField, filterFieldNames, rangeFieldNames, sortFieldNames, requireAttribute) : AppSync_Resolver_Functions$PulumiAws.listAllItems);
|
|
104
111
|
let resolversByIndex = indexes.map(indexConfig => {
|
|
105
112
|
let index = indexConfig.index;
|
|
106
113
|
let stripLeadingBy = s => {
|
|
@@ -238,6 +245,7 @@ export {
|
|
|
238
245
|
log,
|
|
239
246
|
queryInterceptorConfig,
|
|
240
247
|
interceptorCode,
|
|
248
|
+
internalRowRequiredAttr,
|
|
241
249
|
make,
|
|
242
250
|
}
|
|
243
251
|
/* log Not a pure module */
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// Regression guard for docs/plans/platform-plugins-admin-connection-null-rows.md:
|
|
4
|
+
// the Plugins admin RM shares its DynamoDB table with internal bookkeeping rows
|
|
5
|
+
// (`deploy-schema:*`, `plugin-info:*`, `deploy-schema-hash:*`) that carry no `name`.
|
|
6
|
+
// The auto-generated Connection Scan must exclude them, or `name: String!` resolves
|
|
7
|
+
// to null and nulls the entire Platform_PluginConnection ("No data" on the page).
|
|
8
|
+
|
|
9
|
+
// `Pulumi.Input.make` is `%identity`, so a generated resolver's `Pulumi.Input.t<string>`
|
|
10
|
+
// IS the underlying JS string — recover it with Obj.magic for assertion.
|
|
11
|
+
let codeOf = (input: Pulumi.Input.t<string>): string => Obj.magic(input)
|
|
12
|
+
|
|
13
|
+
describe("QueryDbResolvers_AppSync.internalRowRequiredAttr", () => {
|
|
14
|
+
testSync("Plugins RM requires the `name` discriminator", () => {
|
|
15
|
+
expect(QueryDbResolvers_AppSync.internalRowRequiredAttr("Plugins"))->toEqual(Some("name"))
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
testSync("ordinary read models require nothing (unchanged behaviour)", () => {
|
|
19
|
+
expect(QueryDbResolvers_AppSync.internalRowRequiredAttr("Products"))->toEqual(None)
|
|
20
|
+
expect(QueryDbResolvers_AppSync.internalRowRequiredAttr("Orders"))->toEqual(None)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe("AppSync_Resolver_Retrying.Functions.listAllItemsConnection", () => {
|
|
25
|
+
testSync("with ~requireAttribute emits an attribute_exists filter that excludes internal rows", () => {
|
|
26
|
+
let code =
|
|
27
|
+
AppSync_Resolver_Retrying.Functions.listAllItemsConnection(
|
|
28
|
+
~labelField="name",
|
|
29
|
+
~requireAttribute="name",
|
|
30
|
+
)->codeOf
|
|
31
|
+
expect(code->String.includes("attribute_exists(#name)"))->toBe(true)
|
|
32
|
+
expect(code->String.includes("names['#name'] = 'name'"))->toBe(true)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
testSync("without ~requireAttribute emits no attribute_exists filter (default read models unchanged)", () => {
|
|
36
|
+
let code =
|
|
37
|
+
AppSync_Resolver_Retrying.Functions.listAllItemsConnection(~labelField="name")->codeOf
|
|
38
|
+
expect(code->String.includes("attribute_exists"))->toBe(false)
|
|
39
|
+
})
|
|
40
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as AppSync_Resolver_Functions$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/AppSync/AppSync_Resolver_Functions.res.mjs";
|
|
4
|
+
import * as QueryDbResolvers_AppSync$ReventlessAws from "../src/adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs";
|
|
5
|
+
|
|
6
|
+
function codeOf(input) {
|
|
7
|
+
return input;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
globalThis.describe("QueryDbResolvers_AppSync.internalRowRequiredAttr", () => {
|
|
11
|
+
globalThis.test("Plugins RM requires the `name` discriminator", () => {
|
|
12
|
+
globalThis.expect(QueryDbResolvers_AppSync$ReventlessAws.internalRowRequiredAttr("Plugins")).toEqual("name");
|
|
13
|
+
});
|
|
14
|
+
globalThis.test("ordinary read models require nothing (unchanged behaviour)", () => {
|
|
15
|
+
globalThis.expect(QueryDbResolvers_AppSync$ReventlessAws.internalRowRequiredAttr("Products")).toEqual(undefined);
|
|
16
|
+
globalThis.expect(QueryDbResolvers_AppSync$ReventlessAws.internalRowRequiredAttr("Orders")).toEqual(undefined);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
globalThis.describe("AppSync_Resolver_Retrying.Functions.listAllItemsConnection", () => {
|
|
21
|
+
globalThis.test("with ~requireAttribute emits an attribute_exists filter that excludes internal rows", () => {
|
|
22
|
+
let code = AppSync_Resolver_Functions$PulumiAws.listAllItemsConnection("name", undefined, undefined, undefined, "name");
|
|
23
|
+
globalThis.expect(code.includes("attribute_exists(#name)")).toBe(true);
|
|
24
|
+
globalThis.expect(code.includes("names['#name'] = 'name'")).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
globalThis.test("without ~requireAttribute emits no attribute_exists filter (default read models unchanged)", () => {
|
|
27
|
+
let code = AppSync_Resolver_Functions$PulumiAws.listAllItemsConnection("name", undefined, undefined, undefined, undefined);
|
|
28
|
+
globalThis.expect(code.includes("attribute_exists")).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
codeOf,
|
|
34
|
+
}
|
|
35
|
+
/* Not a pure module */
|