@aws-amplify/amplify-util-uibuilder 1.9.0-replace-lambda-callouts-branch.0 → 1.9.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/CHANGELOG.md +40 -9
- package/lib/clients/amplify-studio-client.d.ts +5 -1
- package/lib/clients/amplify-studio-client.d.ts.map +1 -1
- package/lib/clients/amplify-studio-client.js +54 -8
- package/lib/clients/amplify-studio-client.js.map +1 -1
- package/lib/commands/generateComponents.d.ts.map +1 -1
- package/lib/commands/generateComponents.js +33 -34
- package/lib/commands/generateComponents.js.map +1 -1
- package/lib/commands/utils/codegenResources.d.ts +1 -15
- package/lib/commands/utils/codegenResources.d.ts.map +1 -1
- package/lib/commands/utils/codegenResources.js +1 -122
- package/lib/commands/utils/codegenResources.js.map +1 -1
- package/lib/commands/utils/detachedFormCleanup.d.ts +3 -4
- package/lib/commands/utils/detachedFormCleanup.d.ts.map +1 -1
- package/lib/commands/utils/detachedFormCleanup.js.map +1 -1
- package/lib/commands/utils/storageManager.d.ts +3 -4
- package/lib/commands/utils/storageManager.d.ts.map +1 -1
- package/lib/commands/utils/storageManager.js +1 -5
- package/lib/commands/utils/storageManager.js.map +1 -1
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts +8 -16
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts.map +1 -1
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.js +186 -74
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.js.map +1 -1
- package/package.json +28 -19
- package/src/__tests__/__snapshots__/syncAmplifyUiComponents.test.ts.snap +46 -0
- package/src/__tests__/cloneComponentsFromEnv.test.ts +8 -0
- package/src/__tests__/detachedFormCleanup.test.ts +7 -5
- package/src/__tests__/generateComponents.test.ts +187 -15
- package/src/__tests__/getAmplifyDataSchema.test.ts +1 -2
- package/src/__tests__/prePushHandler.test.ts +7 -0
- package/src/__tests__/storageManager.test.ts +7 -4
- package/src/__tests__/syncAmplifyUiComponents.test.ts +106 -95
- package/src/clients/amplify-studio-client.ts +56 -9
- package/src/commands/generateComponents.ts +45 -56
- package/src/commands/utils/codegenResources.ts +1 -225
- package/src/commands/utils/detachedFormCleanup.ts +3 -4
- package/src/commands/utils/storageManager.ts +3 -7
- package/src/commands/utils/syncAmplifyUiBuilderComponents.ts +210 -123
- package/tsconfig.tsbuildinfo +1 -1
- package/src/__tests__/createUiBuilderComponent.test.ts +0 -90
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { $TSContext, CloudformationProviderFacade } from '@aws-amplify/amplify-cli-core';
|
|
2
2
|
import type { ServiceConfigurationOptions } from 'aws-sdk/lib/service';
|
|
3
|
-
import type { CreateComponentData, Component, Theme, Form } from 'aws-sdk/clients/amplifyuibuilder';
|
|
3
|
+
import type { CreateComponentData, Component, Theme, Form, StartCodegenJobData } from 'aws-sdk/clients/amplifyuibuilder';
|
|
4
4
|
import { AmplifyUIBuilder, AmplifyBackend } from 'aws-sdk';
|
|
5
5
|
import { printer } from '@aws-amplify/amplify-prompts';
|
|
6
6
|
import { getAppId, getEnvName } from '../commands/utils/environmentHelpers';
|
|
7
7
|
import { getTransformerVersion } from '../commands/utils/featureFlags';
|
|
8
|
+
import { isDataStoreEnabled } from '@aws-amplify/amplify-category-api';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* studio client metadata
|
|
@@ -60,6 +61,7 @@ export default class AmplifyStudioClient {
|
|
|
60
61
|
#envName: string;
|
|
61
62
|
metadata: StudioMetadata;
|
|
62
63
|
isGraphQLSupported = false;
|
|
64
|
+
isDataStoreEnabled = false;
|
|
63
65
|
|
|
64
66
|
/**
|
|
65
67
|
* static function meant to check if given appId is studio enabled
|
|
@@ -82,11 +84,14 @@ export default class AmplifyStudioClient {
|
|
|
82
84
|
static async setClientInfo(context: $TSContext, envName?: string, appId?: string): Promise<AmplifyStudioClient> {
|
|
83
85
|
const resolvedEnvName = getEnvName(context, envName);
|
|
84
86
|
const resolvedAppId = getAppId(context, appId);
|
|
85
|
-
const awsConfigInfo =
|
|
86
|
-
context,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
const [awsConfigInfo, dataStoreStatus] = await Promise.all([
|
|
88
|
+
context.amplify.invokePluginMethod(context, 'awscloudformation', undefined, 'loadConfigurationForEnv', [
|
|
89
|
+
context,
|
|
90
|
+
resolvedEnvName,
|
|
91
|
+
resolvedAppId,
|
|
92
|
+
]) as ServiceConfigurationOptions,
|
|
93
|
+
isDataStoreEnabled(context),
|
|
94
|
+
]);
|
|
90
95
|
|
|
91
96
|
const client = new AmplifyStudioClient(awsConfigInfo, resolvedAppId, resolvedEnvName);
|
|
92
97
|
|
|
@@ -97,6 +102,8 @@ export default class AmplifyStudioClient {
|
|
|
97
102
|
client.isGraphQLSupported = false;
|
|
98
103
|
}
|
|
99
104
|
|
|
105
|
+
client.isDataStoreEnabled = dataStoreStatus;
|
|
106
|
+
|
|
100
107
|
return client;
|
|
101
108
|
}
|
|
102
109
|
|
|
@@ -164,7 +171,6 @@ export default class AmplifyStudioClient {
|
|
|
164
171
|
uiBuilderComponents.push(...response.entities);
|
|
165
172
|
nextToken = response.nextToken;
|
|
166
173
|
} while (nextToken);
|
|
167
|
-
printer.debug(JSON.stringify(uiBuilderComponents, null, 2));
|
|
168
174
|
return { entities: uiBuilderComponents };
|
|
169
175
|
} catch (err) {
|
|
170
176
|
throw new Error(`Failed to list components: ${err.message}`);
|
|
@@ -192,7 +198,6 @@ export default class AmplifyStudioClient {
|
|
|
192
198
|
uiBuilderThemes.push(...response.entities);
|
|
193
199
|
nextToken = response.nextToken;
|
|
194
200
|
} while (nextToken);
|
|
195
|
-
printer.debug(JSON.stringify(uiBuilderThemes, null, 2));
|
|
196
201
|
return { entities: uiBuilderThemes };
|
|
197
202
|
} catch (err) {
|
|
198
203
|
throw new Error(`Failed to list themes: ${err.message}`);
|
|
@@ -216,7 +221,6 @@ export default class AmplifyStudioClient {
|
|
|
216
221
|
uibuilderForms.push(...response.entities);
|
|
217
222
|
nextToken = response.nextToken;
|
|
218
223
|
} while (nextToken);
|
|
219
|
-
printer.debug(JSON.stringify(uibuilderForms, null, 2));
|
|
220
224
|
return { entities: uibuilderForms };
|
|
221
225
|
} catch (err) {
|
|
222
226
|
throw new Error(`Failed to list forms: ${err.message}`);
|
|
@@ -267,4 +271,47 @@ export default class AmplifyStudioClient {
|
|
|
267
271
|
throw new Error(`Models not found in AmplifyBackend:GetBackendAPIModels response: ${err.message}`);
|
|
268
272
|
}
|
|
269
273
|
};
|
|
274
|
+
|
|
275
|
+
startCodegenJob = async (codegenJobToCreate: StartCodegenJobData, appId?: string, envName?: string) => {
|
|
276
|
+
const environmentName = envName || this.#envName;
|
|
277
|
+
const resolvedAppId = appId || this.#appId;
|
|
278
|
+
try {
|
|
279
|
+
const response = await this.#amplifyUiBuilder
|
|
280
|
+
.startCodegenJob({
|
|
281
|
+
appId: resolvedAppId,
|
|
282
|
+
environmentName,
|
|
283
|
+
codegenJobToCreate,
|
|
284
|
+
})
|
|
285
|
+
.promise();
|
|
286
|
+
if (!response.entity || !response.entity.id) {
|
|
287
|
+
throw new Error('Error starting codegen job');
|
|
288
|
+
}
|
|
289
|
+
return response.entity.id;
|
|
290
|
+
} catch (err) {
|
|
291
|
+
printer.debug('Failed to start job');
|
|
292
|
+
printer.debug(err.stack);
|
|
293
|
+
throw err;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
getCodegenJob = async (jobId: string, appId?: string, envName?: string) => {
|
|
298
|
+
const environmentName = envName || this.#envName;
|
|
299
|
+
const resolvedAppId = appId || this.#appId;
|
|
300
|
+
try {
|
|
301
|
+
const { job } = await this.#amplifyUiBuilder
|
|
302
|
+
.getCodegenJob({
|
|
303
|
+
id: jobId,
|
|
304
|
+
appId: resolvedAppId,
|
|
305
|
+
environmentName,
|
|
306
|
+
})
|
|
307
|
+
.promise();
|
|
308
|
+
if (!job) {
|
|
309
|
+
throw new Error('Error getting codegen job');
|
|
310
|
+
}
|
|
311
|
+
return job;
|
|
312
|
+
} catch (err) {
|
|
313
|
+
printer.debug(err.toString());
|
|
314
|
+
throw err;
|
|
315
|
+
}
|
|
316
|
+
};
|
|
270
317
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/* eslint-disable spellcheck/spell-checker */
|
|
2
|
-
import { StudioSchema } from '@aws-amplify/codegen-ui';
|
|
3
2
|
import ora from 'ora';
|
|
4
3
|
import { printer } from '@aws-amplify/amplify-prompts';
|
|
5
4
|
import { $TSContext } from '@aws-amplify/amplify-cli-core';
|
|
@@ -7,18 +6,16 @@ import { AmplifyStudioClient } from '../clients';
|
|
|
7
6
|
import {
|
|
8
7
|
notifyMissingPackages,
|
|
9
8
|
shouldRenderComponents,
|
|
10
|
-
generateUiBuilderComponents,
|
|
11
|
-
generateUiBuilderThemes,
|
|
12
9
|
getAmplifyDataSchema,
|
|
13
|
-
generateAmplifyUiBuilderIndexFile,
|
|
14
|
-
generateUiBuilderForms,
|
|
15
|
-
generateAmplifyUiBuilderUtilFile,
|
|
16
|
-
isStudioForm,
|
|
17
10
|
isFormDetachedFromModel,
|
|
18
11
|
deleteDetachedForms,
|
|
19
12
|
hasStorageField,
|
|
20
|
-
|
|
13
|
+
mapGenericDataSchemaToCodegen,
|
|
14
|
+
waitForSucceededJob,
|
|
15
|
+
getUiBuilderComponentsPath,
|
|
16
|
+
extractUIComponents,
|
|
21
17
|
} from './utils';
|
|
18
|
+
import { AmplifyUIBuilder } from 'aws-sdk';
|
|
22
19
|
|
|
23
20
|
/**
|
|
24
21
|
* Pulls ui components from Studio backend and generates the code in the user's file system
|
|
@@ -37,7 +34,8 @@ export const run = async (context: $TSContext, eventType: 'PostPush' | 'PostPull
|
|
|
37
34
|
studioClient.isGraphQLSupported ? getAmplifyDataSchema(context) : Promise.resolve(undefined),
|
|
38
35
|
]);
|
|
39
36
|
|
|
40
|
-
const nothingWouldAutogenerate =
|
|
37
|
+
const nothingWouldAutogenerate =
|
|
38
|
+
!dataSchema || !studioClient.metadata.autoGenerateForms || !studioClient.isGraphQLSupported || !studioClient.isDataStoreEnabled;
|
|
41
39
|
|
|
42
40
|
if (nothingWouldAutogenerate && [componentSchemas, themeSchemas, formSchemas].every((group) => !group.entities.length)) {
|
|
43
41
|
printer.debug('Skipping UI component generation since none are found.');
|
|
@@ -45,61 +43,51 @@ export const run = async (context: $TSContext, eventType: 'PostPush' | 'PostPull
|
|
|
45
43
|
}
|
|
46
44
|
spinner.start('Generating UI components...');
|
|
47
45
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
const genericDataSchema = dataSchema ? mapGenericDataSchemaToCodegen(dataSchema) : undefined;
|
|
47
|
+
|
|
48
|
+
const job: AmplifyUIBuilder.StartCodegenJobData = {
|
|
49
|
+
renderConfig: {
|
|
50
|
+
react: {
|
|
51
|
+
module: 'es2020',
|
|
52
|
+
target: 'es2020',
|
|
53
|
+
script: 'jsx',
|
|
54
|
+
renderTypeDeclarations: true,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
autoGenerateForms: studioClient.metadata.autoGenerateForms && studioClient.isGraphQLSupported,
|
|
58
|
+
features: studioClient.metadata.formFeatureFlags,
|
|
58
59
|
};
|
|
60
|
+
// SDK will throw if this is undefined
|
|
61
|
+
// even though it's optional
|
|
62
|
+
if (genericDataSchema) {
|
|
63
|
+
job.genericDataSchema = genericDataSchema;
|
|
64
|
+
}
|
|
65
|
+
const jobId = await studioClient.startCodegenJob(job);
|
|
66
|
+
const finishedJob = await waitForSucceededJob(() => studioClient.getCodegenJob(jobId), { pollInterval: 2000 });
|
|
67
|
+
if (!finishedJob.asset?.downloadUrl) {
|
|
68
|
+
throw new Error('No manifest file in codegen job');
|
|
69
|
+
}
|
|
70
|
+
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
71
|
+
await extractUIComponents(finishedJob.asset.downloadUrl, uiBuilderComponentsPath);
|
|
59
72
|
|
|
60
|
-
const successfulSchemas: StudioSchema[] = [];
|
|
61
73
|
const detachedForms: { id: string; name: string }[] = [];
|
|
62
|
-
let hasSuccessfulForm = false;
|
|
63
74
|
const failedResponseNames: string[] = [];
|
|
64
75
|
const modelNames = dataSchema?.models ? new Set(Object.keys(dataSchema.models)) : new Set<string>();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Object.entries(generatedResults).forEach(([key, results]) => {
|
|
68
|
-
results.forEach((result) => {
|
|
69
|
-
if (result.resultType === 'SUCCESS') {
|
|
70
|
-
successfulSchemas.push(result.schema);
|
|
71
|
-
if (key === 'form') {
|
|
72
|
-
hasSuccessfulForm = true;
|
|
76
|
+
const hasStorageManagerField = formSchemas.entities.some((formSchema) => hasStorageField(formSchema));
|
|
73
77
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
const failedSchema = result.schema;
|
|
80
|
-
/**
|
|
81
|
-
* A form resource may fail to generate because it's DataStore model type
|
|
82
|
-
* no longer exists.
|
|
83
|
-
*/
|
|
84
|
-
if (
|
|
85
|
-
isStudioForm(failedSchema) &&
|
|
86
|
-
failedSchema.id &&
|
|
87
|
-
dataSchema &&
|
|
88
|
-
eventType === 'PostPush' &&
|
|
89
|
-
isFormDetachedFromModel(failedSchema, modelNames)
|
|
90
|
-
) {
|
|
91
|
-
// Don't need to add form to failedResponseNames if it is going to be deleted
|
|
92
|
-
detachedForms.push({ id: failedSchema.id, name: failedSchema.name });
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
failedResponseNames.push(result.schemaName);
|
|
96
|
-
}
|
|
78
|
+
if (dataSchema && eventType === 'PostPush') {
|
|
79
|
+
formSchemas.entities.forEach((formSchema) => {
|
|
80
|
+
isFormDetachedFromModel(formSchema, modelNames) && detachedForms.push({ id: formSchema.id, name: formSchema.name });
|
|
97
81
|
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
generateAmplifyUiBuilderIndexFile(context, successfulSchemas);
|
|
82
|
+
}
|
|
101
83
|
|
|
102
|
-
|
|
84
|
+
if (finishedJob.statusMessage) {
|
|
85
|
+
const errorStack = JSON.parse(finishedJob.statusMessage)?.codegenErrors as {
|
|
86
|
+
schemaName: string;
|
|
87
|
+
error: string;
|
|
88
|
+
}[];
|
|
89
|
+
errorStack.forEach((e) => failedResponseNames.push(e.schemaName));
|
|
90
|
+
}
|
|
103
91
|
|
|
104
92
|
if (failedResponseNames.length > 0) {
|
|
105
93
|
spinner.fail(`Failed to sync the following components: ${failedResponseNames.join(', ')}`);
|
|
@@ -111,6 +99,7 @@ export const run = async (context: $TSContext, eventType: 'PostPush' | 'PostPull
|
|
|
111
99
|
...componentSchemas.entities.filter((component) => !component.schemaVersion).map((component) => component.name),
|
|
112
100
|
...formSchemas.entities.filter((form) => !form.schemaVersion).map((form) => form.name),
|
|
113
101
|
];
|
|
102
|
+
|
|
114
103
|
if (invalidComponentNames.length) {
|
|
115
104
|
printer.warn(
|
|
116
105
|
`The components ${invalidComponentNames.join(
|
|
@@ -1,210 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
// You have a misspelled word: Renderer on Identifier
|
|
3
|
-
import {
|
|
4
|
-
StudioTemplateRendererManager,
|
|
5
|
-
StudioComponent,
|
|
6
|
-
StudioTheme,
|
|
7
|
-
StudioTemplateRendererFactory,
|
|
8
|
-
StudioTemplateRenderer,
|
|
9
|
-
FrameworkOutputManager,
|
|
10
|
-
RenderTextComponentResponse,
|
|
11
|
-
GenericDataSchema,
|
|
12
|
-
getGenericFromDataStore,
|
|
13
|
-
StudioForm,
|
|
14
|
-
StudioSchema,
|
|
15
|
-
FormFeatureFlags,
|
|
16
|
-
} from '@aws-amplify/codegen-ui';
|
|
17
|
-
import {
|
|
18
|
-
AmplifyRenderer,
|
|
19
|
-
AmplifyFormRenderer,
|
|
20
|
-
ReactThemeStudioTemplateRenderer,
|
|
21
|
-
ReactIndexStudioTemplateRenderer,
|
|
22
|
-
ModuleKind,
|
|
23
|
-
ScriptTarget,
|
|
24
|
-
ScriptKind,
|
|
25
|
-
UtilTemplateType,
|
|
26
|
-
ReactUtilsStudioTemplateRenderer,
|
|
27
|
-
ReactThemeStudioTemplateRendererOptions,
|
|
28
|
-
} from '@aws-amplify/codegen-ui-react';
|
|
1
|
+
import { GenericDataSchema, getGenericFromDataStore } from '@aws-amplify/codegen-ui';
|
|
29
2
|
import { printer } from '@aws-amplify/amplify-prompts';
|
|
30
3
|
import { $TSContext } from '@aws-amplify/amplify-cli-core';
|
|
31
|
-
import { getUiBuilderComponentsPath } from './getUiBuilderComponentsPath';
|
|
32
4
|
import { ModelIntrospectionSchema } from '@aws-amplify/appsync-modelgen-plugin';
|
|
33
5
|
|
|
34
|
-
const config = {
|
|
35
|
-
module: ModuleKind.ES2020,
|
|
36
|
-
target: ScriptTarget.ES2020,
|
|
37
|
-
script: ScriptKind.JSX,
|
|
38
|
-
renderTypeDeclarations: true,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Writes component file to the work space
|
|
43
|
-
*/
|
|
44
|
-
export const createUiBuilderComponent = (context: $TSContext, schema: StudioComponent, dataSchema?: GenericDataSchema): StudioComponent => {
|
|
45
|
-
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
46
|
-
const rendererFactory = new StudioTemplateRendererFactory(
|
|
47
|
-
(component: StudioComponent) => new AmplifyRenderer(component as StudioComponent, config, dataSchema),
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
const outputPathDir = uiBuilderComponentsPath;
|
|
51
|
-
|
|
52
|
-
const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
|
|
53
|
-
outputPathDir,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
rendererManager.renderSchemaToTemplate(schema);
|
|
57
|
-
return schema;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Writes theme file to the work space
|
|
62
|
-
*/
|
|
63
|
-
export const createUiBuilderTheme = (
|
|
64
|
-
context: $TSContext,
|
|
65
|
-
schema: StudioTheme,
|
|
66
|
-
options?: ReactThemeStudioTemplateRendererOptions,
|
|
67
|
-
): StudioTheme => {
|
|
68
|
-
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
69
|
-
const rendererFactory = new StudioTemplateRendererFactory(
|
|
70
|
-
(component: StudioTheme) =>
|
|
71
|
-
new ReactThemeStudioTemplateRenderer(component, config, options) as unknown as StudioTemplateRenderer<
|
|
72
|
-
unknown,
|
|
73
|
-
StudioTheme,
|
|
74
|
-
FrameworkOutputManager<unknown>,
|
|
75
|
-
RenderTextComponentResponse
|
|
76
|
-
>,
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const outputPathDir = uiBuilderComponentsPath;
|
|
80
|
-
|
|
81
|
-
const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
|
|
82
|
-
outputPathDir,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
rendererManager.renderSchemaToTemplate(schema);
|
|
87
|
-
return schema;
|
|
88
|
-
} catch (e) {
|
|
89
|
-
printer.debug(e);
|
|
90
|
-
printer.debug('Skipping invalid theme with schema');
|
|
91
|
-
printer.debug(JSON.stringify(schema, null, 2));
|
|
92
|
-
throw e;
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Writes form file to the work space
|
|
98
|
-
*/
|
|
99
|
-
export const createUiBuilderForm = (
|
|
100
|
-
context: $TSContext,
|
|
101
|
-
schema: StudioForm,
|
|
102
|
-
dataSchema?: GenericDataSchema,
|
|
103
|
-
formFeatureFlags?: FormFeatureFlags,
|
|
104
|
-
): StudioForm => {
|
|
105
|
-
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
106
|
-
const rendererFactory = new StudioTemplateRendererFactory(
|
|
107
|
-
(form: StudioForm) =>
|
|
108
|
-
new AmplifyFormRenderer(form, dataSchema, config, formFeatureFlags) as unknown as StudioTemplateRenderer<
|
|
109
|
-
unknown,
|
|
110
|
-
StudioForm,
|
|
111
|
-
FrameworkOutputManager<unknown>,
|
|
112
|
-
RenderTextComponentResponse
|
|
113
|
-
>,
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const outputPathDir = uiBuilderComponentsPath;
|
|
117
|
-
|
|
118
|
-
const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
|
|
119
|
-
outputPathDir,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
rendererManager.renderSchemaToTemplate(schema);
|
|
124
|
-
return schema;
|
|
125
|
-
} catch (e) {
|
|
126
|
-
printer.debug(e);
|
|
127
|
-
printer.debug('Skipping invalid form with schema');
|
|
128
|
-
printer.debug(JSON.stringify(schema, null, 2));
|
|
129
|
-
throw e;
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Writes index file to the work space
|
|
135
|
-
*/
|
|
136
|
-
export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas: StudioSchema[]): void => {
|
|
137
|
-
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
138
|
-
const rendererFactory = new StudioTemplateRendererFactory(
|
|
139
|
-
(schema: StudioSchema[]) =>
|
|
140
|
-
new ReactIndexStudioTemplateRenderer(schema, config) as unknown as StudioTemplateRenderer<
|
|
141
|
-
unknown,
|
|
142
|
-
StudioSchema[],
|
|
143
|
-
FrameworkOutputManager<unknown>,
|
|
144
|
-
RenderTextComponentResponse
|
|
145
|
-
>,
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
const outputPathDir = uiBuilderComponentsPath;
|
|
149
|
-
|
|
150
|
-
const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
|
|
151
|
-
outputPathDir,
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
if (schemas.length) {
|
|
156
|
-
rendererManager.renderSchemaToTemplate(schemas);
|
|
157
|
-
}
|
|
158
|
-
} catch (e) {
|
|
159
|
-
printer.debug(e);
|
|
160
|
-
printer.debug('Failed to generate component index file');
|
|
161
|
-
throw e;
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
type UtilFileChecks = {
|
|
166
|
-
hasForms: boolean;
|
|
167
|
-
hasViews: boolean;
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Writes utils file to the work space
|
|
172
|
-
*/
|
|
173
|
-
export const generateAmplifyUiBuilderUtilFile = (context: $TSContext, { hasForms, hasViews }: UtilFileChecks): void => {
|
|
174
|
-
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
175
|
-
const rendererFactory = new StudioTemplateRendererFactory(
|
|
176
|
-
(utils: UtilTemplateType[]) => new ReactUtilsStudioTemplateRenderer(utils, config),
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
const outputPathDir = uiBuilderComponentsPath;
|
|
180
|
-
|
|
181
|
-
const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
|
|
182
|
-
outputPathDir,
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
const utils = new Set<UtilTemplateType>();
|
|
186
|
-
|
|
187
|
-
if (hasForms) {
|
|
188
|
-
utils.add('validation');
|
|
189
|
-
utils.add('formatter');
|
|
190
|
-
utils.add('fetchByPath');
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (hasViews) {
|
|
194
|
-
utils.add('formatter');
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
if (utils.size) {
|
|
199
|
-
rendererManager.renderSchemaToTemplate([...utils]);
|
|
200
|
-
}
|
|
201
|
-
} catch (e) {
|
|
202
|
-
printer.debug(e);
|
|
203
|
-
printer.debug('Failed to generate component index file');
|
|
204
|
-
throw e;
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
|
|
208
6
|
/**
|
|
209
7
|
* If models are available, they will be populated in the models field of the returned object.
|
|
210
8
|
* If they're not available, it will return undefined
|
|
@@ -223,25 +21,3 @@ export const getAmplifyDataSchema = async (context: $TSContext): Promise<Generic
|
|
|
223
21
|
return undefined;
|
|
224
22
|
}
|
|
225
23
|
};
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* generates base create/update froms from names
|
|
229
|
-
*/
|
|
230
|
-
export const generateBaseForms = (modelMap: { [model: string]: Set<'create' | 'update'> }): StudioForm[] => {
|
|
231
|
-
const getSchema = (name: string, type: 'create' | 'update'): StudioForm => ({
|
|
232
|
-
name: `${name}${type === 'create' ? 'CreateForm' : 'UpdateForm'}`,
|
|
233
|
-
formActionType: type,
|
|
234
|
-
dataType: { dataSourceType: 'DataStore', dataTypeName: name },
|
|
235
|
-
fields: {},
|
|
236
|
-
sectionalElements: {},
|
|
237
|
-
style: {},
|
|
238
|
-
cta: {},
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
const schemas: StudioForm[] = [];
|
|
242
|
-
|
|
243
|
-
Object.entries(modelMap).forEach(([name, set]) => {
|
|
244
|
-
set.forEach((type) => schemas.push(getSchema(name, type)));
|
|
245
|
-
});
|
|
246
|
-
return schemas;
|
|
247
|
-
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { StudioForm, StudioSchema } from '@aws-amplify/codegen-ui';
|
|
2
1
|
import { Form } from 'aws-sdk/clients/amplifyuibuilder';
|
|
3
2
|
import { AmplifyStudioClient } from '../../clients';
|
|
4
3
|
import { printer } from '@aws-amplify/amplify-prompts';
|
|
@@ -7,7 +6,7 @@ import asyncPool from 'tiny-async-pool';
|
|
|
7
6
|
/**
|
|
8
7
|
* Does the form's reference DataStore type exist in the list of models
|
|
9
8
|
*/
|
|
10
|
-
export const isFormDetachedFromModel = (formSchema:
|
|
9
|
+
export const isFormDetachedFromModel = (formSchema: Form, modelNames: Set<string>): boolean => {
|
|
11
10
|
return formSchema.dataType.dataSourceType === 'DataStore' && !modelNames.has(formSchema.dataType.dataTypeName);
|
|
12
11
|
};
|
|
13
12
|
|
|
@@ -16,7 +15,7 @@ export const isFormDetachedFromModel = (formSchema: StudioForm | Form, modelName
|
|
|
16
15
|
* @param schema The schema to test
|
|
17
16
|
* @returns boolean
|
|
18
17
|
*/
|
|
19
|
-
export const isFormSchemaCustomized = (formSchema:
|
|
18
|
+
export const isFormSchemaCustomized = (formSchema: Form): boolean => {
|
|
20
19
|
const { fields, style, sectionalElements } = formSchema;
|
|
21
20
|
// Fields and sectionalElements just need an empty check
|
|
22
21
|
if (!isEmpty({ ...fields, ...sectionalElements })) {
|
|
@@ -31,7 +30,7 @@ export const isFormSchemaCustomized = (formSchema: StudioForm | Form): boolean =
|
|
|
31
30
|
/**
|
|
32
31
|
* Is the schema a StudioForm
|
|
33
32
|
*/
|
|
34
|
-
export const isStudioForm = (schema:
|
|
33
|
+
export const isStudioForm = (schema: Form | undefined): schema is Form => {
|
|
35
34
|
if (!schema) return false;
|
|
36
35
|
return 'formActionType' in schema;
|
|
37
36
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FieldConfig, Form } from 'aws-sdk/clients/amplifyuibuilder';
|
|
2
2
|
|
|
3
|
-
export const hasStorageField = (form:
|
|
3
|
+
export const hasStorageField = (form: Form): boolean => {
|
|
4
4
|
const result = Object.values(form.fields).some((field) => {
|
|
5
5
|
return isGenericFieldConfig(field) && field.inputType?.type === 'StorageField';
|
|
6
6
|
});
|
|
@@ -8,10 +8,6 @@ export const hasStorageField = (form: StudioForm): boolean => {
|
|
|
8
8
|
return result;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
export const
|
|
12
|
-
return schema && Object.keys(schema).includes('fields');
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const isGenericFieldConfig = (config: StudioFormFieldConfig): config is StudioGenericFieldConfig => {
|
|
11
|
+
export const isGenericFieldConfig = (config: FieldConfig) => {
|
|
16
12
|
return !Object.keys(config).includes('excluded');
|
|
17
13
|
};
|