@suss/contract-appsync 0.36.0 → 0.37.1
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 +6 -2
- package/dist/index.cjs +136 -47
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +141 -48
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -11,7 +11,11 @@ This package builds suss `BehavioralSummary[]` from an [AWS AppSync](https://doc
|
|
|
11
11
|
- **Raw resources**: `AWS::AppSync::GraphQLApi`, `AWS::AppSync::GraphQLSchema`, `AWS::AppSync::Resolver`, `AWS::AppSync::FunctionConfiguration` and `AWS::AppSync::DataSource`.
|
|
12
12
|
- **The SAM shorthand** `AWS::Serverless::GraphQLApi`. Its inline `SchemaInline` / `SchemaUri`, `DataSources`, `Functions` and `Resolvers` blocks are converted into the same model before any summary is built. Generated logical ids are prefixed with the API's logical id, so two GraphQL APIs in one template stay apart.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
A resolver or a pipeline function can point at its data source by logical id or by the data source's `Name`. A name is looked up among the data sources of the same API, so two APIs can each have a data source called `orders`.
|
|
15
|
+
|
|
16
|
+
The reader also reads every template the file embeds through an `AWS::CloudFormation::Stack` or `AWS::Serverless::Application`, and treats them as one API surface. A child's resources get the stack path in front of their logical id, as in `DirectoryStack/DirectoryFunction`, the same id the aws-lambda pack gives that function. When a parent passes a resource to a child as a parameter, such as `ApiId: !GetAtt Api.ApiId`, the child's `!Ref ApiId` reads as that resource, so a child's resolvers attach to the API and schema the root declares. A child it cannot open is reported by name.
|
|
17
|
+
|
|
18
|
+
The SDL comes from the inline `Definition` / `SchemaInline` property, or from a local file when `DefinitionS3Location` / `SchemaUri` is a path, resolved against the directory of the template that declares it. A static reader cannot fetch an `s3://` or `http(s)://` URI, or a location the template computes with an intrinsic. Both cases are recorded under `metadata.appsync.schemaSource` with a `status` of `"unresolved"` and a reason, so a schema the reader could not open is always reported.
|
|
15
19
|
|
|
16
20
|
## What it produces
|
|
17
21
|
|
|
@@ -36,7 +40,7 @@ Data source types are normalized from `AWS_LAMBDA`, `AMAZON_DYNAMODB`, `AMAZON_E
|
|
|
36
40
|
- **Dynamic intrinsics.** A resolver whose `TypeName` or `FieldName` comes from a `!Ref` or an `Fn::Join` is skipped, so the reader never guesses. A pipeline whose `Functions` array cannot be resolved statically still reports `kind: "PIPELINE"`, with an empty chain.
|
|
37
41
|
- **Remote schemas.** Nothing is fetched over the network.
|
|
38
42
|
- **Authorization.** The API's `AuthenticationType` is recorded on each resolver, and does not add transitions.
|
|
39
|
-
- **
|
|
43
|
+
- **Stack outputs.** A parent that reads a child's resource through `!GetAtt Child.Outputs.X` points at nothing the reader follows.
|
|
40
44
|
|
|
41
45
|
## Worked example
|
|
42
46
|
|
package/dist/index.cjs
CHANGED
|
@@ -36,12 +36,22 @@ __export(index_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
38
38
|
var import_files = require("@suss/behavioral-ir/files");
|
|
39
|
-
var
|
|
39
|
+
var import_manifest_aws4 = require("@suss/manifest-aws");
|
|
40
|
+
|
|
41
|
+
// src/cfn.ts
|
|
42
|
+
var import_manifest_aws3 = require("@suss/manifest-aws");
|
|
40
43
|
|
|
41
44
|
// src/refs.ts
|
|
42
45
|
var import_manifest_aws = require("@suss/manifest-aws");
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
var OWN_DOCUMENT = {
|
|
47
|
+
own: (logicalId) => logicalId,
|
|
48
|
+
ref: (value) => (0, import_manifest_aws.refTarget)(value)
|
|
49
|
+
};
|
|
50
|
+
function documentRefsOf(document) {
|
|
51
|
+
return {
|
|
52
|
+
own: (logicalId) => (0, import_manifest_aws.qualifiedLogicalId)(document.stackPath, logicalId),
|
|
53
|
+
ref: (value) => (0, import_manifest_aws.documentRef)(document, value)
|
|
54
|
+
};
|
|
45
55
|
}
|
|
46
56
|
function stringField(value) {
|
|
47
57
|
return typeof value === "string" ? value : null;
|
|
@@ -54,6 +64,7 @@ function asRecord(value) {
|
|
|
54
64
|
}
|
|
55
65
|
|
|
56
66
|
// src/sam.ts
|
|
67
|
+
var import_manifest_aws2 = require("@suss/manifest-aws");
|
|
57
68
|
var RESOLVER_ROOT_TYPES = ["Query", "Mutation", "Subscription"];
|
|
58
69
|
var DATA_SOURCE_CATEGORIES = {
|
|
59
70
|
Lambda: "lambda",
|
|
@@ -68,7 +79,7 @@ var DATA_SOURCE_CATEGORIES = {
|
|
|
68
79
|
RelationalDatabases: "relational",
|
|
69
80
|
None: "none"
|
|
70
81
|
};
|
|
71
|
-
function readServerlessGraphQLApis(resources) {
|
|
82
|
+
function readServerlessGraphQLApis(resources, refs) {
|
|
72
83
|
const out = {
|
|
73
84
|
apis: [],
|
|
74
85
|
resolvers: [],
|
|
@@ -79,18 +90,20 @@ function readServerlessGraphQLApis(resources) {
|
|
|
79
90
|
if (resource?.Type !== "AWS::Serverless::GraphQLApi") {
|
|
80
91
|
continue;
|
|
81
92
|
}
|
|
82
|
-
readOne(logicalId, resource.Properties ?? {}, out);
|
|
93
|
+
readOne(refs.own(logicalId), resource.Properties ?? {}, out, refs);
|
|
83
94
|
}
|
|
84
95
|
return out;
|
|
85
96
|
}
|
|
86
|
-
function readOne(apiLogicalId, props, out) {
|
|
97
|
+
function readOne(apiLogicalId, props, out, refs) {
|
|
87
98
|
out.apis.push({
|
|
88
99
|
logicalId: apiLogicalId,
|
|
89
100
|
name: stringField(props.Name),
|
|
90
101
|
schemaSource: readSchemaSource(props),
|
|
91
102
|
authenticationType: readAuthType(props.Auth)
|
|
92
103
|
});
|
|
93
|
-
out.dataSources.push(
|
|
104
|
+
out.dataSources.push(
|
|
105
|
+
...readDataSources(apiLogicalId, props.DataSources, refs)
|
|
106
|
+
);
|
|
94
107
|
out.functions.push(...readFunctions(apiLogicalId, props.Functions));
|
|
95
108
|
out.resolvers.push(...readResolvers(apiLogicalId, props.Resolvers));
|
|
96
109
|
}
|
|
@@ -112,7 +125,7 @@ function readAuthType(auth) {
|
|
|
112
125
|
const record = asRecord(auth);
|
|
113
126
|
return record === null ? null : stringField(record.Type);
|
|
114
127
|
}
|
|
115
|
-
function readDataSources(apiLogicalId, raw) {
|
|
128
|
+
function readDataSources(apiLogicalId, raw, refs) {
|
|
116
129
|
const dataSources = asRecord(raw);
|
|
117
130
|
if (dataSources === null) {
|
|
118
131
|
return [];
|
|
@@ -129,8 +142,9 @@ function readDataSources(apiLogicalId, raw) {
|
|
|
129
142
|
out.push({
|
|
130
143
|
logicalId: dataSourceId(apiLogicalId, name),
|
|
131
144
|
apiLogicalId,
|
|
145
|
+
name,
|
|
132
146
|
type,
|
|
133
|
-
lambdaFunctionLogicalId: type === "lambda" && def !== null ?
|
|
147
|
+
lambdaFunctionLogicalId: type === "lambda" && def !== null ? refs.ref(def.FunctionArn) : null
|
|
134
148
|
});
|
|
135
149
|
}
|
|
136
150
|
}
|
|
@@ -149,6 +163,7 @@ function readFunctions(apiLogicalId, raw) {
|
|
|
149
163
|
apiLogicalId,
|
|
150
164
|
name,
|
|
151
165
|
dataSourceLogicalId: referencedDataSourceId(apiLogicalId, def.DataSource),
|
|
166
|
+
dataSourceWritten: null,
|
|
152
167
|
codeUri: stringField(def.CodeUri),
|
|
153
168
|
runtime: readRuntime(def.Runtime)
|
|
154
169
|
});
|
|
@@ -182,6 +197,7 @@ function buildResolver(apiLogicalId, typeName, fieldName, def) {
|
|
|
182
197
|
typeName,
|
|
183
198
|
fieldName,
|
|
184
199
|
dataSourceLogicalId: isPipeline ? null : referencedDataSourceId(apiLogicalId, def.DataSource),
|
|
200
|
+
dataSourceWritten: null,
|
|
185
201
|
kind: isPipeline ? "PIPELINE" : "UNIT",
|
|
186
202
|
pipelineFunctionLogicalIds: pipeline,
|
|
187
203
|
codeUri: stringField(def.CodeUri),
|
|
@@ -194,7 +210,7 @@ function readPipeline(apiLogicalId, raw) {
|
|
|
194
210
|
}
|
|
195
211
|
const out = [];
|
|
196
212
|
for (const entry of raw) {
|
|
197
|
-
const name = stringField(entry) ??
|
|
213
|
+
const name = stringField(entry) ?? (0, import_manifest_aws2.refTarget)(entry);
|
|
198
214
|
if (name !== null) {
|
|
199
215
|
out.push(functionId(apiLogicalId, name));
|
|
200
216
|
}
|
|
@@ -217,15 +233,15 @@ function functionId(apiLogicalId, name) {
|
|
|
217
233
|
}
|
|
218
234
|
|
|
219
235
|
// src/cfn.ts
|
|
220
|
-
function readAppSyncFromCfn(template) {
|
|
236
|
+
function readAppSyncFromCfn(template, refs = OWN_DOCUMENT) {
|
|
221
237
|
const resources = template.Resources ?? {};
|
|
222
238
|
const raw = {
|
|
223
|
-
apis: collectApis(resources),
|
|
224
|
-
resolvers: collectResolvers(resources),
|
|
225
|
-
functions: collectFunctions(resources),
|
|
226
|
-
dataSources: collectDataSources(resources)
|
|
239
|
+
apis: collectApis(resources, refs),
|
|
240
|
+
resolvers: collectResolvers(resources, refs),
|
|
241
|
+
functions: collectFunctions(resources, refs),
|
|
242
|
+
dataSources: collectDataSources(resources, refs)
|
|
227
243
|
};
|
|
228
|
-
const sam = readServerlessGraphQLApis(resources);
|
|
244
|
+
const sam = readServerlessGraphQLApis(resources, refs);
|
|
229
245
|
return {
|
|
230
246
|
apis: [...raw.apis, ...sam.apis],
|
|
231
247
|
resolvers: [...raw.resolvers, ...sam.resolvers],
|
|
@@ -233,31 +249,63 @@ function readAppSyncFromCfn(template) {
|
|
|
233
249
|
dataSources: [...raw.dataSources, ...sam.dataSources]
|
|
234
250
|
};
|
|
235
251
|
}
|
|
236
|
-
function
|
|
252
|
+
function mergeConfigs(configs) {
|
|
253
|
+
const merged = {
|
|
254
|
+
apis: configs.flatMap((c) => c.apis),
|
|
255
|
+
resolvers: configs.flatMap((c) => c.resolvers),
|
|
256
|
+
functions: configs.flatMap((c) => c.functions),
|
|
257
|
+
dataSources: configs.flatMap((c) => c.dataSources)
|
|
258
|
+
};
|
|
259
|
+
return withDataSourcesByName(merged);
|
|
260
|
+
}
|
|
261
|
+
function withDataSourcesByName(config) {
|
|
262
|
+
const byId = new Set(config.dataSources.map((ds) => ds.logicalId));
|
|
263
|
+
const byName = /* @__PURE__ */ new Map();
|
|
264
|
+
for (const ds of config.dataSources) {
|
|
265
|
+
if (ds.name !== null) {
|
|
266
|
+
byName.set((0, import_manifest_aws3.apiScopedKey)(ds.apiLogicalId, ds.name), ds.logicalId);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const resolve = (entry) => {
|
|
270
|
+
const { dataSourceLogicalId, dataSourceWritten } = entry;
|
|
271
|
+
if (dataSourceLogicalId !== null && byId.has(dataSourceLogicalId)) {
|
|
272
|
+
return entry;
|
|
273
|
+
}
|
|
274
|
+
const named = dataSourceWritten === null ? void 0 : byName.get((0, import_manifest_aws3.apiScopedKey)(entry.apiLogicalId, dataSourceWritten));
|
|
275
|
+
return named === void 0 ? entry : { ...entry, dataSourceLogicalId: named };
|
|
276
|
+
};
|
|
277
|
+
return {
|
|
278
|
+
...config,
|
|
279
|
+
resolvers: config.resolvers.map(resolve),
|
|
280
|
+
functions: config.functions.map(resolve)
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function collectApis(resources, refs) {
|
|
237
284
|
const apis = [];
|
|
238
|
-
const schemaByApi = indexSchemasByApi(resources);
|
|
285
|
+
const schemaByApi = indexSchemasByApi(resources, refs);
|
|
239
286
|
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
240
287
|
if (resource?.Type !== "AWS::AppSync::GraphQLApi") {
|
|
241
288
|
continue;
|
|
242
289
|
}
|
|
243
290
|
const props = resource.Properties ?? {};
|
|
291
|
+
const id = refs.own(logicalId);
|
|
244
292
|
apis.push({
|
|
245
|
-
logicalId,
|
|
293
|
+
logicalId: id,
|
|
246
294
|
name: stringField(props.Name),
|
|
247
|
-
schemaSource: schemaByApi.get(
|
|
295
|
+
schemaSource: schemaByApi.get(id) ?? { kind: "absent" },
|
|
248
296
|
authenticationType: stringField(props.AuthenticationType)
|
|
249
297
|
});
|
|
250
298
|
}
|
|
251
299
|
return apis;
|
|
252
300
|
}
|
|
253
|
-
function indexSchemasByApi(resources) {
|
|
301
|
+
function indexSchemasByApi(resources, refs) {
|
|
254
302
|
const out = /* @__PURE__ */ new Map();
|
|
255
303
|
for (const resource of Object.values(resources)) {
|
|
256
304
|
if (resource?.Type !== "AWS::AppSync::GraphQLSchema") {
|
|
257
305
|
continue;
|
|
258
306
|
}
|
|
259
307
|
const props = resource.Properties ?? {};
|
|
260
|
-
const apiRef =
|
|
308
|
+
const apiRef = refs.ref(props.ApiId);
|
|
261
309
|
if (apiRef === null) {
|
|
262
310
|
continue;
|
|
263
311
|
}
|
|
@@ -277,7 +325,7 @@ function indexSchemasByApi(resources) {
|
|
|
277
325
|
}
|
|
278
326
|
return out;
|
|
279
327
|
}
|
|
280
|
-
function collectResolvers(resources) {
|
|
328
|
+
function collectResolvers(resources, refs) {
|
|
281
329
|
const out = [];
|
|
282
330
|
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
283
331
|
if (resource?.Type !== "AWS::AppSync::Resolver") {
|
|
@@ -290,20 +338,24 @@ function collectResolvers(resources) {
|
|
|
290
338
|
continue;
|
|
291
339
|
}
|
|
292
340
|
out.push({
|
|
293
|
-
logicalId,
|
|
294
|
-
apiLogicalId:
|
|
341
|
+
logicalId: refs.own(logicalId),
|
|
342
|
+
apiLogicalId: refs.ref(props.ApiId),
|
|
295
343
|
typeName,
|
|
296
344
|
fieldName,
|
|
297
|
-
dataSourceLogicalId:
|
|
345
|
+
dataSourceLogicalId: refs.ref(props.DataSourceName),
|
|
346
|
+
dataSourceWritten: stringField(props.DataSourceName),
|
|
298
347
|
kind: resolverKind(stringField(props.Kind)),
|
|
299
|
-
pipelineFunctionLogicalIds: pipelineFunctionIds(
|
|
348
|
+
pipelineFunctionLogicalIds: pipelineFunctionIds(
|
|
349
|
+
props.PipelineConfig,
|
|
350
|
+
refs
|
|
351
|
+
),
|
|
300
352
|
codeUri: null,
|
|
301
353
|
runtime: null
|
|
302
354
|
});
|
|
303
355
|
}
|
|
304
356
|
return out;
|
|
305
357
|
}
|
|
306
|
-
function pipelineFunctionIds(pipelineConfig) {
|
|
358
|
+
function pipelineFunctionIds(pipelineConfig, refs) {
|
|
307
359
|
const config = asRecord(pipelineConfig);
|
|
308
360
|
if (config === null) {
|
|
309
361
|
return [];
|
|
@@ -314,14 +366,14 @@ function pipelineFunctionIds(pipelineConfig) {
|
|
|
314
366
|
}
|
|
315
367
|
const out = [];
|
|
316
368
|
for (const entry of functions) {
|
|
317
|
-
const ref =
|
|
369
|
+
const ref = refs.ref(entry);
|
|
318
370
|
if (ref !== null) {
|
|
319
371
|
out.push(ref);
|
|
320
372
|
}
|
|
321
373
|
}
|
|
322
374
|
return out;
|
|
323
375
|
}
|
|
324
|
-
function collectFunctions(resources) {
|
|
376
|
+
function collectFunctions(resources, refs) {
|
|
325
377
|
const out = [];
|
|
326
378
|
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
327
379
|
if (resource?.Type !== "AWS::AppSync::FunctionConfiguration") {
|
|
@@ -329,17 +381,18 @@ function collectFunctions(resources) {
|
|
|
329
381
|
}
|
|
330
382
|
const props = resource.Properties ?? {};
|
|
331
383
|
out.push({
|
|
332
|
-
logicalId,
|
|
333
|
-
apiLogicalId:
|
|
384
|
+
logicalId: refs.own(logicalId),
|
|
385
|
+
apiLogicalId: refs.ref(props.ApiId),
|
|
334
386
|
name: stringField(props.Name),
|
|
335
|
-
dataSourceLogicalId:
|
|
387
|
+
dataSourceLogicalId: refs.ref(props.DataSourceName),
|
|
388
|
+
dataSourceWritten: stringField(props.DataSourceName),
|
|
336
389
|
codeUri: null,
|
|
337
390
|
runtime: null
|
|
338
391
|
});
|
|
339
392
|
}
|
|
340
393
|
return out;
|
|
341
394
|
}
|
|
342
|
-
function collectDataSources(resources) {
|
|
395
|
+
function collectDataSources(resources, refs) {
|
|
343
396
|
const out = [];
|
|
344
397
|
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
345
398
|
if (resource?.Type !== "AWS::AppSync::DataSource") {
|
|
@@ -348,10 +401,11 @@ function collectDataSources(resources) {
|
|
|
348
401
|
const props = resource.Properties ?? {};
|
|
349
402
|
const lambdaConfig = asRecord(props.LambdaConfig);
|
|
350
403
|
out.push({
|
|
351
|
-
logicalId,
|
|
352
|
-
apiLogicalId:
|
|
404
|
+
logicalId: refs.own(logicalId),
|
|
405
|
+
apiLogicalId: refs.ref(props.ApiId),
|
|
406
|
+
name: stringField(props.Name),
|
|
353
407
|
type: dataSourceType(stringField(props.Type)),
|
|
354
|
-
lambdaFunctionLogicalId: lambdaConfig === null ? null :
|
|
408
|
+
lambdaFunctionLogicalId: lambdaConfig === null ? null : refs.ref(lambdaConfig.LambdaFunctionArn)
|
|
355
409
|
});
|
|
356
410
|
}
|
|
357
411
|
return out;
|
|
@@ -720,18 +774,31 @@ function buildTransitions(ownerKey, resolver, field) {
|
|
|
720
774
|
|
|
721
775
|
// src/index.ts
|
|
722
776
|
function appsyncToSummaries(template, options = {}) {
|
|
723
|
-
const config = readAppSyncFromCfn(template);
|
|
777
|
+
const config = mergeConfigs([readAppSyncFromCfn(template)]);
|
|
724
778
|
const baseDir = options.baseDir ?? null;
|
|
725
|
-
|
|
779
|
+
return summariesOf(
|
|
780
|
+
config,
|
|
781
|
+
new Map(config.apis.map((api) => [api.logicalId, baseDir])),
|
|
782
|
+
options.source
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
function summariesOf(config, baseDirByApi, source) {
|
|
786
|
+
const resolvedByApi = resolveSchemas(config, baseDirByApi);
|
|
726
787
|
const schemasByApi = indexSchemas(resolvedByApi);
|
|
727
788
|
return buildResolverSummaries(config, resolvedByApi, schemasByApi, {
|
|
728
|
-
...
|
|
789
|
+
...source !== void 0 ? { source } : {}
|
|
729
790
|
});
|
|
730
791
|
}
|
|
731
|
-
function resolveSchemas(config,
|
|
792
|
+
function resolveSchemas(config, baseDirByApi) {
|
|
732
793
|
const out = /* @__PURE__ */ new Map();
|
|
733
794
|
for (const api of config.apis) {
|
|
734
|
-
out.set(
|
|
795
|
+
out.set(
|
|
796
|
+
api.logicalId,
|
|
797
|
+
resolveSchemaSource(
|
|
798
|
+
api.schemaSource,
|
|
799
|
+
baseDirByApi.get(api.logicalId) ?? null
|
|
800
|
+
)
|
|
801
|
+
);
|
|
735
802
|
}
|
|
736
803
|
return out;
|
|
737
804
|
}
|
|
@@ -745,12 +812,34 @@ function indexSchemas(resolvedByApi) {
|
|
|
745
812
|
return out;
|
|
746
813
|
}
|
|
747
814
|
function appsyncFileToSummaries(filePath, options = {}) {
|
|
748
|
-
const
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
815
|
+
const tree = (0, import_manifest_aws4.loadTemplateTree)(filePath);
|
|
816
|
+
for (const stack of tree.unfollowed) {
|
|
817
|
+
const message = (0, import_manifest_aws4.unfollowedStackMessage)(stack);
|
|
818
|
+
if (options.warnings !== void 0) {
|
|
819
|
+
options.warnings.push(message);
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
process.stderr.write(`[suss] appsync: ${message}
|
|
823
|
+
`);
|
|
824
|
+
}
|
|
825
|
+
const baseDirByApi = /* @__PURE__ */ new Map();
|
|
826
|
+
const configs = tree.documents.map((document) => {
|
|
827
|
+
const config = readAppSyncFromCfn(
|
|
828
|
+
document.template,
|
|
829
|
+
documentRefsOf(document)
|
|
830
|
+
);
|
|
831
|
+
const baseDir = document.stackPath.length === 0 && options.baseDir !== void 0 ? options.baseDir : import_node_path2.default.dirname(document.path);
|
|
832
|
+
for (const api of config.apis) {
|
|
833
|
+
baseDirByApi.set(api.logicalId, baseDir);
|
|
834
|
+
}
|
|
835
|
+
return config;
|
|
753
836
|
});
|
|
837
|
+
const root = options.projectRoot ?? (0, import_files.defaultProjectRoot)(filePath);
|
|
838
|
+
return summariesOf(
|
|
839
|
+
mergeConfigs(configs),
|
|
840
|
+
baseDirByApi,
|
|
841
|
+
options.source ?? (0, import_files.pathInProject)(filePath, root)
|
|
842
|
+
);
|
|
754
843
|
}
|
|
755
844
|
// Annotate the CommonJS export names for ESM import in node:
|
|
756
845
|
0 && (module.exports = {
|
package/dist/index.d.cts
CHANGED
|
@@ -5,9 +5,12 @@ import { BehavioralSummary } from '@suss/behavioral-ir';
|
|
|
5
5
|
* SAM AWS::Serverless::GraphQLApi reader finds, so both ways of writing
|
|
6
6
|
* an API reach the summary builder as one model.
|
|
7
7
|
*
|
|
8
|
-
* A value written with a dynamic intrinsic
|
|
9
|
-
*
|
|
8
|
+
* A value written with a dynamic intrinsic is left unresolved and
|
|
9
|
+
* reported. The reader never guesses it. A `!Ref` to a parameter the
|
|
10
|
+
* parent stack sets to a resource is read as that resource, which is how
|
|
11
|
+
* a nested stack's resolvers find the API their parent declares.
|
|
10
12
|
*/
|
|
13
|
+
|
|
11
14
|
interface CfnTemplate {
|
|
12
15
|
Resources?: Record<string, CfnResource | undefined>;
|
|
13
16
|
}
|
|
@@ -38,6 +41,8 @@ interface AppsyncToSummariesOptions {
|
|
|
38
41
|
interface AppsyncFileToSummariesOptions extends AppsyncToSummariesOptions {
|
|
39
42
|
/** Absolute. The directory the template's path is measured from. */
|
|
40
43
|
projectRoot?: string;
|
|
44
|
+
/** Collects each child template that could not be opened. Without it, each goes to stderr. */
|
|
45
|
+
warnings?: string[];
|
|
41
46
|
}
|
|
42
47
|
/**
|
|
43
48
|
* Converts a template already in memory, such as a CDK
|
|
@@ -45,8 +50,11 @@ interface AppsyncFileToSummariesOptions extends AppsyncToSummariesOptions {
|
|
|
45
50
|
*/
|
|
46
51
|
declare function appsyncToSummaries(template: CfnTemplate, options?: AppsyncToSummariesOptions): BehavioralSummary[];
|
|
47
52
|
/**
|
|
48
|
-
* Reads a CloudFormation or SAM template, in JSON or YAML, and
|
|
49
|
-
* it
|
|
53
|
+
* Reads a CloudFormation or SAM template, in JSON or YAML, and every
|
|
54
|
+
* template it embeds as a nested stack, and converts them as one API
|
|
55
|
+
* surface. A child's resolvers often belong to an API the root declares
|
|
56
|
+
* and passes down as a parameter. A relative schema path resolves
|
|
57
|
+
* against the directory of the template that declares it.
|
|
50
58
|
*/
|
|
51
59
|
declare function appsyncFileToSummaries(filePath: string, options?: AppsyncFileToSummariesOptions): BehavioralSummary[];
|
|
52
60
|
|
package/dist/index.d.ts
CHANGED
|
@@ -5,9 +5,12 @@ import { BehavioralSummary } from '@suss/behavioral-ir';
|
|
|
5
5
|
* SAM AWS::Serverless::GraphQLApi reader finds, so both ways of writing
|
|
6
6
|
* an API reach the summary builder as one model.
|
|
7
7
|
*
|
|
8
|
-
* A value written with a dynamic intrinsic
|
|
9
|
-
*
|
|
8
|
+
* A value written with a dynamic intrinsic is left unresolved and
|
|
9
|
+
* reported. The reader never guesses it. A `!Ref` to a parameter the
|
|
10
|
+
* parent stack sets to a resource is read as that resource, which is how
|
|
11
|
+
* a nested stack's resolvers find the API their parent declares.
|
|
10
12
|
*/
|
|
13
|
+
|
|
11
14
|
interface CfnTemplate {
|
|
12
15
|
Resources?: Record<string, CfnResource | undefined>;
|
|
13
16
|
}
|
|
@@ -38,6 +41,8 @@ interface AppsyncToSummariesOptions {
|
|
|
38
41
|
interface AppsyncFileToSummariesOptions extends AppsyncToSummariesOptions {
|
|
39
42
|
/** Absolute. The directory the template's path is measured from. */
|
|
40
43
|
projectRoot?: string;
|
|
44
|
+
/** Collects each child template that could not be opened. Without it, each goes to stderr. */
|
|
45
|
+
warnings?: string[];
|
|
41
46
|
}
|
|
42
47
|
/**
|
|
43
48
|
* Converts a template already in memory, such as a CDK
|
|
@@ -45,8 +50,11 @@ interface AppsyncFileToSummariesOptions extends AppsyncToSummariesOptions {
|
|
|
45
50
|
*/
|
|
46
51
|
declare function appsyncToSummaries(template: CfnTemplate, options?: AppsyncToSummariesOptions): BehavioralSummary[];
|
|
47
52
|
/**
|
|
48
|
-
* Reads a CloudFormation or SAM template, in JSON or YAML, and
|
|
49
|
-
* it
|
|
53
|
+
* Reads a CloudFormation or SAM template, in JSON or YAML, and every
|
|
54
|
+
* template it embeds as a nested stack, and converts them as one API
|
|
55
|
+
* surface. A child's resolvers often belong to an API the root declares
|
|
56
|
+
* and passes down as a parameter. A relative schema path resolves
|
|
57
|
+
* against the directory of the template that declares it.
|
|
50
58
|
*/
|
|
51
59
|
declare function appsyncFileToSummaries(filePath: string, options?: AppsyncFileToSummariesOptions): BehavioralSummary[];
|
|
52
60
|
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import path2 from "path";
|
|
3
3
|
import { defaultProjectRoot, pathInProject } from "@suss/behavioral-ir/files";
|
|
4
|
-
import {
|
|
4
|
+
import { loadTemplateTree, unfollowedStackMessage } from "@suss/manifest-aws";
|
|
5
|
+
|
|
6
|
+
// src/cfn.ts
|
|
7
|
+
import { apiScopedKey } from "@suss/manifest-aws";
|
|
5
8
|
|
|
6
9
|
// src/refs.ts
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
import {
|
|
11
|
+
documentRef,
|
|
12
|
+
qualifiedLogicalId,
|
|
13
|
+
refTarget
|
|
14
|
+
} from "@suss/manifest-aws";
|
|
15
|
+
var OWN_DOCUMENT = {
|
|
16
|
+
own: (logicalId) => logicalId,
|
|
17
|
+
ref: (value) => refTarget(value)
|
|
18
|
+
};
|
|
19
|
+
function documentRefsOf(document) {
|
|
20
|
+
return {
|
|
21
|
+
own: (logicalId) => qualifiedLogicalId(document.stackPath, logicalId),
|
|
22
|
+
ref: (value) => documentRef(document, value)
|
|
23
|
+
};
|
|
10
24
|
}
|
|
11
25
|
function stringField(value) {
|
|
12
26
|
return typeof value === "string" ? value : null;
|
|
@@ -19,6 +33,7 @@ function asRecord(value) {
|
|
|
19
33
|
}
|
|
20
34
|
|
|
21
35
|
// src/sam.ts
|
|
36
|
+
import { refTarget as refTarget2 } from "@suss/manifest-aws";
|
|
22
37
|
var RESOLVER_ROOT_TYPES = ["Query", "Mutation", "Subscription"];
|
|
23
38
|
var DATA_SOURCE_CATEGORIES = {
|
|
24
39
|
Lambda: "lambda",
|
|
@@ -33,7 +48,7 @@ var DATA_SOURCE_CATEGORIES = {
|
|
|
33
48
|
RelationalDatabases: "relational",
|
|
34
49
|
None: "none"
|
|
35
50
|
};
|
|
36
|
-
function readServerlessGraphQLApis(resources) {
|
|
51
|
+
function readServerlessGraphQLApis(resources, refs) {
|
|
37
52
|
const out = {
|
|
38
53
|
apis: [],
|
|
39
54
|
resolvers: [],
|
|
@@ -44,18 +59,20 @@ function readServerlessGraphQLApis(resources) {
|
|
|
44
59
|
if (resource?.Type !== "AWS::Serverless::GraphQLApi") {
|
|
45
60
|
continue;
|
|
46
61
|
}
|
|
47
|
-
readOne(logicalId, resource.Properties ?? {}, out);
|
|
62
|
+
readOne(refs.own(logicalId), resource.Properties ?? {}, out, refs);
|
|
48
63
|
}
|
|
49
64
|
return out;
|
|
50
65
|
}
|
|
51
|
-
function readOne(apiLogicalId, props, out) {
|
|
66
|
+
function readOne(apiLogicalId, props, out, refs) {
|
|
52
67
|
out.apis.push({
|
|
53
68
|
logicalId: apiLogicalId,
|
|
54
69
|
name: stringField(props.Name),
|
|
55
70
|
schemaSource: readSchemaSource(props),
|
|
56
71
|
authenticationType: readAuthType(props.Auth)
|
|
57
72
|
});
|
|
58
|
-
out.dataSources.push(
|
|
73
|
+
out.dataSources.push(
|
|
74
|
+
...readDataSources(apiLogicalId, props.DataSources, refs)
|
|
75
|
+
);
|
|
59
76
|
out.functions.push(...readFunctions(apiLogicalId, props.Functions));
|
|
60
77
|
out.resolvers.push(...readResolvers(apiLogicalId, props.Resolvers));
|
|
61
78
|
}
|
|
@@ -77,7 +94,7 @@ function readAuthType(auth) {
|
|
|
77
94
|
const record = asRecord(auth);
|
|
78
95
|
return record === null ? null : stringField(record.Type);
|
|
79
96
|
}
|
|
80
|
-
function readDataSources(apiLogicalId, raw) {
|
|
97
|
+
function readDataSources(apiLogicalId, raw, refs) {
|
|
81
98
|
const dataSources = asRecord(raw);
|
|
82
99
|
if (dataSources === null) {
|
|
83
100
|
return [];
|
|
@@ -94,8 +111,9 @@ function readDataSources(apiLogicalId, raw) {
|
|
|
94
111
|
out.push({
|
|
95
112
|
logicalId: dataSourceId(apiLogicalId, name),
|
|
96
113
|
apiLogicalId,
|
|
114
|
+
name,
|
|
97
115
|
type,
|
|
98
|
-
lambdaFunctionLogicalId: type === "lambda" && def !== null ?
|
|
116
|
+
lambdaFunctionLogicalId: type === "lambda" && def !== null ? refs.ref(def.FunctionArn) : null
|
|
99
117
|
});
|
|
100
118
|
}
|
|
101
119
|
}
|
|
@@ -114,6 +132,7 @@ function readFunctions(apiLogicalId, raw) {
|
|
|
114
132
|
apiLogicalId,
|
|
115
133
|
name,
|
|
116
134
|
dataSourceLogicalId: referencedDataSourceId(apiLogicalId, def.DataSource),
|
|
135
|
+
dataSourceWritten: null,
|
|
117
136
|
codeUri: stringField(def.CodeUri),
|
|
118
137
|
runtime: readRuntime(def.Runtime)
|
|
119
138
|
});
|
|
@@ -147,6 +166,7 @@ function buildResolver(apiLogicalId, typeName, fieldName, def) {
|
|
|
147
166
|
typeName,
|
|
148
167
|
fieldName,
|
|
149
168
|
dataSourceLogicalId: isPipeline ? null : referencedDataSourceId(apiLogicalId, def.DataSource),
|
|
169
|
+
dataSourceWritten: null,
|
|
150
170
|
kind: isPipeline ? "PIPELINE" : "UNIT",
|
|
151
171
|
pipelineFunctionLogicalIds: pipeline,
|
|
152
172
|
codeUri: stringField(def.CodeUri),
|
|
@@ -159,7 +179,7 @@ function readPipeline(apiLogicalId, raw) {
|
|
|
159
179
|
}
|
|
160
180
|
const out = [];
|
|
161
181
|
for (const entry of raw) {
|
|
162
|
-
const name = stringField(entry) ??
|
|
182
|
+
const name = stringField(entry) ?? refTarget2(entry);
|
|
163
183
|
if (name !== null) {
|
|
164
184
|
out.push(functionId(apiLogicalId, name));
|
|
165
185
|
}
|
|
@@ -182,15 +202,15 @@ function functionId(apiLogicalId, name) {
|
|
|
182
202
|
}
|
|
183
203
|
|
|
184
204
|
// src/cfn.ts
|
|
185
|
-
function readAppSyncFromCfn(template) {
|
|
205
|
+
function readAppSyncFromCfn(template, refs = OWN_DOCUMENT) {
|
|
186
206
|
const resources = template.Resources ?? {};
|
|
187
207
|
const raw = {
|
|
188
|
-
apis: collectApis(resources),
|
|
189
|
-
resolvers: collectResolvers(resources),
|
|
190
|
-
functions: collectFunctions(resources),
|
|
191
|
-
dataSources: collectDataSources(resources)
|
|
208
|
+
apis: collectApis(resources, refs),
|
|
209
|
+
resolvers: collectResolvers(resources, refs),
|
|
210
|
+
functions: collectFunctions(resources, refs),
|
|
211
|
+
dataSources: collectDataSources(resources, refs)
|
|
192
212
|
};
|
|
193
|
-
const sam = readServerlessGraphQLApis(resources);
|
|
213
|
+
const sam = readServerlessGraphQLApis(resources, refs);
|
|
194
214
|
return {
|
|
195
215
|
apis: [...raw.apis, ...sam.apis],
|
|
196
216
|
resolvers: [...raw.resolvers, ...sam.resolvers],
|
|
@@ -198,31 +218,63 @@ function readAppSyncFromCfn(template) {
|
|
|
198
218
|
dataSources: [...raw.dataSources, ...sam.dataSources]
|
|
199
219
|
};
|
|
200
220
|
}
|
|
201
|
-
function
|
|
221
|
+
function mergeConfigs(configs) {
|
|
222
|
+
const merged = {
|
|
223
|
+
apis: configs.flatMap((c) => c.apis),
|
|
224
|
+
resolvers: configs.flatMap((c) => c.resolvers),
|
|
225
|
+
functions: configs.flatMap((c) => c.functions),
|
|
226
|
+
dataSources: configs.flatMap((c) => c.dataSources)
|
|
227
|
+
};
|
|
228
|
+
return withDataSourcesByName(merged);
|
|
229
|
+
}
|
|
230
|
+
function withDataSourcesByName(config) {
|
|
231
|
+
const byId = new Set(config.dataSources.map((ds) => ds.logicalId));
|
|
232
|
+
const byName = /* @__PURE__ */ new Map();
|
|
233
|
+
for (const ds of config.dataSources) {
|
|
234
|
+
if (ds.name !== null) {
|
|
235
|
+
byName.set(apiScopedKey(ds.apiLogicalId, ds.name), ds.logicalId);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const resolve = (entry) => {
|
|
239
|
+
const { dataSourceLogicalId, dataSourceWritten } = entry;
|
|
240
|
+
if (dataSourceLogicalId !== null && byId.has(dataSourceLogicalId)) {
|
|
241
|
+
return entry;
|
|
242
|
+
}
|
|
243
|
+
const named = dataSourceWritten === null ? void 0 : byName.get(apiScopedKey(entry.apiLogicalId, dataSourceWritten));
|
|
244
|
+
return named === void 0 ? entry : { ...entry, dataSourceLogicalId: named };
|
|
245
|
+
};
|
|
246
|
+
return {
|
|
247
|
+
...config,
|
|
248
|
+
resolvers: config.resolvers.map(resolve),
|
|
249
|
+
functions: config.functions.map(resolve)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function collectApis(resources, refs) {
|
|
202
253
|
const apis = [];
|
|
203
|
-
const schemaByApi = indexSchemasByApi(resources);
|
|
254
|
+
const schemaByApi = indexSchemasByApi(resources, refs);
|
|
204
255
|
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
205
256
|
if (resource?.Type !== "AWS::AppSync::GraphQLApi") {
|
|
206
257
|
continue;
|
|
207
258
|
}
|
|
208
259
|
const props = resource.Properties ?? {};
|
|
260
|
+
const id = refs.own(logicalId);
|
|
209
261
|
apis.push({
|
|
210
|
-
logicalId,
|
|
262
|
+
logicalId: id,
|
|
211
263
|
name: stringField(props.Name),
|
|
212
|
-
schemaSource: schemaByApi.get(
|
|
264
|
+
schemaSource: schemaByApi.get(id) ?? { kind: "absent" },
|
|
213
265
|
authenticationType: stringField(props.AuthenticationType)
|
|
214
266
|
});
|
|
215
267
|
}
|
|
216
268
|
return apis;
|
|
217
269
|
}
|
|
218
|
-
function indexSchemasByApi(resources) {
|
|
270
|
+
function indexSchemasByApi(resources, refs) {
|
|
219
271
|
const out = /* @__PURE__ */ new Map();
|
|
220
272
|
for (const resource of Object.values(resources)) {
|
|
221
273
|
if (resource?.Type !== "AWS::AppSync::GraphQLSchema") {
|
|
222
274
|
continue;
|
|
223
275
|
}
|
|
224
276
|
const props = resource.Properties ?? {};
|
|
225
|
-
const apiRef =
|
|
277
|
+
const apiRef = refs.ref(props.ApiId);
|
|
226
278
|
if (apiRef === null) {
|
|
227
279
|
continue;
|
|
228
280
|
}
|
|
@@ -242,7 +294,7 @@ function indexSchemasByApi(resources) {
|
|
|
242
294
|
}
|
|
243
295
|
return out;
|
|
244
296
|
}
|
|
245
|
-
function collectResolvers(resources) {
|
|
297
|
+
function collectResolvers(resources, refs) {
|
|
246
298
|
const out = [];
|
|
247
299
|
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
248
300
|
if (resource?.Type !== "AWS::AppSync::Resolver") {
|
|
@@ -255,20 +307,24 @@ function collectResolvers(resources) {
|
|
|
255
307
|
continue;
|
|
256
308
|
}
|
|
257
309
|
out.push({
|
|
258
|
-
logicalId,
|
|
259
|
-
apiLogicalId:
|
|
310
|
+
logicalId: refs.own(logicalId),
|
|
311
|
+
apiLogicalId: refs.ref(props.ApiId),
|
|
260
312
|
typeName,
|
|
261
313
|
fieldName,
|
|
262
|
-
dataSourceLogicalId:
|
|
314
|
+
dataSourceLogicalId: refs.ref(props.DataSourceName),
|
|
315
|
+
dataSourceWritten: stringField(props.DataSourceName),
|
|
263
316
|
kind: resolverKind(stringField(props.Kind)),
|
|
264
|
-
pipelineFunctionLogicalIds: pipelineFunctionIds(
|
|
317
|
+
pipelineFunctionLogicalIds: pipelineFunctionIds(
|
|
318
|
+
props.PipelineConfig,
|
|
319
|
+
refs
|
|
320
|
+
),
|
|
265
321
|
codeUri: null,
|
|
266
322
|
runtime: null
|
|
267
323
|
});
|
|
268
324
|
}
|
|
269
325
|
return out;
|
|
270
326
|
}
|
|
271
|
-
function pipelineFunctionIds(pipelineConfig) {
|
|
327
|
+
function pipelineFunctionIds(pipelineConfig, refs) {
|
|
272
328
|
const config = asRecord(pipelineConfig);
|
|
273
329
|
if (config === null) {
|
|
274
330
|
return [];
|
|
@@ -279,14 +335,14 @@ function pipelineFunctionIds(pipelineConfig) {
|
|
|
279
335
|
}
|
|
280
336
|
const out = [];
|
|
281
337
|
for (const entry of functions) {
|
|
282
|
-
const ref =
|
|
338
|
+
const ref = refs.ref(entry);
|
|
283
339
|
if (ref !== null) {
|
|
284
340
|
out.push(ref);
|
|
285
341
|
}
|
|
286
342
|
}
|
|
287
343
|
return out;
|
|
288
344
|
}
|
|
289
|
-
function collectFunctions(resources) {
|
|
345
|
+
function collectFunctions(resources, refs) {
|
|
290
346
|
const out = [];
|
|
291
347
|
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
292
348
|
if (resource?.Type !== "AWS::AppSync::FunctionConfiguration") {
|
|
@@ -294,17 +350,18 @@ function collectFunctions(resources) {
|
|
|
294
350
|
}
|
|
295
351
|
const props = resource.Properties ?? {};
|
|
296
352
|
out.push({
|
|
297
|
-
logicalId,
|
|
298
|
-
apiLogicalId:
|
|
353
|
+
logicalId: refs.own(logicalId),
|
|
354
|
+
apiLogicalId: refs.ref(props.ApiId),
|
|
299
355
|
name: stringField(props.Name),
|
|
300
|
-
dataSourceLogicalId:
|
|
356
|
+
dataSourceLogicalId: refs.ref(props.DataSourceName),
|
|
357
|
+
dataSourceWritten: stringField(props.DataSourceName),
|
|
301
358
|
codeUri: null,
|
|
302
359
|
runtime: null
|
|
303
360
|
});
|
|
304
361
|
}
|
|
305
362
|
return out;
|
|
306
363
|
}
|
|
307
|
-
function collectDataSources(resources) {
|
|
364
|
+
function collectDataSources(resources, refs) {
|
|
308
365
|
const out = [];
|
|
309
366
|
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
310
367
|
if (resource?.Type !== "AWS::AppSync::DataSource") {
|
|
@@ -313,10 +370,11 @@ function collectDataSources(resources) {
|
|
|
313
370
|
const props = resource.Properties ?? {};
|
|
314
371
|
const lambdaConfig = asRecord(props.LambdaConfig);
|
|
315
372
|
out.push({
|
|
316
|
-
logicalId,
|
|
317
|
-
apiLogicalId:
|
|
373
|
+
logicalId: refs.own(logicalId),
|
|
374
|
+
apiLogicalId: refs.ref(props.ApiId),
|
|
375
|
+
name: stringField(props.Name),
|
|
318
376
|
type: dataSourceType(stringField(props.Type)),
|
|
319
|
-
lambdaFunctionLogicalId: lambdaConfig === null ? null :
|
|
377
|
+
lambdaFunctionLogicalId: lambdaConfig === null ? null : refs.ref(lambdaConfig.LambdaFunctionArn)
|
|
320
378
|
});
|
|
321
379
|
}
|
|
322
380
|
return out;
|
|
@@ -693,18 +751,31 @@ function buildTransitions(ownerKey, resolver, field) {
|
|
|
693
751
|
|
|
694
752
|
// src/index.ts
|
|
695
753
|
function appsyncToSummaries(template, options = {}) {
|
|
696
|
-
const config = readAppSyncFromCfn(template);
|
|
754
|
+
const config = mergeConfigs([readAppSyncFromCfn(template)]);
|
|
697
755
|
const baseDir = options.baseDir ?? null;
|
|
698
|
-
|
|
756
|
+
return summariesOf(
|
|
757
|
+
config,
|
|
758
|
+
new Map(config.apis.map((api) => [api.logicalId, baseDir])),
|
|
759
|
+
options.source
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
function summariesOf(config, baseDirByApi, source) {
|
|
763
|
+
const resolvedByApi = resolveSchemas(config, baseDirByApi);
|
|
699
764
|
const schemasByApi = indexSchemas(resolvedByApi);
|
|
700
765
|
return buildResolverSummaries(config, resolvedByApi, schemasByApi, {
|
|
701
|
-
...
|
|
766
|
+
...source !== void 0 ? { source } : {}
|
|
702
767
|
});
|
|
703
768
|
}
|
|
704
|
-
function resolveSchemas(config,
|
|
769
|
+
function resolveSchemas(config, baseDirByApi) {
|
|
705
770
|
const out = /* @__PURE__ */ new Map();
|
|
706
771
|
for (const api of config.apis) {
|
|
707
|
-
out.set(
|
|
772
|
+
out.set(
|
|
773
|
+
api.logicalId,
|
|
774
|
+
resolveSchemaSource(
|
|
775
|
+
api.schemaSource,
|
|
776
|
+
baseDirByApi.get(api.logicalId) ?? null
|
|
777
|
+
)
|
|
778
|
+
);
|
|
708
779
|
}
|
|
709
780
|
return out;
|
|
710
781
|
}
|
|
@@ -718,12 +789,34 @@ function indexSchemas(resolvedByApi) {
|
|
|
718
789
|
return out;
|
|
719
790
|
}
|
|
720
791
|
function appsyncFileToSummaries(filePath, options = {}) {
|
|
721
|
-
const
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
792
|
+
const tree = loadTemplateTree(filePath);
|
|
793
|
+
for (const stack of tree.unfollowed) {
|
|
794
|
+
const message = unfollowedStackMessage(stack);
|
|
795
|
+
if (options.warnings !== void 0) {
|
|
796
|
+
options.warnings.push(message);
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
process.stderr.write(`[suss] appsync: ${message}
|
|
800
|
+
`);
|
|
801
|
+
}
|
|
802
|
+
const baseDirByApi = /* @__PURE__ */ new Map();
|
|
803
|
+
const configs = tree.documents.map((document) => {
|
|
804
|
+
const config = readAppSyncFromCfn(
|
|
805
|
+
document.template,
|
|
806
|
+
documentRefsOf(document)
|
|
807
|
+
);
|
|
808
|
+
const baseDir = document.stackPath.length === 0 && options.baseDir !== void 0 ? options.baseDir : path2.dirname(document.path);
|
|
809
|
+
for (const api of config.apis) {
|
|
810
|
+
baseDirByApi.set(api.logicalId, baseDir);
|
|
811
|
+
}
|
|
812
|
+
return config;
|
|
726
813
|
});
|
|
814
|
+
const root = options.projectRoot ?? defaultProjectRoot(filePath);
|
|
815
|
+
return summariesOf(
|
|
816
|
+
mergeConfigs(configs),
|
|
817
|
+
baseDirByApi,
|
|
818
|
+
options.source ?? pathInProject(filePath, root)
|
|
819
|
+
);
|
|
727
820
|
}
|
|
728
821
|
export {
|
|
729
822
|
appsyncFileToSummaries,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/contract-appsync",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Turn the AppSync API in a CloudFormation or SAM template into a suss contract and check it against the resolvers and clients on either side of it.",
|
|
6
6
|
"keywords": [
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
"typecheck": "tsc --noEmit"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@suss/behavioral-ir": "0.
|
|
52
|
-
"@suss/contract-graphql": "0.
|
|
53
|
-
"@suss/manifest-aws": "0.
|
|
51
|
+
"@suss/behavioral-ir": "0.37.1",
|
|
52
|
+
"@suss/contract-graphql": "0.37.1",
|
|
53
|
+
"@suss/manifest-aws": "0.37.1",
|
|
54
54
|
"graphql": "^16.8.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|