Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gofynd/fdk-cli",
"version": "8.0.8",
"version": "8.0.9",
"main": "index.js",
"license": "MIT",
"bin": {
Expand Down
69 changes: 69 additions & 0 deletions src/__tests__/setup_extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function cleanUp() {
rimraf.sync(extensionList.items[0].name);
rimraf.sync('app-ext');
rimraf.sync('payment-ext');
rimraf.sync('unique-payment-ext');
rimraf.sync('fdk.ext.config.json');
rimraf.sync('frontend/fdk.ext.config.json');
rimraf.sync(CONSTANTS.EXTENSION_CONTEXT_FILE_NAME);
Expand Down Expand Up @@ -262,6 +263,12 @@ describe('Extension Commands', () => {
});

it('should create a payment extension', async () => {
await mockAxios
.onGet(URLS.CHECK_PAYMENT_EXTENSION_NAME('payment-ext'))
.reply(200, {
is_valid: true,
});

const inquirerMock = mockFunction(inquirer.prompt);
inquirerMock
.mockResolvedValueOnce({
Expand All @@ -282,6 +289,68 @@ describe('Extension Commands', () => {
expect(fs.existsSync('payment-ext')).toBe(true);
});

it('should re-prompt and validate payment extension name before asking slug', async () => {
await mockAxios
.onGet(URLS.CHECK_PAYMENT_EXTENSION_NAME('payment-ext'))
.reply(200, {
is_valid: false,
error_message: "Payment extension name 'payment-ext' is already in use.",
});
await mockAxios
.onGet(URLS.CHECK_PAYMENT_EXTENSION_NAME('unique-payment-ext'))
.reply(200, {
is_valid: true,
});

const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => undefined);
const inquirerMock = mockFunction(inquirer.prompt);
inquirerMock
.mockResolvedValueOnce({
action: CONSTANTS.INIT_ACTIONS.create_extension,
})
.mockResolvedValueOnce({ name: 'payment-ext' })
.mockResolvedValueOnce({ type: 'Private', launch_type: 'Payment' })
.mockResolvedValueOnce({ name: 'unique-payment-ext' })
.mockResolvedValueOnce({ payment_mode_slug: 'unique-payment-ext' })
.mockResolvedValueOnce({
project_type: 'Node + React.js + SQLite(Payment)',
});

try {
await program.parseAsync([
'ts-node',
'./src/fdk.ts',
'extension',
'init',
]);

expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining("Payment extension name 'payment-ext' is already in use."),
);
const paymentNameQuestionCall = inquirerMock.mock.calls.find(([questions]) => (
Array.isArray(questions)
&& questions[0]?.name === 'name'
&& questions[0]?.message === 'Enter a different Extension name :'
));
expect(paymentNameQuestionCall).toBeDefined();

const paymentNameQuestion = paymentNameQuestionCall[0][0];
expect(paymentNameQuestion.name).toBe('name');
expect(paymentNameQuestion.message).toBe('Enter a different Extension name :');
expect(paymentNameQuestion.default).toBeUndefined();
expect(await paymentNameQuestion.validate('payment-ext')).toBe(
"Payment extension name 'payment-ext' is already in use.",
);
expect(await paymentNameQuestion.validate('unique-payment-ext')).toBe(true);

const registerPayload = JSON.parse(mockAxios.history.post[0].data);
expect(registerPayload.name).toBe('unique-payment-ext');
expect(fs.existsSync('unique-payment-ext')).toBe(true);
} finally {
consoleSpy.mockRestore();
}
});

it('should select an existing extension', async () => {
const inquirerMock = mockFunction(inquirer.prompt);
inquirerMock
Expand Down
16 changes: 15 additions & 1 deletion src/helper/extension_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,4 +317,18 @@ export const checkAndValidatePaymentSlug = async(slug: string) => {
}

return true;
}
}

export const checkAndValidatePaymentExtensionName = async(name: string) => {
const validInput = validateEmpty(name);
if(!validInput){
return 'Extension name is required';
}

const isNameAvailable = await ExtensionService.checkPaymentExtensionName(String(name).trim());
if(!isNameAvailable.is_valid){
return isNameAvailable.error_message || 'Payment extension name already exists. Enter a different name.';
}

return true;
}
25 changes: 20 additions & 5 deletions src/lib/Extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
validateEmpty,
replaceContent,
selectExtensionFromList,
checkAndValidatePaymentSlug
checkAndValidatePaymentSlug,
checkAndValidatePaymentExtensionName
} from '../helper/extension_utils';

import { createDirectory, writeFile, readFile } from '../helper/file.utils';
Expand Down Expand Up @@ -123,10 +124,6 @@ export default class Extension {
answers.name = String(value.name).trim();
});
}
answers.targetDir = options['targetDir'] || answers.name;

Extension.checkFolderAndGitExists(answers.targetDir, isExtensionNameFixed);

const extensionTypeQuestions = [];

// If user wants to create new extension then ask type else it is already set in above section
Expand Down Expand Up @@ -159,6 +156,20 @@ export default class Extension {
// If launch type is Payment, ask for payment mode slug right after launch type selection
// Only ask for new extensions, not when selecting existing extensions
if (prompt_answers.launch_type === LAUNCH_TYPES.PAYMENT && action === INIT_ACTIONS.create_extension) {
const paymentExtensionNameValidation = await checkAndValidatePaymentExtensionName(answers.name);
if (paymentExtensionNameValidation !== true) {
console.log(`${chalk.red('>>')} ${paymentExtensionNameValidation}`);
const paymentExtensionNameAnswer = await inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Enter a different Extension name :',
validate: checkAndValidatePaymentExtensionName,
},
]);
answers.name = String(paymentExtensionNameAnswer.name).trim();
}

const paymentModeAnswer = await inquirer.prompt([
{
type: 'input',
Expand All @@ -170,6 +181,10 @@ export default class Extension {
prompt_answers = { ...prompt_answers, ...paymentModeAnswer };
}

answers.targetDir = options['targetDir'] || answers.name;

Extension.checkFolderAndGitExists(answers.targetDir, isExtensionNameFixed);

if(template){
const selectedLaunchType = prompt_answers.launch_type;
if(TEMPLATES[template].launchTypes.includes(selectedLaunchType)){
Expand Down
10 changes: 10 additions & 0 deletions src/lib/api/services/extension.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,5 +285,15 @@ export default {
} catch (error) {
throw error;
}
},

checkPaymentExtensionName: async (name: string) => {
try {
const axiosOption = Object.assign({}, getCommonHeaderOptions());
const res = await ApiClient.get(URLS.CHECK_PAYMENT_EXTENSION_NAME(name), axiosOption);
return res.data;
} catch (error) {
throw error;
}
}
};
7 changes: 7 additions & 0 deletions src/lib/api/services/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,5 +295,12 @@ export const URLS = {
PAYMENT_URL(),
`organization/${getOrganizationId()}/payment/slug?slug=${slug}`,
);
},

CHECK_PAYMENT_EXTENSION_NAME: (name: string) => {
return urlJoin(
PAYMENT_URL(),
`organization/${getOrganizationId()}/payment/slug?name=${encodeURIComponent(name)}`,
);
}
};
Loading