@contentful/app-scripts 1.21.0 → 1.22.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/lib/analytics.js CHANGED
@@ -26,7 +26,7 @@ function track({ command, ci }) {
26
26
  command,
27
27
  ci: String(ci),
28
28
  },
29
- anonymousId: Date.now().toString(),
29
+ anonymousId: Date.now().toString(), // generate a random id
30
30
  timestamp: new Date(),
31
31
  });
32
32
  // eslint-disable-next-line no-empty
@@ -1,8 +1,13 @@
1
- import { AppLocation, FieldType } from 'contentful-management';
1
+ import { AppLocation, FieldType, ParameterDefinition, InstallationParameterType } from 'contentful-management';
2
2
  export interface AppDefinitionSettings {
3
3
  name: string;
4
4
  locations: AppLocation['location'][];
5
5
  fields?: FieldType[];
6
6
  host?: string;
7
+ buildAppParameters: boolean;
8
+ parameters?: {
9
+ instance: ParameterDefinition[];
10
+ installation: ParameterDefinition<InstallationParameterType>[];
11
+ };
7
12
  }
8
13
  export declare function buildAppDefinitionSettings(): Promise<AppDefinitionSettings>;
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  const inquirer_1 = __importDefault(require("inquirer"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const constants_1 = require("../constants");
11
+ const build_app_parameter_settings_1 = require("./build-app-parameter-settings");
11
12
  async function buildAppDefinitionSettings() {
12
13
  console.log(chalk_1.default.dim(`
13
14
  NOTE: This will create an app definition in your Contentful organization.
@@ -79,7 +80,16 @@ NOTE: This will create an app definition in your Contentful organization.
79
80
  message: `Contentful CMA endpoint URL:`,
80
81
  default: constants_1.DEFAULT_CONTENTFUL_API_HOST,
81
82
  },
83
+ {
84
+ name: 'buildAppParameters',
85
+ message: 'Would you like to specify App Parameter schemas? (see https://ctfl.io/app-parameters)',
86
+ type: 'confirm',
87
+ default: false,
88
+ },
82
89
  ]);
90
+ if (appDefinitionSettings.buildAppParameters) {
91
+ appDefinitionSettings.parameters = await (0, build_app_parameter_settings_1.buildAppParameterSettings)();
92
+ }
83
93
  appDefinitionSettings.locations = ['dialog', ...appDefinitionSettings.locations];
84
94
  return appDefinitionSettings;
85
95
  }
@@ -0,0 +1,5 @@
1
+ import { InstallationParameterType, ParameterDefinition } from 'contentful-management';
2
+ export declare function buildAppParameterSettings(): Promise<{
3
+ instance: ParameterDefinition[];
4
+ installation: ParameterDefinition<InstallationParameterType>[];
5
+ }>;
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildAppParameterSettings = void 0;
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const lodash_1 = require("lodash");
9
+ const PARAMETER_ID_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/;
10
+ const validateDefault = (input, type, options) => {
11
+ if (input === '')
12
+ return true;
13
+ switch (type) {
14
+ case 'Symbol':
15
+ return (0, lodash_1.isString)(input) || 'Default value must be a string.';
16
+ case 'Enum':
17
+ if (!(0, lodash_1.isString)(input))
18
+ return 'Default value must be a string.';
19
+ else if (options && !options.includes(input))
20
+ return 'Default value must be one of the options.';
21
+ return true;
22
+ case 'Number':
23
+ return (0, lodash_1.isNumber)(Number(input)) || 'Default value must be a number.';
24
+ case 'Boolean':
25
+ return input === 'true' || input === 'false' || 'Default value must be a boolean.';
26
+ default:
27
+ return true;
28
+ }
29
+ };
30
+ const validateEnumOptions = (input, type) => {
31
+ if (type !== 'Enum')
32
+ return true;
33
+ const allString = input.every(lodash_1.isString);
34
+ const allLabelled = input.every(lodash_1.isPlainObject);
35
+ return allString || allLabelled || 'Options should be all strings or all label objects.';
36
+ };
37
+ async function promptForParameter() {
38
+ const parameter = await inquirer_1.default.prompt([
39
+ {
40
+ name: 'instanceOrInstallation',
41
+ message: 'Is this an Instance or an Installation parameter?',
42
+ type: 'list',
43
+ choices: ['Instance', 'Installation'],
44
+ },
45
+ {
46
+ name: 'name',
47
+ message: 'Parameter name:',
48
+ validate(input) {
49
+ return input ? true : 'Parameter name is required.';
50
+ },
51
+ },
52
+ {
53
+ name: 'id',
54
+ message: 'Parameter ID:',
55
+ validate(input) {
56
+ if (!input)
57
+ return 'Parameter ID is required.';
58
+ else if (!PARAMETER_ID_RE.test(input))
59
+ return 'Parameter ID must start with a letter and contain only letters, numbers, and underscores.';
60
+ return true;
61
+ },
62
+ },
63
+ {
64
+ name: 'description',
65
+ message: 'Parameter description (optional):',
66
+ },
67
+ {
68
+ name: 'type',
69
+ message: 'Parameter type:',
70
+ type: 'list',
71
+ choices(answers) {
72
+ const parameterTypes = ['Boolean', 'Symbol', 'Number', 'Enum'];
73
+ // TODO uncomment when secret app installation parameters are finalized in the API
74
+ // if (answers.instanceOrInstallation === 'Installation') {
75
+ // parameterTypes.push('Secret');
76
+ // }
77
+ return parameterTypes;
78
+ },
79
+ },
80
+ {
81
+ name: 'required',
82
+ message: 'Is this parameter required?',
83
+ type: 'confirm',
84
+ default: false,
85
+ },
86
+ {
87
+ name: 'options',
88
+ message: 'Parameter options (comma-separated) (optional):',
89
+ when(answers) {
90
+ return answers.type === 'Enum';
91
+ },
92
+ filter(input) {
93
+ return input ? input.split(',').map((opt) => opt.trim()) : [];
94
+ },
95
+ validate(input, answers) {
96
+ if (!input)
97
+ return 'Options are required for Enum parameters.';
98
+ return validateEnumOptions(input, answers.type);
99
+ },
100
+ },
101
+ {
102
+ name: 'default',
103
+ message: 'Default value (leave blank if none):',
104
+ // TODO uncomment when secret app installation parameters are finalized in the API
105
+ // when(answers) {
106
+ // return answers.type !== 'Secret';
107
+ // },
108
+ validate(input, answers) {
109
+ return validateDefault(input, answers.type, answers.options);
110
+ },
111
+ },
112
+ {
113
+ name: 'booleanLabels',
114
+ message: 'Parameter labels (true/false comma-separated) (optional):',
115
+ when(answers) {
116
+ return answers.type === 'Boolean';
117
+ },
118
+ filter(input) {
119
+ const labels = input ? input.split(',').map((label) => label.trim()) : [];
120
+ return JSON.stringify({
121
+ true: labels[0] || '',
122
+ false: labels[1] || '',
123
+ });
124
+ },
125
+ },
126
+ {
127
+ name: 'enumEmptyLabel',
128
+ message: 'Empty label (optional):',
129
+ filter(input) {
130
+ return JSON.stringify({
131
+ empty: input.trim(),
132
+ });
133
+ },
134
+ when(answers) {
135
+ return answers.type === 'Enum';
136
+ },
137
+ },
138
+ ]);
139
+ return parameter;
140
+ }
141
+ async function buildAppParameterSettings() {
142
+ const parameters = {
143
+ instance: [],
144
+ installation: [],
145
+ };
146
+ let addMore = true;
147
+ while (addMore) {
148
+ try {
149
+ const parameter = await promptForParameter();
150
+ const labels = parameter.booleanLabels || parameter.enumEmptyLabel;
151
+ parameters[parameter.instanceOrInstallation.toLowerCase()].push({
152
+ id: parameter.id,
153
+ name: parameter.name,
154
+ description: parameter.description,
155
+ type: parameter.type,
156
+ required: parameter.required,
157
+ default: parameter.default,
158
+ options: parameter.options,
159
+ labels: labels ? JSON.parse(labels) : undefined,
160
+ });
161
+ }
162
+ catch (e) {
163
+ console.error('Failed to build parameter', e);
164
+ }
165
+ const { addAnother } = await inquirer_1.default.prompt({
166
+ name: 'addAnother',
167
+ message: 'Do you want to add another parameter?',
168
+ type: 'confirm',
169
+ default: false,
170
+ });
171
+ addMore = addAnother;
172
+ }
173
+ if ((0, lodash_1.uniq)(parameters.instance.map((p) => p.id)).length !== parameters.instance.length) {
174
+ console.log('Instance parameter IDs must be unique.');
175
+ return buildAppParameterSettings();
176
+ }
177
+ if ((0, lodash_1.uniq)(parameters.installation.map((p) => p.id)).length !== parameters.installation.length) {
178
+ console.log('Installation parameter IDs must be unique.');
179
+ return buildAppParameterSettings();
180
+ }
181
+ return parameters;
182
+ }
183
+ exports.buildAppParameterSettings = buildAppParameterSettings;
@@ -33,7 +33,7 @@ function assertValidArguments(accessToken, appDefinitionSettings) {
33
33
  (0, utils_1.throwValidationException)('AccessToken', `Expected string got ${typeof accessToken}`);
34
34
  }
35
35
  if (!(0, lodash_1.isPlainObject)(appDefinitionSettings) || !(0, lodash_1.has)(appDefinitionSettings, 'locations')) {
36
- (0, utils_1.throwValidationException)('AppDefinitionSettings', `Expected plain object with 'location' property, got ${JSON.stringify(appDefinitionSettings, null, 2)}`, `Example: ${JSON.stringify({
36
+ (0, utils_1.throwValidationException)('AppDefinitionSettings', `Expected plain object with 'locations' property, got ${JSON.stringify(appDefinitionSettings, null, 2)}`, `Example: ${JSON.stringify({
37
37
  name: 'app-name',
38
38
  locations: ['entry-field'],
39
39
  fields: [{ type: 'Boolean' }],
@@ -61,6 +61,14 @@ async function createAppDefinition(accessToken, appDefinitionSettings) {
61
61
  location,
62
62
  };
63
63
  }),
64
+ parameters: {
65
+ ...(appDefinitionSettings.parameters?.instance && {
66
+ instance: appDefinitionSettings.parameters.instance,
67
+ }),
68
+ ...(appDefinitionSettings.parameters?.installation && {
69
+ installation: appDefinitionSettings.parameters.installation,
70
+ }),
71
+ },
64
72
  };
65
73
  try {
66
74
  const organization = await client.getOrganization(organizationId);
@@ -1,8 +1,8 @@
1
1
  export declare const getAppInfo: ({ organizationId, definitionId, token, host, }: {
2
- organizationId?: string | undefined;
3
- definitionId?: string | undefined;
4
- token?: string | undefined;
5
- host?: string | undefined;
2
+ organizationId?: string;
3
+ definitionId?: string;
4
+ token?: string;
5
+ host?: string;
6
6
  }) => Promise<{
7
7
  accessToken: string;
8
8
  organization: import("./organization-api").Organization;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/app-scripts",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "A collection of scripts for building Contentful Apps",
5
5
  "author": "Contentful GmbH",
6
6
  "license": "MIT",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@segment/analytics-node": "^2.0.0",
51
- "adm-zip": "0.5.12",
51
+ "adm-zip": "0.5.14",
52
52
  "bottleneck": "2.19.5",
53
53
  "chalk": "4.1.2",
54
54
  "commander": "12.1.0",
@@ -60,16 +60,19 @@
60
60
  "open": "8.4.2",
61
61
  "ora": "5.4.1"
62
62
  },
63
- "gitHead": "81cc3ea1a7090350e4791112816a82b243c3fc14",
63
+ "gitHead": "7aee855902d01d921a74350772f24ce1d90aeff8",
64
64
  "devDependencies": {
65
65
  "@tsconfig/node18": "18.2.4",
66
66
  "@types/adm-zip": "0.5.5",
67
67
  "@types/analytics-node": "3.1.14",
68
+ "@types/chai": "4.3.16",
68
69
  "@types/inquirer": "8.2.1",
69
70
  "@types/lodash": "4.17.4",
70
71
  "@types/mocha": "10.0.6",
71
72
  "@types/proxyquire": "1.3.31",
72
73
  "@types/sinon": "17.0.3",
74
+ "chai": "5.1.1",
75
+ "mocha": "10.4.0",
73
76
  "proxyquire": "2.1.3",
74
77
  "sinon": "18.0.0",
75
78
  "ts-mocha": "10.0.0",