@ordino.ai/cli
Version:
ordino.ai global command line interface
441 lines (394 loc) • 11.4 kB
text/typescript
import inquirer from "inquirer";
import { systemApiKeyService } from "../service/ordino.systemApiKey.service";
import { userApiKeyService } from "../service/ordino.userApiKey.service";
import { printMessage } from "../utils/printMessage.util";
import { userService } from "../service/ordino.user.service";
import { teamService } from "../service/ordino.team.service";
import { projectService } from "../service/ordino.project.service";
import {
User,
Team,
Project,
projectTypes,
ApplicationType,
} from "../models/models";
import { serviceFactory } from "../config/service-factory";
import { getApiEndpoints } from "../config/api-endpoints";
// Types for the prompts
type SystemLoginAnswers = {
username: string;
password: string;
};
type ProjectAnswer = {
project: Project
};
type InitialPromptAnswers = {
isCompanyIDAvailable: boolean;
};
export type ProjectTypeAnswers = {
authToken: string;
projectType: projectTypes;
};
type MicroFrontendAnswers = {
clientTestProjectURL: string;
testProjectURL: string;
};
type TestPromptAnswers = {
includeSampleTest: boolean;
};
type GitUrlAndYmlAnswers = {
gitUrl: string | null;
needGitActionSetup: boolean;
};
// Functions
// Initial prompt to ask if the company ID is available
async function getInitialPrompts(): Promise<InitialPromptAnswers> {
return inquirer.prompt([
{
type: "confirm",
name: "isCompanyIDAvailable",
message: "Do you have a company ID?",
default: true,
},
]);
}
// Validate the provided API key
async function validateApiKey(apiKey: string): Promise<true | string> {
const frameworkVersion = "1.0.0";
if (apiKey.trim() === "") {
return "API key is required!!!";
}
try {
// Get the current environment and corresponding endpoints
const currentEnvironment = serviceFactory.getEnvironment();
const endpoints = getApiEndpoints(currentEnvironment);
const response = await fetch(endpoints.authentication, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ apiKey, frameworkVersion }),
});
if (!response.ok) {
return "Use a valid key.";
}
const data = await response.json();
switch (data.status) {
case "INVALID":
return "Use a valid key.";
case "FRAMEWORK_OUT_DATED":
return "Update your CLI.";
case "VALID":
return true;
default:
return "Unexpected response. Please try again.";
}
} catch (error) {
return `Use a valid key. ${error}`;
}
}
// Prompt for project type and company ID
async function projectType(): Promise<ProjectTypeAnswers> {
return inquirer.prompt([
{
type: "input",
name: "authToken",
message: "Provide the Company ID:",
validate: validateApiKey,
},
{
type: "list",
name: "projectType",
message: "What is the type of your project? (Choose one: UI / API):",
choices: mapEnumToChoices(),
},
]);
}
// Get prompts for Micro Frontend project URLs
async function getMicroFrontendPrompts(): Promise<MicroFrontendAnswers> {
return inquirer.prompt([
{
type: "input",
name: "clientTestProjectURL",
message: "Please add the Client Test Project URL:",
},
{
type: "input",
name: "testProjectURL",
message: "Please add the Test Project URL:",
},
]);
}
// Get prompts for project name and company
async function getStandardPrompts(
projectName: string,
companyName: string
): Promise<string> {
const suggestedName = `${companyName}-${projectName}-test`;
const nameConfirmation = await inquirer.prompt<{ useSuggestedName: boolean }>(
[
{
type: "confirm",
name: "useSuggestedName",
message: `Would you like the tool to suggest a project name (${suggestedName})?`,
default: true,
},
]
);
if (!nameConfirmation.useSuggestedName) {
const customName = await inquirer.prompt<{ customProjectName: string }>([
{
type: "input",
name: "customProjectName",
message: "Please enter a project name:",
validate: (input: string) =>
input.trim() !== "" || "Project name cannot be empty.",
},
]);
return customName.customProjectName;
}
return suggestedName;
}
// Get project details based on API key and project type
async function getProjectName(
apiKey: string,
projectType: projectTypes
): Promise<Project | null> {
const partnerId = await getPartnerId(apiKey);
if (!partnerId) {
return null;
}
const users: User[] = await userService.getAllUsers(partnerId);
const teams: Team[] = await teamService.getAllTeams(partnerId);
const projects: Project[] = await projectService.getAllProjects();
if (projects.length > 0) {
const { projectId } = await inquirer.prompt({
type: "list",
name: "projectId",
message: "Select a project:",
choices: projects.map((project) => ({
name: project.name,
value: project.id,
})),
});
return projects.find(({ id }) => id === projectId) ?? null;
} else if (teams.length > 0) {
const { teamId } = await inquirer.prompt({
type: "list",
name: "teamId",
message: "Select a team:",
choices: teams.map((team) => ({ name: team.name, value: team.id })),
});
const { projectName } = await inquirer.prompt([
{
type: "input",
name: "projectName",
message: "Enter the project name:",
},
]);
const { userId } = await inquirer.prompt({
type: "list",
name: "userId",
message: "Select a user:",
choices: users.map((user) => ({
name: user.information.email,
value: user.id,
})),
});
const project = await projectService.cerateProject({
partnerId: partnerId,
teamId: teamId,
name: projectName,
applicationTypes: getApplicationType(projectType),
qaLeadId: userId,
isRegressionSuite: false,
isCloudEnabled: false,
});
return project ?? null;
} else {
const { teamName, projectName } = await inquirer.prompt([
{
type: "input",
name: "teamName",
message: "Enter the team name:",
},
{
type: "input",
name: "projectName",
message: "Enter the project name:",
},
]);
const team = await teamService.createTeam({
partnerId: partnerId,
name: teamName,
description: "",
applicationTypes: getApplicationType(projectType),
integrationDefinitionIds: ["TEST_INTEGRATION"],
});
const { userId } = await inquirer.prompt({
type: "list",
name: "userId",
message: "Select a user:",
choices: users.map((user) => ({
name: user.information.email,
value: user.id,
})),
});
const project = await projectService.cerateProject({
partnerId: partnerId,
teamId: team.id,
name: projectName,
applicationTypes: getApplicationType(projectType),
qaLeadId: userId,
isRegressionSuite: false,
isCloudEnabled: false,
});
return project ?? null;
}
}
// Prompt for whether to include a sample test
async function getTestPrompt(): Promise<TestPromptAnswers> {
return inquirer.prompt([
{
type: "confirm",
name: "includeSampleTest",
message: "Do you want to add a sample test? (yes/no):",
default: true,
},
]);
}
// Map project types to choices for user selection
function mapEnumToChoices(): { name: string; value: projectTypes }[] {
return Object.keys(projectTypes)
.filter((key) => isNaN(Number(key)))
.map((key) => ({
name: key,
value: projectTypes[key as keyof typeof projectTypes],
}));
}
// Get application types based on project type
function getApplicationType(projectType: projectTypes) {
switch (projectType) {
case projectTypes.UI:
return [ApplicationType.WEB_UI];
case projectTypes.API:
return [ApplicationType.API];
case projectTypes.UI_AND_API:
return [ApplicationType.WEB_UI, ApplicationType.API];
default:
throw new Error("Invalid project type");
}
}
// Get the partner ID based on the API key
async function getPartnerId(apiKey: string): Promise<string | null> {
const partnerId: string | null = null;
try {
if (apiKey.startsWith("SYS:")) {
const res = await systemApiKeyService.getBySystemApiKey(apiKey);
return res.partnerId;
}
if (apiKey.startsWith("USR:")) {
const res = await userApiKeyService.getByUserApiKey(apiKey);
return res.partnerId;
}
} catch (error) {
printMessage(
`Please check you api key is valid. If it's valid please contact support team https://www.ordino.ai/support`,
"35",
true,
true
);
return partnerId;
}
return partnerId;
}
// Convert project name to a valid package name
export function convertToValidPackageName(projectName: string) {
const validName = projectName
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/[^a-z0-9-]/g, "")
.replace(/^-+/, "")
.replace(/-+$/, "");
return validName;
}
async function handleGitAndYml(): Promise<GitUrlAndYmlAnswers> {
const gitUrlResponse = await inquirer.prompt([
{
type: "confirm",
name: "gitUrl",
message: "Do you want to set up a Git repository?",
default: true,
},
]);
let gitUrl = null;
let needGitActionSetup = false;
if (gitUrlResponse.gitUrl) {
const gitUrlInput = await inquirer.prompt([
{
type: "input",
name: "gitUrl",
message: "Please provide the Git repository URL:",
validate: (input: string) =>
input.trim() !== "" ? true : "Git URL cannot be empty.",
},
{
type: "confirm",
name: "needGitActionSetup",
message: "Do you want to use GitHub Actions for CI/CD?",
default: true,
},
]);
gitUrl = gitUrlInput.gitUrl;
needGitActionSetup = gitUrlInput.needGitActionSetup;
}
return { gitUrl, needGitActionSetup };
}
//Initial prompt to ask login credentials
async function getLoginPrompts(): Promise<SystemLoginAnswers> {
return inquirer.prompt<SystemLoginAnswers>([
{
type: "input",
name: "username",
message: "Ordino portal login username?",
validate: (input: string) =>
input.trim() !== "" ? true : "Username cannot be empty.",
},
{
type: "password",
name: "password",
message: "Ordino portal login password?",
validate: (input: string) =>
input.trim() !== "" ? true : "password cannot be empty.",
},
]);
}
//Get projects list
async function getProjectsList(projectsList: Project[]): Promise<ProjectAnswer> {
return inquirer.prompt([
{
type: "list",
name: "project",
message: "Please select the project?",
choices: mapProjectsList(projectsList),
}
]);
}
//Map projects list.
function mapProjectsList(projectsList: Project[]): { name: string, value: Project }[] {
return projectsList.map((project) => ({
name: project.name,
value: project
}));
}
export {
getLoginPrompts,
getProjectsList,
getInitialPrompts,
getMicroFrontendPrompts,
getStandardPrompts,
getTestPrompt,
projectType,
getProjectName,
handleGitAndYml,
};