@suss/contract-graphql 0.33.0 → 0.34.0
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 +21 -21
- package/dist/index.cjs +8 -13
- package/dist/index.d.cts +52 -44
- package/dist/index.d.ts +52 -44
- package/dist/index.js +8 -13
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
1
|
# @suss/contract-graphql
|
|
2
2
|
|
|
3
|
-
Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and
|
|
3
|
+
Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and reports where the two disagree.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package builds suss `BehavioralSummary[]` from the GraphQL files a repository already commits. It has two readers, one for each side of a GraphQL boundary:
|
|
6
6
|
|
|
7
7
|
| Reader | Input | Summaries |
|
|
8
8
|
|--------|-------|-----------|
|
|
9
9
|
| `suss contract --from graphql` | a plain SDL schema file | one `resolver`-kind summary per root field (the provider side) |
|
|
10
10
|
| `suss contract --from graphql-documents` | `.graphql` / `.gql` operation documents | one `client`-kind summary per operation (the consumer side) |
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Each side pairs with the other, and with summaries the Apollo and NestJS packs extract from code. Nothing has to be traced through TypeScript first.
|
|
13
13
|
|
|
14
14
|
## Reading a schema
|
|
15
15
|
|
|
16
|
-
`graphqlSdlFileToSummaries` parses the SDL and walks every field on `Query`, `Mutation
|
|
16
|
+
`graphqlSdlFileToSummaries` parses the SDL and walks every field on `Query`, `Mutation` and `Subscription`, including fields added with `extend type Query { ... }`. Each field becomes one summary:
|
|
17
17
|
|
|
18
|
-
- `identity.name` is `Type.field`, with a `graphql-resolver` boundary binding that records `transport: "http-graphql"`, `recognition: "graphql"`, and the type and field names.
|
|
18
|
+
- `identity.name` is `Type.field`, with a `graphql-resolver` boundary binding that records `transport: "http-graphql"`, `recognition: "graphql"`, and the type and field names. You can override both defaults, which helps when the same SDL backs several deployments and the findings should tell them apart.
|
|
19
19
|
- Inputs come from the field's arguments, each with `role: "args"`.
|
|
20
|
-
- Two transitions: a default success
|
|
21
|
-
- `metadata.graphql.declaredContract` records the return type, the arguments with a `required` flag
|
|
20
|
+
- Two transitions: a default success that returns the field's declared shape, and a `GraphQLError` throw for the `errors[]` path.
|
|
21
|
+
- `metadata.graphql.declaredContract` records the return type, the arguments with a `required` flag on each, and the framework tag. Its provenance is `"derived"`, because the contract and the transitions come from the same field declaration, and comparing one with the other would find nothing.
|
|
22
22
|
|
|
23
|
-
One extra `library`-kind summary contains the SDL text itself, under `metadata.graphql.schemaSdl`. Type definitions belong to the schema
|
|
23
|
+
One extra `library`-kind summary contains the SDL text itself, under `metadata.graphql.schemaSdl`. Type definitions belong to the schema as a whole, so every resolver points at that document through a source-document label. The checker follows the label to resolve a consumer's nested selections against a resolver's return type. That summary does not bind to a boundary, so pairing records it as taking no part.
|
|
24
24
|
|
|
25
|
-
Named types convert to `TypeShape`
|
|
25
|
+
Named types convert to `TypeShape` like this: `String` and `ID` become `text`, `Int` and `Float` become `number`, `Boolean` becomes `boolean`, and any other named type becomes a `ref` with that name. A list becomes an `array`. A non-null wrapper is dropped, because in a `TypeShape` a value is non-null when its union does not include null.
|
|
26
26
|
|
|
27
27
|
## Reading operation documents
|
|
28
28
|
|
|
29
|
-
`graphqlDocumentsPathToSummaries` accepts a single file or a directory
|
|
29
|
+
`graphqlDocumentsPathToSummaries` accepts a single file, or a directory that it searches recursively for `.graphql` and `.gql` files, skipping `node_modules`. Each query, mutation and subscription definition becomes one summary:
|
|
30
30
|
|
|
31
|
-
- A `graphql-operation` boundary binding with the operation type and, when the operation is named, its name. The default recognition tag is `"graphql-documents"`, so findings
|
|
32
|
-
- Inputs from the operation's variable definitions, each with `role: "variable"
|
|
33
|
-
- The full document text at `metadata.graphql.document`, the same place the TypeScript adapter puts documents it finds in code, so the checker's GraphQL pairing pass reads both
|
|
34
|
-
- A response shape
|
|
31
|
+
- A `graphql-operation` boundary binding with the operation type and, when the operation is named, its name. The default recognition tag is `"graphql-documents"`, so findings can tell these apart from operations found at a call site.
|
|
32
|
+
- Inputs from the operation's variable definitions, each with `role: "variable"`. That is the same role the TypeScript adapter gives the `$variables` it finds in a tagged template literal.
|
|
33
|
+
- The full document text at `metadata.graphql.document`, the same place the TypeScript adapter puts documents it finds in code, so the checker's GraphQL pairing pass reads both the same way.
|
|
34
|
+
- A response shape estimated from the selection set. Field names become record properties and leaves become `unknown`, since a document does not declare field types. A field selected twice, once directly and once through a fragment, merges into one property, the way a server would merge it.
|
|
35
35
|
|
|
36
|
-
Fragment spreads are resolved against every fragment definition in the read
|
|
36
|
+
Fragment spreads are resolved against every fragment definition in the files read, and inlined into the stored document, so the pairing pass sees the selected fields directly. A spread the reader cannot expand stays in the document as written, and becomes a gap on the summary instead of an error. The fragment may be missing from the files read, part of a cycle, or defined in more than one file. When it is defined more than once, the first definition in read order wins, and the gap lists which files had one.
|
|
37
37
|
|
|
38
|
-
An anonymous operation is named after its path relative to the directory you passed, plus its operation type, so the name is the same on every machine that checks the repo
|
|
38
|
+
An anonymous operation is named after its path relative to the directory you passed, plus its operation type, so the name is the same on every machine that checks out the repo. A repeated name gets a `#2` suffix, because transition ids are built from the name, and a repeated id would make two operations look like one.
|
|
39
39
|
|
|
40
40
|
## What neither reader does
|
|
41
41
|
|
|
42
|
-
- **Non-root resolvers.**
|
|
42
|
+
- **Non-root resolvers.** The schema reader only produces summaries for `Query`, `Mutation` and `Subscription` fields. A field resolver on `Order.customer` does not get a boundary summary today.
|
|
43
43
|
- **Directives, unions and interface resolution, and custom scalars** beyond the `ref` fallback are not modeled.
|
|
44
|
-
- **Schema validation.** An SDL file that fails to parse comes back as an empty array
|
|
45
|
-
- **
|
|
44
|
+
- **Schema validation.** An SDL file that fails to parse comes back as an empty array, with no error. The documents reader skips a file it cannot parse, so one bad file does not lose the rest.
|
|
45
|
+
- **Checking documents against a schema.** The documents reader never resolves a selected field's type. The checker does that pairing later, against the schema document summary.
|
|
46
46
|
|
|
47
47
|
## Worked example
|
|
48
48
|
|
|
@@ -65,7 +65,7 @@ suss contract --from graphql-documents src/queries -o summaries/operations.json
|
|
|
65
65
|
suss check summaries/schema.json summaries/operations.json
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
Or
|
|
68
|
+
Or from code:
|
|
69
69
|
|
|
70
70
|
```ts
|
|
71
71
|
import {
|
|
@@ -79,7 +79,7 @@ const consumer = graphqlDocumentsPathToSummaries("src/queries");
|
|
|
79
79
|
|
|
80
80
|
## Where it fits in suss
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
The package depends only on `@suss/behavioral-ir`, for the IR types it produces, and `graphql`, for parsing and printing. It does not extract from source code and does not use the language adapters. `@suss/contract-appsync` reuses this package's SDL loader and type conversion for AppSync schemas declared in CloudFormation.
|
|
83
83
|
|
|
84
84
|
## More
|
|
85
85
|
|
package/dist/index.cjs
CHANGED
|
@@ -287,8 +287,8 @@ function buildVariableInputs(op) {
|
|
|
287
287
|
type: "parameter",
|
|
288
288
|
name: variable.variable.name.value,
|
|
289
289
|
position: index,
|
|
290
|
-
//
|
|
291
|
-
//
|
|
290
|
+
// The TypeScript adapter gives an operation's `$variables` the same
|
|
291
|
+
// role, so the checker treats both alike.
|
|
292
292
|
role: "variable",
|
|
293
293
|
shape: typeNodeToShape(variable.type)
|
|
294
294
|
}));
|
|
@@ -335,9 +335,8 @@ function unexpandedSpreadGaps(unexpanded, fragments) {
|
|
|
335
335
|
const gaps = [];
|
|
336
336
|
for (const name of [...unexpanded.missing].sort()) {
|
|
337
337
|
gaps.push(
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
// reader.
|
|
338
|
+
// The fragment is probably defined in a file this reader was not
|
|
339
|
+
// given.
|
|
341
340
|
readingGap(
|
|
342
341
|
`Fragment spread "...${name}" has no matching fragment definition in the read set; its selections are not part of this summary.`
|
|
343
342
|
)
|
|
@@ -579,17 +578,13 @@ function buildResolverSummary(rootType, field, settings, definitions) {
|
|
|
579
578
|
},
|
|
580
579
|
inputs: buildInputs(field),
|
|
581
580
|
transitions: buildTransitions2(ownerKey, field),
|
|
582
|
-
//
|
|
583
|
-
//
|
|
584
|
-
// both sides rather than a name on one.
|
|
581
|
+
// Every type record in the document, so a comparison with the
|
|
582
|
+
// implementation has a structure to compare and not only a type name.
|
|
585
583
|
...Object.keys(definitions).length > 0 ? { definitions } : {},
|
|
586
584
|
gaps: [],
|
|
587
585
|
confidence: { source: "derived", level: "high" },
|
|
588
|
-
//
|
|
589
|
-
//
|
|
590
|
-
// is "derived" because both this metadata and the summary's
|
|
591
|
-
// transitions come from the same SDL field declaration;
|
|
592
|
-
// self-comparison would be tautological.
|
|
586
|
+
// Provenance is "derived" because the transitions come from the same
|
|
587
|
+
// field declaration, so comparing the contract with them finds nothing.
|
|
593
588
|
metadata: (0, import_behavioral_ir2.withGraphqlMetadata)(
|
|
594
589
|
(0, import_behavioral_ir2.withSourceDocumentMetadata)(void 0, { label: source }),
|
|
595
590
|
{
|
package/dist/index.d.cts
CHANGED
|
@@ -1,101 +1,109 @@
|
|
|
1
1
|
import { BehavioralSummary, TypeShape } from '@suss/behavioral-ir';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Reads committed `.graphql` and `.gql` operation documents and writes one
|
|
5
|
+
* client summary per operation, with no call site to trace. The document
|
|
6
|
+
* text goes where the TypeScript adapter puts documents it finds in code,
|
|
7
|
+
* so the checker's GraphQL pairing pass reads both the same way.
|
|
8
|
+
*
|
|
9
|
+
* Fragment spreads are inlined from every file read. The README describes
|
|
10
|
+
* what happens to a spread that cannot be expanded.
|
|
11
|
+
*/
|
|
12
|
+
|
|
3
13
|
interface GraphqlDocumentsOptions {
|
|
4
14
|
/**
|
|
5
15
|
* Recognition tag for the operation binding. Defaults to
|
|
6
|
-
* `"graphql-documents"
|
|
7
|
-
* operations
|
|
16
|
+
* `"graphql-documents"`, so findings can tell these operations apart
|
|
17
|
+
* from operations found at a call site.
|
|
8
18
|
*/
|
|
9
19
|
recognition?: string;
|
|
10
20
|
/**
|
|
11
|
-
* Transport
|
|
12
|
-
* `"http-graphql"
|
|
21
|
+
* Transport recorded on the boundary binding. Defaults to
|
|
22
|
+
* `"http-graphql"`, the same as the schema reader.
|
|
13
23
|
*/
|
|
14
24
|
transport?: string;
|
|
15
25
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* same on every machine that checks the repo out. Without it the
|
|
19
|
-
* path is used as given.
|
|
26
|
+
* The directory an anonymous operation's name is relative to, so the
|
|
27
|
+
* name is the same on every machine. Without it the path is used as is.
|
|
20
28
|
*/
|
|
21
29
|
rootDir?: string;
|
|
22
30
|
}
|
|
23
|
-
/** One
|
|
31
|
+
/** One document's text and the path it came from. */
|
|
24
32
|
interface DocumentSource {
|
|
25
33
|
/** Path recorded on each summary's `location.file`. */
|
|
26
34
|
path: string;
|
|
27
|
-
/** Raw document text. */
|
|
28
35
|
text: string;
|
|
29
36
|
}
|
|
30
37
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* contributes definitions
|
|
38
|
+
* Converts documents already in memory into one summary per operation.
|
|
39
|
+
* Fragments resolve across the whole set, so a file of fragments alone
|
|
40
|
+
* contributes definitions but does not produce a summary.
|
|
34
41
|
*/
|
|
35
42
|
declare function graphqlDocumentsToSummaries(sources: DocumentSource[], options?: GraphqlDocumentsOptions): BehavioralSummary[];
|
|
36
43
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* lose the rest of the set.
|
|
44
|
+
* Reads `.graphql` and `.gql` files and converts them. A file that cannot
|
|
45
|
+
* be read is skipped, so one bad path does not lose the rest.
|
|
40
46
|
*/
|
|
41
47
|
declare function graphqlDocumentFilesToSummaries(filepaths: string[], options?: GraphqlDocumentsOptions): BehavioralSummary[];
|
|
42
48
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
49
|
+
* Reads one document file, or every `.graphql` and `.gql` file under a
|
|
50
|
+
* directory, skipping `node_modules`. Throws when nothing exists at the
|
|
51
|
+
* path. `suss contract --from graphql-documents <path>` calls this.
|
|
46
52
|
*/
|
|
47
53
|
declare function graphqlDocumentsPathToSummaries(specPath: string, options?: GraphqlDocumentsOptions): BehavioralSummary[];
|
|
48
54
|
|
|
49
55
|
/**
|
|
50
|
-
* The five standard GraphQL scalars
|
|
51
|
-
*
|
|
52
|
-
*
|
|
56
|
+
* The TypeShape for each of the five standard GraphQL scalars. A pack
|
|
57
|
+
* whose library spells the same scalars its own way can build on this
|
|
58
|
+
* table instead of copying it.
|
|
53
59
|
*/
|
|
54
60
|
declare const SCALAR_SHAPES: Record<string, TypeShape>;
|
|
55
|
-
/**
|
|
61
|
+
/** True when the document declares a type, which makes it a schema. */
|
|
56
62
|
declare function describesTypes(text: string): boolean;
|
|
57
|
-
/**
|
|
63
|
+
/** True when the document has at least one operation. A file of fragments has none. */
|
|
58
64
|
declare function describesOperations(text: string): boolean;
|
|
59
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Reads a plain GraphQL SDL file and writes one resolver summary for each
|
|
68
|
+
* field on Query, Mutation and Subscription. @suss/contract-appsync does
|
|
69
|
+
* the same for AppSync schemas declared in CloudFormation. The README
|
|
70
|
+
* describes what each summary contains.
|
|
71
|
+
*/
|
|
72
|
+
|
|
60
73
|
interface GraphqlContractOptions {
|
|
61
74
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* the SDL came from a non-file source (in-memory string).
|
|
75
|
+
* The path recorded on each summary's `location.file`. Defaults to
|
|
76
|
+
* `"graphql"`, for SDL passed in as a string with no file behind it.
|
|
65
77
|
*/
|
|
66
78
|
source?: string;
|
|
67
79
|
/**
|
|
68
80
|
* Recognition tag for the resolver binding. Defaults to `"graphql"`.
|
|
69
|
-
*
|
|
70
|
-
* (`apollo-prod`, `apollo-staging`)
|
|
71
|
-
* distinguish them.
|
|
81
|
+
* When several deployments serve the same SDL, give each its own tag
|
|
82
|
+
* (`apollo-prod`, `apollo-staging`) so findings tell them apart.
|
|
72
83
|
*/
|
|
73
84
|
recognition?: string;
|
|
74
85
|
/**
|
|
75
|
-
* Transport
|
|
76
|
-
* `"http-graphql"
|
|
86
|
+
* Transport recorded on the boundary binding. Defaults to
|
|
87
|
+
* `"http-graphql"`, since most GraphQL servers are served over HTTP.
|
|
77
88
|
*/
|
|
78
89
|
transport?: string;
|
|
79
90
|
}
|
|
80
91
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
92
|
+
* Converts SDL text into one resolver summary per root field, plus one
|
|
93
|
+
* summary for the schema document. Returns an empty array when the SDL
|
|
94
|
+
* does not parse or has no root fields.
|
|
84
95
|
*/
|
|
85
96
|
declare function graphqlSdlToSummaries(sdl: string, options?: GraphqlContractOptions): BehavioralSummary[];
|
|
86
97
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
98
|
+
* Reads an SDL file and converts it, recording the file path as the
|
|
99
|
+
* source. `suss contract --from graphql <file>` calls this.
|
|
89
100
|
*/
|
|
90
101
|
declare function graphqlSdlFileToSummaries(filepath: string, options?: GraphqlContractOptions): BehavioralSummary[];
|
|
91
102
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* SAM template's `DefinitionS3Location` / `SchemaUri`. Reading fails
|
|
97
|
-
* best-effort (missing file, permission error) so the caller can record
|
|
98
|
-
* an unresolved-schema gap instead of throwing.
|
|
103
|
+
* Reads an SDL file and returns its text, or `null` when the file cannot
|
|
104
|
+
* be read. @suss/contract-appsync uses it for the schema file a template
|
|
105
|
+
* points at through `DefinitionS3Location` or `SchemaUri`, and records a
|
|
106
|
+
* gap on `null` so a missing file does not fail the run.
|
|
99
107
|
*/
|
|
100
108
|
declare function loadSdlFile(filepath: string): string | null;
|
|
101
109
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,101 +1,109 @@
|
|
|
1
1
|
import { BehavioralSummary, TypeShape } from '@suss/behavioral-ir';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Reads committed `.graphql` and `.gql` operation documents and writes one
|
|
5
|
+
* client summary per operation, with no call site to trace. The document
|
|
6
|
+
* text goes where the TypeScript adapter puts documents it finds in code,
|
|
7
|
+
* so the checker's GraphQL pairing pass reads both the same way.
|
|
8
|
+
*
|
|
9
|
+
* Fragment spreads are inlined from every file read. The README describes
|
|
10
|
+
* what happens to a spread that cannot be expanded.
|
|
11
|
+
*/
|
|
12
|
+
|
|
3
13
|
interface GraphqlDocumentsOptions {
|
|
4
14
|
/**
|
|
5
15
|
* Recognition tag for the operation binding. Defaults to
|
|
6
|
-
* `"graphql-documents"
|
|
7
|
-
* operations
|
|
16
|
+
* `"graphql-documents"`, so findings can tell these operations apart
|
|
17
|
+
* from operations found at a call site.
|
|
8
18
|
*/
|
|
9
19
|
recognition?: string;
|
|
10
20
|
/**
|
|
11
|
-
* Transport
|
|
12
|
-
* `"http-graphql"
|
|
21
|
+
* Transport recorded on the boundary binding. Defaults to
|
|
22
|
+
* `"http-graphql"`, the same as the schema reader.
|
|
13
23
|
*/
|
|
14
24
|
transport?: string;
|
|
15
25
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* same on every machine that checks the repo out. Without it the
|
|
19
|
-
* path is used as given.
|
|
26
|
+
* The directory an anonymous operation's name is relative to, so the
|
|
27
|
+
* name is the same on every machine. Without it the path is used as is.
|
|
20
28
|
*/
|
|
21
29
|
rootDir?: string;
|
|
22
30
|
}
|
|
23
|
-
/** One
|
|
31
|
+
/** One document's text and the path it came from. */
|
|
24
32
|
interface DocumentSource {
|
|
25
33
|
/** Path recorded on each summary's `location.file`. */
|
|
26
34
|
path: string;
|
|
27
|
-
/** Raw document text. */
|
|
28
35
|
text: string;
|
|
29
36
|
}
|
|
30
37
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* contributes definitions
|
|
38
|
+
* Converts documents already in memory into one summary per operation.
|
|
39
|
+
* Fragments resolve across the whole set, so a file of fragments alone
|
|
40
|
+
* contributes definitions but does not produce a summary.
|
|
34
41
|
*/
|
|
35
42
|
declare function graphqlDocumentsToSummaries(sources: DocumentSource[], options?: GraphqlDocumentsOptions): BehavioralSummary[];
|
|
36
43
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* lose the rest of the set.
|
|
44
|
+
* Reads `.graphql` and `.gql` files and converts them. A file that cannot
|
|
45
|
+
* be read is skipped, so one bad path does not lose the rest.
|
|
40
46
|
*/
|
|
41
47
|
declare function graphqlDocumentFilesToSummaries(filepaths: string[], options?: GraphqlDocumentsOptions): BehavioralSummary[];
|
|
42
48
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
49
|
+
* Reads one document file, or every `.graphql` and `.gql` file under a
|
|
50
|
+
* directory, skipping `node_modules`. Throws when nothing exists at the
|
|
51
|
+
* path. `suss contract --from graphql-documents <path>` calls this.
|
|
46
52
|
*/
|
|
47
53
|
declare function graphqlDocumentsPathToSummaries(specPath: string, options?: GraphqlDocumentsOptions): BehavioralSummary[];
|
|
48
54
|
|
|
49
55
|
/**
|
|
50
|
-
* The five standard GraphQL scalars
|
|
51
|
-
*
|
|
52
|
-
*
|
|
56
|
+
* The TypeShape for each of the five standard GraphQL scalars. A pack
|
|
57
|
+
* whose library spells the same scalars its own way can build on this
|
|
58
|
+
* table instead of copying it.
|
|
53
59
|
*/
|
|
54
60
|
declare const SCALAR_SHAPES: Record<string, TypeShape>;
|
|
55
|
-
/**
|
|
61
|
+
/** True when the document declares a type, which makes it a schema. */
|
|
56
62
|
declare function describesTypes(text: string): boolean;
|
|
57
|
-
/**
|
|
63
|
+
/** True when the document has at least one operation. A file of fragments has none. */
|
|
58
64
|
declare function describesOperations(text: string): boolean;
|
|
59
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Reads a plain GraphQL SDL file and writes one resolver summary for each
|
|
68
|
+
* field on Query, Mutation and Subscription. @suss/contract-appsync does
|
|
69
|
+
* the same for AppSync schemas declared in CloudFormation. The README
|
|
70
|
+
* describes what each summary contains.
|
|
71
|
+
*/
|
|
72
|
+
|
|
60
73
|
interface GraphqlContractOptions {
|
|
61
74
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* the SDL came from a non-file source (in-memory string).
|
|
75
|
+
* The path recorded on each summary's `location.file`. Defaults to
|
|
76
|
+
* `"graphql"`, for SDL passed in as a string with no file behind it.
|
|
65
77
|
*/
|
|
66
78
|
source?: string;
|
|
67
79
|
/**
|
|
68
80
|
* Recognition tag for the resolver binding. Defaults to `"graphql"`.
|
|
69
|
-
*
|
|
70
|
-
* (`apollo-prod`, `apollo-staging`)
|
|
71
|
-
* distinguish them.
|
|
81
|
+
* When several deployments serve the same SDL, give each its own tag
|
|
82
|
+
* (`apollo-prod`, `apollo-staging`) so findings tell them apart.
|
|
72
83
|
*/
|
|
73
84
|
recognition?: string;
|
|
74
85
|
/**
|
|
75
|
-
* Transport
|
|
76
|
-
* `"http-graphql"
|
|
86
|
+
* Transport recorded on the boundary binding. Defaults to
|
|
87
|
+
* `"http-graphql"`, since most GraphQL servers are served over HTTP.
|
|
77
88
|
*/
|
|
78
89
|
transport?: string;
|
|
79
90
|
}
|
|
80
91
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
92
|
+
* Converts SDL text into one resolver summary per root field, plus one
|
|
93
|
+
* summary for the schema document. Returns an empty array when the SDL
|
|
94
|
+
* does not parse or has no root fields.
|
|
84
95
|
*/
|
|
85
96
|
declare function graphqlSdlToSummaries(sdl: string, options?: GraphqlContractOptions): BehavioralSummary[];
|
|
86
97
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
98
|
+
* Reads an SDL file and converts it, recording the file path as the
|
|
99
|
+
* source. `suss contract --from graphql <file>` calls this.
|
|
89
100
|
*/
|
|
90
101
|
declare function graphqlSdlFileToSummaries(filepath: string, options?: GraphqlContractOptions): BehavioralSummary[];
|
|
91
102
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* SAM template's `DefinitionS3Location` / `SchemaUri`. Reading fails
|
|
97
|
-
* best-effort (missing file, permission error) so the caller can record
|
|
98
|
-
* an unresolved-schema gap instead of throwing.
|
|
103
|
+
* Reads an SDL file and returns its text, or `null` when the file cannot
|
|
104
|
+
* be read. @suss/contract-appsync uses it for the schema file a template
|
|
105
|
+
* points at through `DefinitionS3Location` or `SchemaUri`, and records a
|
|
106
|
+
* gap on `null` so a missing file does not fail the run.
|
|
99
107
|
*/
|
|
100
108
|
declare function loadSdlFile(filepath: string): string | null;
|
|
101
109
|
|
package/dist/index.js
CHANGED
|
@@ -262,8 +262,8 @@ function buildVariableInputs(op) {
|
|
|
262
262
|
type: "parameter",
|
|
263
263
|
name: variable.variable.name.value,
|
|
264
264
|
position: index,
|
|
265
|
-
//
|
|
266
|
-
//
|
|
265
|
+
// The TypeScript adapter gives an operation's `$variables` the same
|
|
266
|
+
// role, so the checker treats both alike.
|
|
267
267
|
role: "variable",
|
|
268
268
|
shape: typeNodeToShape(variable.type)
|
|
269
269
|
}));
|
|
@@ -310,9 +310,8 @@ function unexpandedSpreadGaps(unexpanded, fragments) {
|
|
|
310
310
|
const gaps = [];
|
|
311
311
|
for (const name of [...unexpanded.missing].sort()) {
|
|
312
312
|
gaps.push(
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
// reader.
|
|
313
|
+
// The fragment is probably defined in a file this reader was not
|
|
314
|
+
// given.
|
|
316
315
|
readingGap(
|
|
317
316
|
`Fragment spread "...${name}" has no matching fragment definition in the read set; its selections are not part of this summary.`
|
|
318
317
|
)
|
|
@@ -554,17 +553,13 @@ function buildResolverSummary(rootType, field, settings, definitions) {
|
|
|
554
553
|
},
|
|
555
554
|
inputs: buildInputs(field),
|
|
556
555
|
transitions: buildTransitions2(ownerKey, field),
|
|
557
|
-
//
|
|
558
|
-
//
|
|
559
|
-
// both sides rather than a name on one.
|
|
556
|
+
// Every type record in the document, so a comparison with the
|
|
557
|
+
// implementation has a structure to compare and not only a type name.
|
|
560
558
|
...Object.keys(definitions).length > 0 ? { definitions } : {},
|
|
561
559
|
gaps: [],
|
|
562
560
|
confidence: { source: "derived", level: "high" },
|
|
563
|
-
//
|
|
564
|
-
//
|
|
565
|
-
// is "derived" because both this metadata and the summary's
|
|
566
|
-
// transitions come from the same SDL field declaration;
|
|
567
|
-
// self-comparison would be tautological.
|
|
561
|
+
// Provenance is "derived" because the transitions come from the same
|
|
562
|
+
// field declaration, so comparing the contract with them finds nothing.
|
|
568
563
|
metadata: withGraphqlMetadata2(
|
|
569
564
|
withSourceDocumentMetadata(void 0, { label: source }),
|
|
570
565
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/contract-graphql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Turn a GraphQL schema or a committed operation document into a suss contract and check it against the resolvers and clients on either side.",
|
|
6
6
|
"keywords": [
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"license": "Apache-2.0",
|
|
16
16
|
"author": "Nimbus Cloud AI LLC",
|
|
17
|
-
"homepage": "https://
|
|
17
|
+
"homepage": "https://suss.sh",
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
20
|
"url": "git+https://github.com/nimbuscloud-ai/suss.git",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"typecheck": "tsc --noEmit"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@suss/behavioral-ir": "0.
|
|
50
|
+
"@suss/behavioral-ir": "0.34.0",
|
|
51
51
|
"graphql": "^16.8.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|