@aws-amplify/amplify-util-uibuilder 1.3.0-rc.c36fa71f5.0 → 1.3.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/lib/clients/amplify-studio-client.d.ts +17 -0
  3. package/lib/clients/amplify-studio-client.d.ts.map +1 -0
  4. package/lib/clients/amplify-studio-client.js +145 -0
  5. package/lib/clients/amplify-studio-client.js.map +1 -0
  6. package/lib/clients/index.d.ts +2 -0
  7. package/lib/clients/index.d.ts.map +1 -0
  8. package/lib/clients/index.js +9 -0
  9. package/lib/clients/index.js.map +1 -0
  10. package/lib/commands/cloneComponentsFromEnv.d.ts +1 -1
  11. package/lib/commands/cloneComponentsFromEnv.d.ts.map +1 -1
  12. package/lib/commands/cloneComponentsFromEnv.js +15 -31
  13. package/lib/commands/cloneComponentsFromEnv.js.map +1 -1
  14. package/lib/commands/generateComponents.d.ts.map +1 -1
  15. package/lib/commands/generateComponents.js +14 -12
  16. package/lib/commands/generateComponents.js.map +1 -1
  17. package/lib/commands/utils/{createUiBuilderComponent.d.ts → codegenResources.d.ts} +5 -3
  18. package/lib/commands/utils/codegenResources.d.ts.map +1 -0
  19. package/lib/commands/utils/{createUiBuilderComponent.js → codegenResources.js} +26 -4
  20. package/lib/commands/utils/codegenResources.js.map +1 -0
  21. package/lib/commands/utils/environmentHelpers.d.ts +5 -0
  22. package/lib/commands/utils/environmentHelpers.d.ts.map +1 -0
  23. package/lib/commands/utils/environmentHelpers.js +25 -0
  24. package/lib/commands/utils/environmentHelpers.js.map +1 -0
  25. package/lib/commands/utils/index.d.ts +8 -0
  26. package/lib/commands/utils/index.d.ts.map +1 -0
  27. package/lib/commands/utils/index.js +20 -0
  28. package/lib/commands/utils/index.js.map +1 -0
  29. package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts +9 -18
  30. package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts.map +1 -1
  31. package/lib/commands/utils/syncAmplifyUiBuilderComponents.js +8 -80
  32. package/lib/commands/utils/syncAmplifyUiBuilderComponents.js.map +1 -1
  33. package/lib/index.d.ts +3 -3
  34. package/lib/index.d.ts.map +1 -1
  35. package/lib/index.js +5 -5
  36. package/lib/index.js.map +1 -1
  37. package/package.json +14 -6
  38. package/src/__tests__/cloneComponentsFromEnv.test.ts +33 -26
  39. package/src/__tests__/createUiBuilderComponent.test.ts +14 -13
  40. package/src/__tests__/generateComponents.test.ts +40 -30
  41. package/src/__tests__/getAmplifyDataSchema.test.ts +102 -0
  42. package/src/__tests__/syncAmplifyUiComponents.test.ts +55 -33
  43. package/src/clients/amplify-studio-client.ts +161 -0
  44. package/src/clients/index.ts +1 -0
  45. package/src/commands/cloneComponentsFromEnv.ts +26 -29
  46. package/src/commands/generateComponents.ts +19 -12
  47. package/src/commands/utils/{createUiBuilderComponent.ts → codegenResources.ts} +39 -11
  48. package/src/commands/utils/environmentHelpers.ts +32 -0
  49. package/src/commands/utils/index.ts +7 -0
  50. package/src/commands/utils/syncAmplifyUiBuilderComponents.ts +38 -81
  51. package/src/index.ts +15 -5
  52. package/tsconfig.tsbuildinfo +1 -1
  53. package/lib/commands/utils/amplifyUiBuilderService.d.ts +0 -5
  54. package/lib/commands/utils/amplifyUiBuilderService.d.ts.map +0 -1
  55. package/lib/commands/utils/amplifyUiBuilderService.js +0 -20
  56. package/lib/commands/utils/amplifyUiBuilderService.js.map +0 -1
  57. package/lib/commands/utils/createUiBuilderComponent.d.ts.map +0 -1
  58. package/lib/commands/utils/createUiBuilderComponent.js.map +0 -1
  59. package/src/commands/utils/amplifyUiBuilderService.ts +0 -22
@@ -1,24 +1,29 @@
1
- import { $TSContext } from 'amplify-cli-core';
1
+ import { AmplifyCategories, AmplifySupportedService, stateManager } from 'amplify-cli-core'; // eslint-disable-line import/no-extraneous-dependencies
2
+ import aws from 'aws-sdk'; // eslint-disable-line import/no-extraneous-dependencies
2
3
  import {
3
4
  generateUiBuilderComponents,
5
+ generateUiBuilderThemes,
4
6
  getEnvName,
5
7
  getAppId,
6
- listUiBuilderComponents,
7
- listUiBuilderThemes,
8
8
  resolveAppId,
9
- generateUiBuilderThemes,
10
- } from '../commands/utils/syncAmplifyUiBuilderComponents';
11
- import { getAmplifyUIBuilderService } from '../commands/utils/amplifyUiBuilderService'
12
- import aws from 'aws-sdk';
13
- import * as CLICore from 'amplify-cli-core';
14
- import * as createUiBuilderComponentDependency from '../commands/utils/createUiBuilderComponent';
15
- const aws_mock = aws as any;
16
- const createUiBuilderComponentDependency_mock = createUiBuilderComponentDependency as any;
9
+ } from '../commands/utils';
10
+ import { AmplifyStudioClient } from '../clients';
11
+ import * as createUiBuilderComponentDependency from '../commands/utils/codegenResources';
12
+
13
+ jest.mock('amplify-cli-core', () => ({
14
+ ...jest.requireActual('amplify-cli-core'),
15
+ FeatureFlags: {
16
+ getBoolean: () => false,
17
+ getNumber: () => 0,
18
+ },
19
+ }));
20
+
21
+ const awsMock = aws as any;
22
+ const stateManagerMock = stateManager as any;
23
+ const createUiBuilderComponentDependencyMock = createUiBuilderComponentDependency as any;
17
24
  describe('should sync amplify ui builder components', () => {
18
- let context: $TSContext | any;
25
+ let context: any;
19
26
  beforeEach(() => {
20
- CLICore.FeatureFlags.getBoolean = () => false;
21
- CLICore.FeatureFlags.getNumber = () => 0;
22
27
  context = {
23
28
  exeInfo: {
24
29
  projectConfig: {
@@ -43,7 +48,18 @@ describe('should sync amplify ui builder components', () => {
43
48
  },
44
49
  };
45
50
 
46
- aws_mock.AmplifyUIBuilder = jest.fn(() => ({
51
+ stateManagerMock.getMeta = jest.fn(() => ({
52
+ [AmplifyCategories.API]: {
53
+ MyResourceName: {
54
+ service: AmplifySupportedService.APPSYNC,
55
+ },
56
+ },
57
+ providers: {
58
+ awscloudformation: { AmplifyAppId: 'testAppId' },
59
+ },
60
+ }));
61
+
62
+ awsMock.AmplifyUIBuilder = jest.fn(() => ({
47
63
  exportComponents: jest.fn(() => ({
48
64
  promise: jest.fn(() => ({
49
65
  entities: [
@@ -68,8 +84,8 @@ describe('should sync amplify ui builder components', () => {
68
84
  })),
69
85
  }));
70
86
 
71
- createUiBuilderComponentDependency_mock.createUiBuilderComponent = jest.fn();
72
- createUiBuilderComponentDependency_mock.createUiBuilderTheme = jest.fn();
87
+ createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn();
88
+ createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn();
73
89
  });
74
90
 
75
91
  it('pulls components from aws-sdk and passes them to createUiBuilderComponent', () => {
@@ -77,14 +93,14 @@ describe('should sync amplify ui builder components', () => {
77
93
  });
78
94
 
79
95
  it('does not throw an error when createUiBuilderComponent fails', () => {
80
- createUiBuilderComponentDependency_mock.createUiBuilderComponent = jest.fn(() => {
96
+ createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn(() => {
81
97
  throw new Error('ahhh!');
82
98
  });
83
99
  expect(async () => generateUiBuilderComponents(context, [])).not.toThrow();
84
100
  });
85
101
 
86
102
  it('does not throw an error when createUiBuilderThemes fails', () => {
87
- createUiBuilderComponentDependency_mock.createUiBuilderComponent = jest.fn(() => {
103
+ createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn(() => {
88
104
  throw new Error('ahhh!');
89
105
  });
90
106
  expect(async () => generateUiBuilderThemes(context, [])).not.toThrow();
@@ -93,20 +109,26 @@ describe('should sync amplify ui builder components', () => {
93
109
  it('can getAmplifyUIBuilderService', async () => {
94
110
  process.env.UI_BUILDER_ENDPOINT = 'https://mock-endpoint.com';
95
111
  process.env.UI_BUILDER_REGION = 'mock-region';
96
- const service = await getAmplifyUIBuilderService(context, 'testEnv', 'testAppId');
97
- expect(Object.keys(service)).toContain('exportComponents');
98
- expect(Object.keys(service)).toContain('exportThemes');
112
+ const client = await AmplifyStudioClient.setClientInfo(context);
113
+ expect(Object.keys(client)).toEqual(expect.arrayContaining([
114
+ 'listComponents',
115
+ 'listThemes',
116
+ 'getModels',
117
+ 'createComponent',
118
+ ]));
99
119
  });
100
- it('can listUiBuilderThemes', async () => {
101
- const themes = await listUiBuilderThemes(context, 'testEnv');
120
+ it('can list themes', async () => {
121
+ const client = await AmplifyStudioClient.setClientInfo(context);
122
+ const themes = await client.listThemes();
102
123
  expect(themes.entities).toHaveLength(1);
103
124
  });
104
125
  it('can listUiBuilderComponents', async () => {
105
- const components = await listUiBuilderComponents(context, 'testEnv');
126
+ const client = await AmplifyStudioClient.setClientInfo(context);
127
+ const components = await client.listThemes();
106
128
  expect(components.entities).toHaveLength(1);
107
129
  });
108
130
  it('can getAppId', async () => {
109
- const appId = await getAppId(context);
131
+ const appId = getAppId(context);
110
132
  expect(appId).toBe('testAppId');
111
133
  });
112
134
  it('can getEnvName', () => {
@@ -114,34 +136,34 @@ describe('should sync amplify ui builder components', () => {
114
136
  expect(envName).toBe('testEnvName');
115
137
  });
116
138
  it('can resolveAppId', async () => {
117
- context.amplify.invokePluginMethod = () => 'testAppId';
118
- const appId = await resolveAppId(context);
139
+ const appId = resolveAppId();
119
140
  expect(appId).toBe('testAppId');
120
141
  });
121
142
  it('can throw on getAppId', async () => {
122
143
  context.input.options.appId = null;
123
144
  context.amplify.invokePluginMethod = () => null;
124
- expect(async () => await getAppId(context)).rejects.toThrowError();
145
+ stateManagerMock.getMeta = () => ({});
146
+ expect(() => getAppId(context)).toThrowError();
125
147
  });
126
148
  it('can generate ui builder components', async () => {
127
- createUiBuilderComponentDependency_mock.createUiBuilderComponent = jest.fn().mockImplementation(() => ({}));
149
+ createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn().mockImplementation(() => ({}));
128
150
  const components = generateUiBuilderComponents(context, [{}, {}]);
129
151
  expect(components.every(component => component.resultType === 'SUCCESS')).toBeTruthy();
130
152
  });
131
153
  it('can handle failed generation generate ui builder components', async () => {
132
- createUiBuilderComponentDependency_mock.createUiBuilderComponent = jest.fn().mockImplementation(() => {
154
+ createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn().mockImplementation(() => {
133
155
  throw new Error('ahh!');
134
156
  });
135
157
  const components = generateUiBuilderComponents(context, [{}, {}]);
136
158
  expect(components.every(component => component.resultType === 'FAILURE')).toBeTruthy();
137
159
  });
138
160
  it('can generate ui builder themes', async () => {
139
- createUiBuilderComponentDependency_mock.createUiBuilderTheme = jest.fn().mockImplementation(() => ({}));
161
+ createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn().mockImplementation(() => ({}));
140
162
  const themes = generateUiBuilderThemes(context, [{}, {}]);
141
163
  expect(themes.every(theme => theme.resultType === 'SUCCESS')).toBeTruthy();
142
164
  });
143
165
  it('can handle failed generation generate ui builder themes', async () => {
144
- createUiBuilderComponentDependency_mock.createUiBuilderTheme = jest.fn().mockImplementation(() => {
166
+ createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn().mockImplementation(() => {
145
167
  throw new Error('ahh!');
146
168
  });
147
169
  const themes = generateUiBuilderThemes(context, [{}, {}]);
@@ -0,0 +1,161 @@
1
+ import type { $TSContext } from 'amplify-cli-core';
2
+ import type { ServiceConfigurationOptions } from 'aws-sdk/lib/service';
3
+ import type { CreateComponentData, Component, Theme } from 'aws-sdk/clients/amplifyuibuilder';
4
+ import { AmplifyUIBuilder, AmplifyBackend } from 'aws-sdk';
5
+ import { printer } from 'amplify-prompts';
6
+ import { getAppId, getEnvName } from '../commands/utils/environmentHelpers';
7
+
8
+ /**
9
+ * Builds the Amplify Backend Client
10
+ */
11
+ const buildAmplifyBackendClient = (awsConfigInfo: ServiceConfigurationOptions): AmplifyBackend => {
12
+ const awsConfig = { ...awsConfigInfo };
13
+ if (process.env.AMPLIFY_BACKEND_ENDPOINT) {
14
+ awsConfig.endpoint = process.env.AMPLIFY_BACKEND_ENDPOINT;
15
+ }
16
+
17
+ if (process.env.AMPLIFY_BACKEND_REGION) {
18
+ awsConfig.region = process.env.AMPLIFY_BACKEND_REGION;
19
+ }
20
+
21
+ return new AmplifyBackend(awsConfig);
22
+ };
23
+
24
+ /**
25
+ * Builds the Amplify UIBuilder Client
26
+ */
27
+ const buildAmplifyUiBuilderClient = (awsConfigInfo: ServiceConfigurationOptions): AmplifyUIBuilder => {
28
+ const awsConfig = { ...awsConfigInfo };
29
+ if (process.env.UI_BUILDER_ENDPOINT) {
30
+ awsConfig.endpoint = process.env.UI_BUILDER_ENDPOINT;
31
+ }
32
+
33
+ if (process.env.UI_BUILDER_REGION) {
34
+ awsConfig.region = process.env.UI_BUILDER_REGION;
35
+ }
36
+
37
+ return new AmplifyUIBuilder(awsConfig);
38
+ };
39
+
40
+ /**
41
+ * used for Amplify Studio related clients
42
+ */
43
+ export default class AmplifyStudioClient {
44
+ #amplifyUiBuilder: AmplifyUIBuilder;
45
+ #amplifyBackend: AmplifyBackend;
46
+ #appId: string;
47
+ #envName: string;
48
+ /**
49
+ * Used to configure the AWS Amplify clients.
50
+ */
51
+ static async setClientInfo(context: $TSContext, envName?: string, appId?: string): Promise<AmplifyStudioClient> {
52
+ const resolvedEnvName = getEnvName(context, envName);
53
+ const resolvedAppId = getAppId(context, appId);
54
+ const awsConfigInfo = (await context.amplify.invokePluginMethod(context, 'awscloudformation', undefined, 'loadConfigurationForEnv', [
55
+ context,
56
+ resolvedEnvName,
57
+ resolvedAppId,
58
+ ])) as ServiceConfigurationOptions;
59
+ return new AmplifyStudioClient(awsConfigInfo, resolvedAppId, resolvedEnvName);
60
+ }
61
+
62
+ /*
63
+ * Builds the clients
64
+ */
65
+ constructor(awsConfigInfo: ServiceConfigurationOptions, appId: string, envName: string) {
66
+ this.#amplifyUiBuilder = buildAmplifyUiBuilderClient(awsConfigInfo);
67
+ this.#amplifyBackend = buildAmplifyBackendClient(awsConfigInfo);
68
+ this.#appId = appId;
69
+ this.#envName = envName;
70
+ }
71
+
72
+ /**
73
+ * listComponents from Studio
74
+ */
75
+ listComponents = async (envName?: string, appId?: string): Promise<{ entities: Component[] }> => {
76
+ const environmentName = envName || this.#envName;
77
+ const resolvedAppId = appId || this.#appId;
78
+ try {
79
+ let nextToken: string | undefined;
80
+ const uiBuilderComponents: Component[] = [];
81
+ do {
82
+ const response = await this.#amplifyUiBuilder
83
+ .exportComponents({
84
+ appId: resolvedAppId,
85
+ environmentName,
86
+ nextToken,
87
+ })
88
+ .promise();
89
+ uiBuilderComponents.push(...response.entities);
90
+ nextToken = response.nextToken;
91
+ } while (nextToken);
92
+ printer.debug(JSON.stringify(uiBuilderComponents, null, 2));
93
+ return { entities: uiBuilderComponents };
94
+ } catch (e) {
95
+ printer.debug(e);
96
+ throw e;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Returns all the UI Builder themes from the app
102
+ */
103
+ listThemes = async (envName?: string, appId?: string): Promise<{ entities: Theme[] }> => {
104
+ const environmentName = envName || this.#envName;
105
+ const resolvedAppId = appId || this.#appId;
106
+
107
+ try {
108
+ let nextToken: string | undefined;
109
+ const uiBuilderThemes: Theme[] = [];
110
+ do {
111
+ const response = await this.#amplifyUiBuilder
112
+ .exportThemes({
113
+ appId: resolvedAppId,
114
+ environmentName,
115
+ nextToken,
116
+ })
117
+ .promise();
118
+ uiBuilderThemes.push(...response.entities);
119
+ nextToken = response.nextToken;
120
+ } while (nextToken);
121
+ printer.debug(JSON.stringify(uiBuilderThemes, null, 2));
122
+ return { entities: uiBuilderThemes };
123
+ } catch (e) {
124
+ printer.debug(e);
125
+ throw e;
126
+ }
127
+ }
128
+
129
+ createComponent = async (component: CreateComponentData, envName?: string, appId?: string): Promise<Component | undefined> => {
130
+ const environmentName = envName || this.#envName;
131
+ const resolvedAppId = appId || this.#appId;
132
+ try {
133
+ const response = await this.#amplifyUiBuilder.createComponent({
134
+ appId: resolvedAppId,
135
+ environmentName,
136
+ componentToCreate: component,
137
+ }).promise();
138
+ return response.entity;
139
+ } catch (err) {
140
+ printer.debug(err.toString());
141
+ throw err;
142
+ }
143
+ }
144
+
145
+ getModels = async (resourceName: string, envName?: string, appId?: string): Promise<string | undefined> => {
146
+ try {
147
+ const environmentName = envName || this.#envName;
148
+ const resolvedAppId = appId || this.#appId;
149
+ const { Models } = await this.#amplifyBackend.getBackendAPIModels({
150
+ AppId: resolvedAppId,
151
+ BackendEnvironmentName: environmentName,
152
+ ResourceName: resourceName,
153
+ })
154
+ .promise();
155
+ return Models;
156
+ } catch (e) {
157
+ printer.debug(e);
158
+ throw new Error(`Models not found in AmplifyBackend:GetBackendAPIModels response: ${e.message}`);
159
+ }
160
+ }
161
+ }
@@ -0,0 +1 @@
1
+ export { default as AmplifyStudioClient } from './amplify-studio-client';
@@ -1,21 +1,25 @@
1
- import { extractArgs } from './utils/extractArgs';
2
- import { listUiBuilderComponents } from './utils/syncAmplifyUiBuilderComponents';
3
1
  import { printer } from 'amplify-prompts';
4
2
  import { $TSContext } from 'amplify-cli-core';
5
- import { getAmplifyUIBuilderService } from './utils/amplifyUiBuilderService';
3
+ import { extractArgs } from './utils';
4
+ import { AmplifyStudioClient } from '../clients';
6
5
 
7
- export async function run(context: $TSContext) {
6
+ /**
7
+ * Clones the components from the source to new environment
8
+ */
9
+ export const run = async (context: $TSContext): Promise<void> => {
8
10
  printer.debug('Running create components command in amplify-util-uibuilder');
9
11
  const args = extractArgs(context);
10
12
  const sourceEnvName = args.sourceEnvName ? args.sourceEnvName : context.exeInfo.sourceEnvName;
11
13
  const newEnvName = args.newEnvName ? args.newEnvName : context.exeInfo.localEnvInfo.envName;
14
+ const appId = args.appId ?? context.exeInfo.teamProviderInfo[sourceEnvName].awscloudformation.AmplifyAppId;
15
+ const studioClient = await AmplifyStudioClient.setClientInfo(context, sourceEnvName, appId);
12
16
 
13
- const existingComponents = await listUiBuilderComponents(context, sourceEnvName);
17
+ const [existingComponents, existingComponentsNewEnv] = await Promise.all([
18
+ studioClient.listComponents(sourceEnvName), studioClient.listComponents(newEnvName)]);
14
19
  if (existingComponents.entities.length === 0) {
15
20
  printer.debug(`${existingComponents.entities.length} components exist in source env. Skipping creation of local components.`);
16
21
  return;
17
22
  }
18
- const existingComponentsNewEnv = await listUiBuilderComponents(context, newEnvName);
19
23
 
20
24
  if (existingComponentsNewEnv.entities.length > 0) {
21
25
  printer.debug(
@@ -24,35 +28,28 @@ export async function run(context: $TSContext) {
24
28
  return;
25
29
  }
26
30
 
27
- const environmentName = args.environmentName ?? context.exeInfo.localEnvInfo.envName;
28
- const appId = args.appId ?? context.exeInfo.teamProviderInfo[environmentName].awscloudformation.AmplifyAppId;
29
- const amplifyUIBuilder = await getAmplifyUIBuilderService(context, environmentName, appId);
30
31
  const components = existingComponents.entities;
31
32
  if (!components.length) {
32
33
  printer.debug(`No UIBuilder components found in app ${appId} from env ${sourceEnvName}. Skipping component clone process.`);
33
34
  return;
34
35
  }
35
- const promises = components.map(async (component: any) => {
36
- return await amplifyUIBuilder
37
- .createComponent({
38
- appId,
39
- environmentName,
40
- componentToCreate: {
41
- bindingProperties: component.bindingProperties,
42
- children: component.children,
43
- componentType: component.componentType,
44
- name: component.name,
45
- overrides: component.overrides,
46
- properties: component.properties,
47
- sourceId: component.sourceId,
48
- variants: component.variants,
49
- },
50
- })
51
- .promise();
52
- });
53
- await Promise.all(promises);
36
+ for (let i = 0; i < components.length; i++) {
37
+ const {
38
+ appId: _appId, // eslint-disable-line @typescript-eslint/no-unused-vars
39
+ environmentName, // eslint-disable-line @typescript-eslint/no-unused-vars
40
+ id, // eslint-disable-line @typescript-eslint/no-unused-vars
41
+ createdAt, // eslint-disable-line @typescript-eslint/no-unused-vars
42
+ modifiedAt, // eslint-disable-line @typescript-eslint/no-unused-vars
43
+ ...componentCreateData
44
+ } = components[i];
45
+ await studioClient.createComponent(
46
+ componentCreateData,
47
+ newEnvName,
48
+ appId,
49
+ );
50
+ }
54
51
 
55
52
  printer.info(
56
53
  `Successfully cloned ${components.length} UIBuilder components in app ${appId} from env ${sourceEnvName} to env ${newEnvName}.`,
57
54
  );
58
- }
55
+ };
@@ -1,35 +1,38 @@
1
1
  import { StudioComponent } from '@aws-amplify/codegen-ui';
2
- import { printer } from 'amplify-prompts';
3
2
  import ora from 'ora';
3
+ import { printer } from 'amplify-prompts';
4
4
  import { $TSContext } from 'amplify-cli-core';
5
- import { notifyMissingPackages } from './utils/notifyMissingPackages';
6
- import { shouldRenderComponents } from './utils/shouldRenderComponents';
5
+ import { AmplifyStudioClient } from '../clients';
7
6
  import {
8
- listUiBuilderComponents,
9
- listUiBuilderThemes,
7
+ notifyMissingPackages,
8
+ shouldRenderComponents,
10
9
  generateUiBuilderComponents,
11
10
  generateUiBuilderThemes,
12
- } from './utils/syncAmplifyUiBuilderComponents';
13
- import { generateAmplifyUiBuilderIndexFile } from './utils/createUiBuilderComponent';
11
+ getAmplifyDataSchema,
12
+ generateAmplifyUiBuilderIndexFile,
13
+ } from './utils';
14
14
 
15
15
  /**
16
16
  * Pulls ui components from Studio backend and generates the code in the user's file system
17
17
  */
18
18
  export const run = async (context: $TSContext): Promise<void> => {
19
- printer.debug('Running generate components command in amplify-util-uibuilder');
20
19
  if (!(await shouldRenderComponents(context))) {
21
20
  return;
22
21
  }
23
22
  const spinner = ora('');
24
23
  try {
25
- const [componentSchemas, themeSchemas] = await Promise.all([listUiBuilderComponents(context), listUiBuilderThemes(context)]);
24
+ const studioClient = await AmplifyStudioClient.setClientInfo(context);
25
+ const [componentSchemas, themeSchemas, dataSchema] = await Promise.all([
26
+ studioClient.listComponents(),
27
+ studioClient.listThemes(),
28
+ getAmplifyDataSchema(studioClient),
29
+ ]);
26
30
  if (componentSchemas.entities.length === 0 && themeSchemas.entities.length === 0) {
27
31
  printer.debug('Skipping UI component generation since none are found.');
28
32
  return;
29
33
  }
30
-
31
34
  spinner.start('Generating UI components...');
32
- const generatedComponentResults = generateUiBuilderComponents(context, componentSchemas.entities);
35
+ const generatedComponentResults = generateUiBuilderComponents(context, componentSchemas.entities, dataSchema);
33
36
  const generatedThemeResults = generateUiBuilderThemes(context, themeSchemas.entities);
34
37
 
35
38
  generateAmplifyUiBuilderIndexFile(context, [
@@ -50,7 +53,11 @@ export const run = async (context: $TSContext): Promise<void> => {
50
53
 
51
54
  const invalidComponentNames = componentSchemas.entities.filter(component => !component.schemaVersion).map(component => component.name);
52
55
  if (invalidComponentNames.length) {
53
- printer.warn(`The components ${invalidComponentNames.join(', ')} were synced with an older version of Amplify Studio. Please re-sync your components with Figma to get latest features and changes.`);
56
+ printer.warn(
57
+ `The components ${invalidComponentNames.join(
58
+ ', ',
59
+ )} were synced with an older version of Amplify Studio. Please re-sync your components with Figma to get latest features and changes.`, // eslint-disable-line spellcheck/spell-checker
60
+ );
54
61
  }
55
62
 
56
63
  notifyMissingPackages(context);
@@ -1,3 +1,5 @@
1
+ /* eslint-disable spellcheck/spell-checker*/
2
+ // You have a misspelled word: Renderer on Identifier
1
3
  import {
2
4
  StudioTemplateRendererManager,
3
5
  StudioComponent,
@@ -6,6 +8,8 @@ import {
6
8
  StudioTemplateRenderer,
7
9
  FrameworkOutputManager,
8
10
  RenderTextComponentResponse,
11
+ GenericDataSchema,
12
+ getGenericFromDataStore,
9
13
  } from '@aws-amplify/codegen-ui';
10
14
  import {
11
15
  AmplifyRenderer,
@@ -15,10 +19,12 @@ import {
15
19
  ScriptTarget,
16
20
  ScriptKind,
17
21
  } from '@aws-amplify/codegen-ui-react';
18
-
19
22
  import { printer } from 'amplify-prompts';
20
- import { $TSContext } from 'amplify-cli-core';
23
+ import {
24
+ $TSContext, AmplifyCategories, AmplifySupportedService, stateManager,
25
+ } from 'amplify-cli-core';
21
26
  import { getUiBuilderComponentsPath } from './getUiBuilderComponentsPath';
27
+ import { AmplifyStudioClient } from '../../clients';
22
28
 
23
29
  const config = {
24
30
  module: ModuleKind.ES2020,
@@ -30,15 +36,10 @@ const config = {
30
36
  /**
31
37
  * Writes component file to the work space
32
38
  */
33
- export const createUiBuilderComponent = (context: $TSContext, schema: StudioComponent): StudioComponent => {
39
+ export const createUiBuilderComponent = (context: $TSContext, schema: StudioComponent, dataSchema?: GenericDataSchema): StudioComponent => {
34
40
  const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
35
41
  const rendererFactory = new StudioTemplateRendererFactory(
36
- (component: StudioComponent) => new AmplifyRenderer(component as StudioComponent, config) as unknown as StudioTemplateRenderer<
37
- unknown,
38
- StudioComponent,
39
- FrameworkOutputManager<unknown>,
40
- RenderTextComponentResponse
41
- >,
42
+ (component: StudioComponent) => new AmplifyRenderer(component as StudioComponent, config, dataSchema),
42
43
  );
43
44
 
44
45
  const outputPathDir = uiBuilderComponentsPath;
@@ -57,7 +58,7 @@ export const createUiBuilderComponent = (context: $TSContext, schema: StudioComp
57
58
  export const createUiBuilderTheme = (context: $TSContext, schema: StudioTheme): StudioTheme => {
58
59
  const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
59
60
  const rendererFactory = new StudioTemplateRendererFactory(
60
- (component: StudioTheme) => new ReactThemeStudioTemplateRenderer(component, config) as unknown as StudioTemplateRenderer<
61
+ (component: StudioTheme) => (new ReactThemeStudioTemplateRenderer(component, config) as unknown) as StudioTemplateRenderer<
61
62
  unknown,
62
63
  StudioTheme,
63
64
  FrameworkOutputManager<unknown>,
@@ -88,7 +89,7 @@ export const createUiBuilderTheme = (context: $TSContext, schema: StudioTheme):
88
89
  export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas: StudioComponent[]): RenderTextComponentResponse => {
89
90
  const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
90
91
  const rendererFactory = new StudioTemplateRendererFactory(
91
- (component: StudioComponent[]) => new ReactIndexStudioTemplateRenderer(component, config) as unknown as StudioTemplateRenderer<
92
+ (component: StudioComponent[]) => (new ReactIndexStudioTemplateRenderer(component, config) as unknown) as StudioTemplateRenderer<
92
93
  unknown,
93
94
  StudioComponent[],
94
95
  FrameworkOutputManager<unknown>,
@@ -110,3 +111,30 @@ export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas:
110
111
  throw e;
111
112
  }
112
113
  };
114
+
115
+ /**
116
+ * If models are available, they will be populated in the models field of the returned object.
117
+ * If they're not available, it will return undefined
118
+ */
119
+ export const getAmplifyDataSchema = async (
120
+ studioClient: AmplifyStudioClient,
121
+ ): Promise<GenericDataSchema | undefined> => {
122
+ try {
123
+ const meta = stateManager.getMeta();
124
+ const resourceName = Object.entries(meta[AmplifyCategories.API]).find(
125
+ ([, value]) => (value as { service: string }).service === AmplifySupportedService.APPSYNC,
126
+ )?.[0];
127
+ if (resourceName) {
128
+ const model = await studioClient.getModels(resourceName);
129
+ if (model) {
130
+ const source = model.replace(model.substring(0, model.indexOf(`{`) - 1), ``).replace(/;/g, ``);
131
+ return getGenericFromDataStore(JSON.parse(source));
132
+ }
133
+ }
134
+ printer.debug(`Provided ResourceName: ${resourceName} did not yield Models.`);
135
+ return undefined;
136
+ } catch (error) {
137
+ printer.debug(error.toString());
138
+ return undefined;
139
+ }
140
+ };
@@ -0,0 +1,32 @@
1
+ import { $TSContext, stateManager } from 'amplify-cli-core';
2
+ import { extractArgs } from './extractArgs';
3
+
4
+ /**
5
+ * Get's the environment name from the given name or the context
6
+ */
7
+ export const getEnvName = (context: $TSContext, envName?: string): string => {
8
+ const args = extractArgs(context);
9
+ return envName || args?.environmentName || context.exeInfo.localEnvInfo.envName;
10
+ };
11
+
12
+ /**
13
+ * Returns the appId from the context
14
+ */
15
+ export const resolveAppId = (): string | undefined => {
16
+ const meta = stateManager.getMeta();
17
+ return meta?.providers?.awscloudformation?.AmplifyAppId;
18
+ };
19
+
20
+ /**
21
+ * Returns the appId or throws an error if it's not found.
22
+ */
23
+ export const getAppId = (context: $TSContext, appId?: string): string => {
24
+ const resolvedAppId = appId || extractArgs(context).appId || resolveAppId();
25
+
26
+ if (!resolvedAppId) {
27
+ throw new Error(
28
+ 'Unable to sync Studio components since appId could not be determined. This can happen when you hit the soft limit of number of apps that you can have in Amplify console.',
29
+ );
30
+ }
31
+ return resolvedAppId;
32
+ };
@@ -0,0 +1,7 @@
1
+ export * from './codegenResources';
2
+ export * from './environmentHelpers';
3
+ export * from './extractArgs';
4
+ export * from './getUiBuilderComponentsPath';
5
+ export * from './notifyMissingPackages';
6
+ export * from './shouldRenderComponents';
7
+ export * from './syncAmplifyUiBuilderComponents';