@aws-amplify/amplify-util-uibuilder 1.4.1-beta.0 → 1.4.2-rc.cf54accdc.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 +92 -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 +15 -8
  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
@@ -0,0 +1,102 @@
1
+ import aws from 'aws-sdk'; // eslint-disable-line import/no-extraneous-dependencies
2
+ import { AmplifyCategories, AmplifySupportedService } from 'amplify-cli-core'; // eslint-disable-line import/no-extraneous-dependencies
3
+ import { printer } from 'amplify-prompts'; // eslint-disable-line import/no-extraneous-dependencies
4
+ import { AmplifyStudioClient } from '../clients';
5
+ import { getAmplifyDataSchema } from '../commands/utils';
6
+
7
+ const awsMock = aws as any;
8
+ const printerMock = printer as any;
9
+
10
+ jest.mock('amplify-cli-core', () => ({
11
+ ...jest.requireActual('amplify-cli-core'),
12
+ stateManager: {
13
+ getMeta: jest.fn(() => ({
14
+ [AmplifyCategories.API]: {
15
+ MyResourceName: {
16
+ service: AmplifySupportedService.APPSYNC,
17
+ },
18
+ },
19
+ })),
20
+ },
21
+ FeatureFlags: {
22
+ getBoolean: () => false,
23
+ getNumber: () => 0,
24
+ },
25
+ }));
26
+ jest.mock('amplify-prompts');
27
+
28
+ const getMockedSchema = jest.fn(
29
+ // eslint-disable-next-line no-useless-escape, spellcheck/spell-checker
30
+ () => `export const schema = {\n \"models\": {\n \"Blog\": {\n \"name\": \"Blog\",\n \"fields\": {\n \"id\": {\n \"name\": \"id\",\n \"isArray\": false,\n \"type\": \"ID\",\n \"isRequired\": true,\n \"attributes\": []\n },\n \"title\": {\n \"name\": \"title\",\n \"isArray\": false,\n \"type\": \"String\",\n \"isRequired\": false,\n \"attributes\": []\n },\n \"content\": {\n \"name\": \"content\",\n \"isArray\": false,\n \"type\": \"String\",\n \"isRequired\": false,\n \"attributes\": []\n },\n \"status\": {\n \"name\": \"status\",\n \"isArray\": false,\n \"type\": {\n \"enum\": \"Status\"\n },\n \"isRequired\": false,\n \"attributes\": []\n },\n \"createdAt\": {\n \"name\": \"createdAt\",\n \"isArray\": false,\n \"type\": \"AWSDateTime\",\n \"isRequired\": false,\n \"attributes\": [],\n \"isReadOnly\": true\n },\n \"updatedAt\": {\n \"name\": \"updatedAt\",\n \"isArray\": false,\n \"type\": \"AWSDateTime\",\n \"isRequired\": false,\n \"attributes\": [],\n \"isReadOnly\": true\n }\n },\n \"syncable\": true,\n \"pluralName\": \"Blogs\",\n \"attributes\": [\n {\n \"type\": \"model\",\n \"properties\": {}\n },\n {\n \"type\": \"auth\",\n \"properties\": {\n \"rules\": [\n {\n \"allow\": \"public\",\n \"operations\": [\n \"create\",\n \"update\",\n \"delete\",\n \"read\"\n ]\n }\n ]\n }\n }\n ]\n }\n },\n \"enums\": {\n \"Status\": {\n \"name\": \"Status\",\n \"values\": [\n \"ENABLED\",\n \"DISABLED\"\n ]\n }\n },\n \"nonModels\": {},\n \"version\": \"demo\"\n};`,
31
+ );
32
+
33
+ describe('should sync amplify backend models', () => {
34
+ let context: any;
35
+
36
+ beforeEach(() => {
37
+ context = {
38
+ exeInfo: {
39
+ projectConfig: {
40
+ javascript: {
41
+ config: {
42
+ SourceDir: 'src',
43
+ },
44
+ },
45
+ },
46
+ },
47
+ amplify: {
48
+ invokePluginMethod: () => ({}),
49
+ },
50
+ parameters: {
51
+ argv: [],
52
+ },
53
+ input: {
54
+ options: {
55
+ appId: 'testAppId',
56
+ envName: 'testEnvName',
57
+ },
58
+ },
59
+ };
60
+ });
61
+
62
+ it('should getAmplifyBackendModels', async () => {
63
+ awsMock.AmplifyBackend = jest.fn(() => ({
64
+ getBackendAPIModels: jest.fn(() => ({
65
+ promise: () => ({
66
+ Models: getMockedSchema(),
67
+ }),
68
+ })),
69
+ }));
70
+ const client = await AmplifyStudioClient.setClientInfo(context);
71
+ const dataSchema = await getAmplifyDataSchema(client);
72
+ expect(getMockedSchema).toBeCalled();
73
+ expect(dataSchema).toBeDefined();
74
+ expect(Object.keys(dataSchema!.models)).toContain('Blog');
75
+ });
76
+
77
+ it('should handle Models not found', async () => {
78
+ awsMock.AmplifyBackend = jest.fn(() => ({
79
+ getBackendAPIModels: jest.fn(() => ({
80
+ promise: jest.fn(() => ({
81
+ Models: undefined,
82
+ })),
83
+ })),
84
+ }));
85
+ const client = await AmplifyStudioClient.setClientInfo(context);
86
+ const dataSchema = await getAmplifyDataSchema(client);
87
+ expect(dataSchema).toBeUndefined();
88
+ expect(printerMock.debug).toHaveBeenCalledWith('Provided ResourceName: MyResourceName did not yield Models.');
89
+ });
90
+
91
+ it('should handle network error', async () => {
92
+ awsMock.AmplifyBackend = jest.fn(() => ({
93
+ getBackendAPIModels: jest.fn(() => ({
94
+ promise: jest.fn().mockRejectedValue(new Error('Network Error')),
95
+ })),
96
+ }));
97
+ const client = await AmplifyStudioClient.setClientInfo(context);
98
+ const dataSchema = await getAmplifyDataSchema(client);
99
+ expect(dataSchema).toBeUndefined();
100
+ expect(printerMock.debug).toHaveBeenCalledWith('Error: Models not found in AmplifyBackend:GetBackendAPIModels response: Network Error');
101
+ });
102
+ });
@@ -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);