Skip to content

Commit 971a394

Browse files
authored
Merge pull request #826 from aws-amplify/main
chore: release changes needed to support Gen2 CLI
2 parents 04919af + 22f1425 commit 971a394

File tree

60 files changed

+12227
-381
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+12227
-381
lines changed

.codebuild/e2e_workflow.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -145,25 +145,25 @@ batch:
145145
depend-on:
146146
- publish_to_local_registry
147147
- identifier: >-
148-
l_build_app_ts_push_codegen_admin_modelgen_uninitialized_project_codegen_js_uninitialized_project_modelgen_android
148+
l_build_app_ts_graphql_generator_app_push_codegen_admin_modelgen_uninitialized_project_codegen_js
149149
buildspec: .codebuild/run_e2e_tests.yml
150150
env:
151151
compute-type: BUILD_GENERAL1_LARGE
152152
variables:
153153
TEST_SUITE: >-
154-
src/__tests__/build-app-ts.test.ts|src/__tests__/push-codegen-admin-modelgen.test.ts|src/__tests__/uninitialized-project-codegen-js.test.ts|src/__tests__/uninitialized-project-modelgen-android.test.ts
154+
src/__tests__/build-app-ts.test.ts|src/__tests__/graphql-generator-app.test.ts|src/__tests__/push-codegen-admin-modelgen.test.ts|src/__tests__/uninitialized-project-codegen-js.test.ts
155155
CLI_REGION: ap-southeast-1
156156
DISABLE_ESLINT_PLUGIN: true
157157
depend-on:
158158
- publish_to_local_registry
159159
- identifier: >-
160-
l_uninitialized_project_modelgen_flutter_uninitialized_project_modelgen_ios_uninitialized_project_modelgen_js
160+
l_uninitialized_project_modelgen_android_uninitialized_project_modelgen_flutter_uninitialized_project_modelgen_ios_uninitialize
161161
buildspec: .codebuild/run_e2e_tests.yml
162162
env:
163163
compute-type: BUILD_GENERAL1_LARGE
164164
variables:
165165
TEST_SUITE: >-
166-
src/__tests__/uninitialized-project-modelgen-flutter.test.ts|src/__tests__/uninitialized-project-modelgen-ios.test.ts|src/__tests__/uninitialized-project-modelgen-js.test.ts
166+
src/__tests__/uninitialized-project-modelgen-android.test.ts|src/__tests__/uninitialized-project-modelgen-flutter.test.ts|src/__tests__/uninitialized-project-modelgen-ios.test.ts|src/__tests__/uninitialized-project-modelgen-js.test.ts
167167
CLI_REGION: ap-southeast-2
168168
depend-on:
169169
- publish_to_local_registry
@@ -252,30 +252,30 @@ batch:
252252
- publish_to_local_registry
253253
- build_windows
254254
- identifier: >-
255-
w_build_app_ts_push_codegen_admin_modelgen_uninitialized_project_codegen_js_uninitialized_project_modelgen_android
255+
w_build_app_ts_graphql_generator_app_push_codegen_admin_modelgen_uninitialized_project_codegen_js
256256
buildspec: .codebuild/run_e2e_tests_windows.yml
257257
env:
258258
compute-type: BUILD_GENERAL1_LARGE
259259
image: $WINDOWS_IMAGE_2019
260260
type: WINDOWS_SERVER_2019_CONTAINER
261261
variables:
262262
TEST_SUITE: >-
263-
src/__tests__/build-app-ts.test.ts|src/__tests__/push-codegen-admin-modelgen.test.ts|src/__tests__/uninitialized-project-codegen-js.test.ts|src/__tests__/uninitialized-project-modelgen-android.test.ts
263+
src/__tests__/build-app-ts.test.ts|src/__tests__/graphql-generator-app.test.ts|src/__tests__/push-codegen-admin-modelgen.test.ts|src/__tests__/uninitialized-project-codegen-js.test.ts
264264
CLI_REGION: us-east-1
265265
DISABLE_ESLINT_PLUGIN: true
266266
depend-on:
267267
- publish_to_local_registry
268268
- build_windows
269269
- identifier: >-
270-
w_uninitialized_project_modelgen_flutter_uninitialized_project_modelgen_ios_uninitialized_project_modelgen_js
270+
w_uninitialized_project_modelgen_android_uninitialized_project_modelgen_flutter_uninitialized_project_modelgen_ios_uninitialize
271271
buildspec: .codebuild/run_e2e_tests_windows.yml
272272
env:
273273
compute-type: BUILD_GENERAL1_LARGE
274274
image: $WINDOWS_IMAGE_2019
275275
type: WINDOWS_SERVER_2019_CONTAINER
276276
variables:
277277
TEST_SUITE: >-
278-
src/__tests__/uninitialized-project-modelgen-flutter.test.ts|src/__tests__/uninitialized-project-modelgen-ios.test.ts|src/__tests__/uninitialized-project-modelgen-js.test.ts
278+
src/__tests__/uninitialized-project-modelgen-android.test.ts|src/__tests__/uninitialized-project-modelgen-flutter.test.ts|src/__tests__/uninitialized-project-modelgen-ios.test.ts|src/__tests__/uninitialized-project-modelgen-js.test.ts
279279
CLI_REGION: us-east-1
280280
depend-on:
281281
- publish_to_local_registry

.eslintrc.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ module.exports = {
268268
'/packages/*/CHANGELOG.md',
269269

270270
// Ignore lint in e2e test apps
271-
'test-apps'
271+
'test-apps',
272+
273+
// Ignore lint for standalone JSON validation function
274+
'/packages/appsync-modelgen-plugin/src/validate-cjs.js'
272275
]
273276
};

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"eslint.packageManager": "yarn",
1515
"eslint.quiet": true,
1616
"editor.codeActionsOnSave": {
17-
"source.fixAll.eslint": true
17+
"source.fixAll.eslint": "explicit"
1818
},
1919
"jest.enableInlineErrorMessages": true,
2020
"jest.showCoverageOnLoad": true,

README-relationships.md

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
# Modeling relationships in the introspection schema
2+
3+
## Background
4+
5+
The Model Introspection Schema (MIS) is an intermediate representation of the GraphQL model that includes Amplify annotations. It is different
6+
from the standard [GraphQL introspection schema](https://graphql.org/learn/introspection/) in that it includes relationship information, not
7+
just type information.
8+
9+
> **NOTE:** The MIS is an internal implementation detail of the Amplify API plugin. It should not be used in a customer application.
10+
11+
## Sample
12+
13+
Given a schema like
14+
15+
```graphql
16+
type Primary @model @auth(rules: [{ allow: public, operations: [read] }, { allow: owner }]) {
17+
id: ID! @primaryKey
18+
relatedMany: [RelatedMany] @hasMany(references: "primaryId")
19+
relatedOne: RelatedOne @hasOne(references: "primaryId")
20+
}
21+
22+
type RelatedMany @model @auth(rules: [{ allow: public, operations: [read] }, { allow: owner }]) {
23+
id: ID! @primaryKey
24+
primaryId: ID!
25+
primary: Primary @belongsTo(references: "primaryId")
26+
}
27+
28+
type RelatedOne @model @auth(rules: [{ allow: public, operations: [read] }, { allow: owner }]) {
29+
id: ID! @primaryKey
30+
primaryId: ID!
31+
primary: Primary @belongsTo(references: "primaryId")
32+
}
33+
```
34+
35+
the MIS (abridged to show relationship information only) looks like:
36+
37+
```json
38+
{
39+
"version": 1,
40+
"models": {
41+
"Primary": {
42+
"name": "Primary",
43+
"fields": {
44+
"relatedMany": {
45+
"name": "relatedMany",
46+
"isArray": true,
47+
"type": {
48+
"model": "RelatedMany"
49+
},
50+
"isRequired": false,
51+
"attributes": [],
52+
"isArrayNullable": true,
53+
"association": {
54+
"connectionType": "HAS_MANY",
55+
"associatedWith": ["primaryId"]
56+
}
57+
},
58+
"relatedOne": {
59+
"name": "relatedOne",
60+
"isArray": false,
61+
"type": {
62+
"model": "RelatedOne"
63+
},
64+
"isRequired": false,
65+
"attributes": [],
66+
"association": {
67+
"connectionType": "HAS_ONE",
68+
"associatedWith": ["primaryId"],
69+
}
70+
}
71+
}
72+
},
73+
"RelatedMany": {
74+
"name": "RelatedMany",
75+
"fields": {
76+
"primary": {
77+
"name": "primary",
78+
"isArray": false,
79+
"type": {
80+
"model": "Primary"
81+
},
82+
"isRequired": false,
83+
"attributes": [],
84+
"association": {
85+
"connectionType": "BELONGS_TO",
86+
"targetNames": ["primaryId"]
87+
}
88+
},
89+
"primaryId": {
90+
"name": "primaryId",
91+
"isArray": false,
92+
"type": "ID",
93+
"isRequired": false,
94+
"attributes": []
95+
}
96+
}
97+
},
98+
"RelatedOne": {
99+
"name": "RelatedOne",
100+
"fields": {
101+
"primary": {
102+
"name": "primary",
103+
"isArray": false,
104+
"type": {
105+
"model": "Primary"
106+
},
107+
"isRequired": false,
108+
"attributes": [],
109+
"association": {
110+
"connectionType": "BELONGS_TO",
111+
"targetNames": ["primaryId"]
112+
}
113+
},
114+
"primaryId": {
115+
"name": "primaryId",
116+
"isArray": false,
117+
"type": "ID",
118+
"isRequired": false,
119+
"attributes": []
120+
}
121+
}
122+
}
123+
}
124+
}
125+
```
126+
127+
## Glossary
128+
129+
* **Associated type** - In a field decorated with a `@hasMany`, `@hasOne`, or `@belongsTo` directive, the model “pointed to” by the directive. In the sample schema:
130+
* `Related` is the **associated type** for the `@hasMany` directive on `Primary.related`
131+
* `Primary` is the **associated type** for the `@belongsTo` directive on `Related.primary`
132+
* **Association field** - See **Connection field**
133+
* **Connection field** - In any model type, the field that is decorated with a `@hasMany`, `@hasOne`, or `@belongsTo` directive. In the sample schema:
134+
* `Primary.related` is the **connection field** in the `Primary` model, for the relationship `Primary -> Related` defined by the `@hasMany` on `Primary.related` and the `@belongsTo` on `Related.primary`
135+
* `Related.primary` is the **connection field** in the `Related` model, for the relationship `Primary -> Related` defined by the `@hasMany` on `Primary.related` and the `@belongsTo` on `Related.primary`
136+
* **Source type** - In a field decorated with a `@hasMany`, `@hasOne`, or `@belongsTo` directive, the model containing the directive. In the sample schema:
137+
* `Primary` is the **source type** for the `@hasMany` directive on `Primary.related`
138+
* `Related` is the **source type** for the `@belongsTo` directive on `Related.primary`
139+
140+
## Structure
141+
142+
Relationships are modeled in an `association` structure in the MIS. The `association` attribute must belong to a `@model` field, not a field of non-model type, enum, input, or custom query/mutation.
143+
144+
Here are the relevant types to define the association structure. Note that this is a simplified rendition of the JSON/JavaScript version of the MIS. Other platforms may represent the MIS differently. The full definition is in [source code](./appsync-modelgen-plugin/src/utils/process-connections.ts);
145+
146+
```ts
147+
enum CodeGenConnectionType {
148+
HAS_ONE = 'HAS_ONE',
149+
BELONGS_TO = 'BELONGS_TO',
150+
HAS_MANY = 'HAS_MANY',
151+
}
152+
153+
type CodeGenConnectionTypeBase = {
154+
kind: CodeGenConnectionType;
155+
connectedModel: CodeGenModel;
156+
// ^-- Type not shown
157+
};
158+
159+
type CodeGenFieldConnectionBelongsTo = CodeGenConnectionTypeBase & {
160+
kind: CodeGenConnectionType.BELONGS_TO;
161+
targetNames: string[];
162+
}
163+
164+
type CodeGenFieldConnectionHasOne = CodeGenConnectionTypeBase & {
165+
kind: CodeGenConnectionType.HAS_ONE;
166+
associatedWith: CodeGenField[];
167+
// ^-- Type not shown -- rendered in MIS as a string array
168+
targetNames: string[];
169+
}
170+
171+
export type CodeGenFieldConnectionHasMany = CodeGenConnectionTypeBase & {
172+
kind: CodeGenConnectionType.HAS_MANY;
173+
associatedWith: CodeGenField[];
174+
// ^-- Type not shown -- rendered in MIS as a string array
175+
}
176+
```
177+
178+
Considering a snippet of the above sample:
179+
180+
```json
181+
"models": {
182+
"Primary": {
183+
"name": "Primary",
184+
"fields": {
185+
"relatedMany": {
186+
"name": "relatedMany",
187+
"isArray": true,
188+
"type": {
189+
"model": "RelatedMany"
190+
},
191+
"isRequired": false,
192+
"attributes": [],
193+
"isArrayNullable": true,
194+
"association": {
195+
"connectionType": "HAS_MANY",
196+
"associatedWith": ["primaryId"]
197+
}
198+
},
199+
...
200+
"RelatedMany": {
201+
"name": "RelatedMany",
202+
"fields": {
203+
"primary": {
204+
"name": "primary",
205+
"isArray": false,
206+
"type": {
207+
"model": "Primary"
208+
},
209+
"isRequired": false,
210+
"attributes": [],
211+
"association": {
212+
"connectionType": "BELONGS_TO",
213+
"targetNames": ["primaryId"]
214+
}
215+
},
216+
"primaryId": {
217+
"name": "primaryId",
218+
"isArray": false,
219+
"type": "ID",
220+
"isRequired": false,
221+
"attributes": []
222+
}
223+
```
224+
225+
- `models.Primary` - A type definition. The **source type** for any `association`s defined in this model.
226+
- `models.Primary.fields.relatedMany` - The **association field**/**connection field**
227+
- `models.Primary.fields.relatedMany.type` - The **associated type** for this relationship. This must be a `@model`.
228+
- `models.Primary.fields.relatedMany.association` - The structure containing the data needed to navigate the relationship with the associated type
229+
- `models.Primary.fields.relatedMany.association.connectionType` - The kind of relationship (has one, has many, belongs to) this **source type** has with the associated type
230+
- `models.Primary.fields.relatedMany.association.associatedWith` - A list of fields on the **associated type** that hold the primary key of the **source** record. This is an array so we can support composite primary keys.
231+
- `models.RelatedMany` - A type definition. The **source type** for any `association`s defined in this model.
232+
- `models.RelatedMany.fields.primary.association.targetNames` - A list of fields on the **source type** (that is, the current type) that hold the primary key of the **associated** record. This is an array so we can support composite primary keys.
233+
- `models.RelatedMany.fields.primaryId` - The field pointed to by `targetNames` above, containing the primary key of the **associated** record for the `RelatedOne.primary` relationship.
234+
235+
236+
## Navigating relationships
237+
238+
We will describe the steps to resolve the record in pseudo-sql
239+
240+
### From source record to associated record
241+
242+
* If the source model has an `associatedWith` but no `targetNames`:
243+
```
244+
SELECT *
245+
FROM <associated type>
246+
WHERE <associatedWith fields> = <source type>.primaryKey
247+
```
248+
* If the source model has an `associatedWith` AND `targetNames`:
249+
```
250+
SELECT *
251+
FROM <associated type>
252+
WHERE <associatedWith fields> = <source type>.<targetNames fields>
253+
```
254+
* If the source model has a `targetNames` but no `associatedWith`:
255+
```
256+
SELECT *
257+
FROM <associated type>
258+
WHERE <source type>.<targetNames fields> = <associated type>.primaryKey
259+
```
260+
261+
262+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { DEFAULT_JS_CONFIG, craInstall, craBuild, cypressRun, isWindows } from '@aws-amplify/amplify-codegen-e2e-core';
2+
import path from 'path';
3+
4+
describe('GraphQL documents generator e2e tests', () => {
5+
let apiName: string;
6+
const projectRoot = path.resolve('test-apps', 'graphql-generator-app');
7+
const config = DEFAULT_JS_CONFIG;
8+
9+
beforeAll(async () => {
10+
await craInstall(projectRoot, { ...config });
11+
});
12+
13+
// skip cypress test on windows
14+
(isWindows() ? it.skip : it)('graphql generator does not crash in browser', async () => {
15+
await cypressRun(projectRoot, { componentsTesting: true });
16+
});
17+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
14+
# misc
15+
.DS_Store
16+
.env.local
17+
.env.development.local
18+
.env.test.local
19+
.env.production.local
20+
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*

0 commit comments

Comments
 (0)