Skip to content
This repository has been archived by the owner on Jul 2, 2024. It is now read-only.

DEVPROD-834: Allow projects to define multiple metadata links #2230

Merged
merged 5 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
67 changes: 46 additions & 21 deletions cypress/integration/projectSettings/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,62 @@ import { clickSave } from "../../utils";

describe("Plugins", () => {
const patchPage = "version/5ecedafb562343215a7ff297";
it("Should set an external link to render on patch metadata panel and then unset it to revert the changes", () => {
// Set the external link
cy.visit(getPluginsRoute(projectUseRepoEnabled));
cy.dataCy("requesters-input").click();
cy.getInputByLabel("Commits").check({ force: true });

const addMetadataLink = (metadataLink: {
displayName: string;
url: string;
}) => {
cy.contains("button", "Add metadata link").scrollIntoView();
cy.contains("button", "Add metadata link").click();
cy.dataCy("requesters-input").first().click();
cy.getInputByLabel("Patches").check({ force: true });
cy.dataCy("requesters-input").click();
cy.dataCy("display-name-input").type("An external link");
cy.dataCy("url-template-input").type("https://example.com/{version_id}", {
cy.dataCy("requesters-input").first().click();
cy.dataCy("display-name-input").first().type(metadataLink.displayName);
cy.dataCy("url-template-input").first().type(metadataLink.url, {
parseSpecialCharSequences: false,
});
};

it("Should be able to set external links to render on patch metadata panel", () => {
// Add external links.
cy.visit(getPluginsRoute(projectUseRepoEnabled));
addMetadataLink({
displayName: "An external link 1",
url: "https://example-1.com/{version_id}",
});
addMetadataLink({
displayName: "An external link 2",
url: "https://example-2.com/{version_id}",
});
cy.dataCy("save-settings-button").scrollIntoView();
clickSave();

cy.visit(patchPage);
cy.dataCy("external-link").contains("An external link");
cy.dataCy("external-link").should(
"have.attr",
"href",
"https://example.com/5ecedafb562343215a7ff297",
);
cy.dataCy("external-link").should("have.length", 2);
cy.dataCy("external-link").last().contains("An external link 1");
cy.dataCy("external-link")
.last()
.should(
"have.attr",
"href",
"https://example-1.com/5ecedafb562343215a7ff297",
);
cy.dataCy("external-link").first().contains("An external link 2");
cy.dataCy("external-link")
.first()
.should(
"have.attr",
"href",
"https://example-2.com/5ecedafb562343215a7ff297",
);

// Unset the external link
// Remove external links.
cy.visit(getPluginsRoute(projectUseRepoEnabled));
cy.dataCy("requesters-input").click();
cy.getInputByLabel("Commits").uncheck({ force: true });
cy.getInputByLabel("Patches").uncheck({ force: true });
cy.dataCy("requesters-input").click();
cy.dataCy("display-name-input").clear();
cy.dataCy("url-template-input").clear();
cy.dataCy("delete-item-button").first().click();
cy.dataCy("delete-item-button").first().click();
cy.dataCy("save-settings-button").scrollIntoView();
clickSave();

cy.visit(patchPage);
cy.dataCy("external-link").should("not.exist");
});
Expand Down
2 changes: 1 addition & 1 deletion src/components/SpruceForm/Widgets/MultiSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const MultiSelect: React.FC<EnumSpruceWidgetProps> = ({
hasStyling={false}
/>
</Dropdown>
{rawErrors.length > 0 && <Error>{rawErrors.join(", ")}</Error>}
{rawErrors?.length > 0 && <Error>{rawErrors?.join(", ")}</Error>}
</Container>
</ElementWrapper>
);
Expand Down
42 changes: 22 additions & 20 deletions src/pages/projectSettings/tabs/PluginsTab/PluginsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const PluginsTab: React.FC<TabProps> = ({
const validate = ((formData, errors) => {
const {
buildBaronSettings: { ticketSearchProjects },
externalLinks: { metadataPanelLink },
externalLinks,
} = formData;

// if a search project is defined, a create project must be defined, and vice versa
Expand All @@ -63,27 +63,29 @@ const validate = ((formData, errors) => {
);
}

const displayNameDefined = metadataPanelLink.displayName.trim() !== "";
const urlTemplateDefined = metadataPanelLink.urlTemplate.trim() !== "";
const requestersDefined = metadataPanelLink.requesters.length > 0;
externalLinks.forEach((link, idx) => {
const displayNameDefined = link.displayName.trim() !== "";
const urlTemplateDefined = link.urlTemplate.trim() !== "";
const requestersDefined = link.requesters.length > 0;

if (displayNameDefined || urlTemplateDefined || requestersDefined) {
if (!displayNameDefined) {
errors.externalLinks.metadataPanelLink.displayName.addError(
"You must specify a display name.",
);
if (displayNameDefined || urlTemplateDefined || requestersDefined) {
if (!displayNameDefined) {
errors.externalLinks[idx].displayName.addError(
"You must specify a display name.",
);
}
if (!urlTemplateDefined) {
errors.externalLinks[idx].urlTemplate.addError(
"You must specify a URL template.",
);
}
if (!requestersDefined) {
errors.externalLinks[idx].requesters.addError(
"You must specify requesters.",
);
}
}
if (!urlTemplateDefined) {
errors.externalLinks.metadataPanelLink.urlTemplate.addError(
"You must specify a URL template.",
);
}
if (!requestersDefined) {
errors.externalLinks.metadataPanelLink.requesters.addError(
"You must specify requesters.",
);
}
}
});

return errors;
}) satisfies ValidateProps<PluginsFormState>;
79 changes: 44 additions & 35 deletions src/pages/projectSettings/tabs/PluginsTab/getFormSchema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,38 +178,39 @@ export const getFormSchema = (
},
},
externalLinks: {
type: "object" as "object",
title: "Metadata Link",
properties: {
metadataPanelLink: {
type: "object" as "object",
title: "",
description:
"Add a URL to the metadata panel for versions with the specified requester. Include {version_id} in the URL template and it will be replaced by an actual version ID.",
properties: {
requesters: {
type: "array" as "array",
title: "Requesters",
uniqueItems: true,
items: {
type: "string" as "string",
anyOf: requesters.map((r) => ({
type: "string" as "string",
title: r.label,
enum: [r.value],
})),
},
},
displayName: {
type: "string" as "string",
title: "Display name",
maxLength: 40,
},
urlTemplate: {
type: "array" as "array",
title: "Metadata Links",
maxItems: 5,
items: {
type: "object" as "object",
properties: {
requesters: {
type: "array" as "array",
title: "Requesters",
uniqueItems: true,
items: {
type: "string" as "string",
title: "URL template",
format: "validURLTemplate",
anyOf: requesters.map((r) => ({
type: "string" as "string",
title: r.label,
enum: [r.value],
})),
},
default: [],
},
displayName: {
type: "string" as "string",
title: "Display name",
default: "",
minLength: 1,
maxLength: 40,
},
urlTemplate: {
type: "string" as "string",
title: "URL template",
default: "",
minLength: 1,
format: "validURLTemplate",
},
},
},
Expand Down Expand Up @@ -292,18 +293,26 @@ export const getFormSchema = (
},
externalLinks: {
"ui:rootFieldId": "externalLinks",
"ui:ObjectFieldTemplate": CardFieldTemplate,
metadataPanelLink: {
"ui:placeholder": "No metadata links are defined.",
"ui:description":
"Add URLs to the metadata panel for versions with the specified requester.",
"ui:addButtonText": "Add metadata link",
"ui:orderable": false,
"ui:useExpandableCard": true,
items: {
"ui:displayTitle": "New Metadata Link",
requesters: {
"ui:widget": widgets.MultiSelectWidget,
"ui:data-cy": "requesters-input",
},
displayName: {
"ui:data-cy": "display-name-input",
},
urlTemplate: {
"ui:placeholder": "https://example.com/{version_id}",
"ui:data-cy": "url-template-input",
},
displayName: {
"ui:data-cy": "display-name-input",
"ui:description":
"Include {version_id} in the URL template and it will be replaced by an actual version ID.",
},
},
},
Expand Down
44 changes: 34 additions & 10 deletions src/pages/projectSettings/tabs/PluginsTab/transformers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,20 @@ const projectForm: PluginsFormState = {
secret: null,
},
},
externalLinks: {
metadataPanelLink: {
externalLinks: [
{
requesters: ["gitter_request", "patch_request"],
displayName: "a link display name",
urlTemplate: "https:/a-link-template-{version_id}.com",
displayTitle: "a link display name",
urlTemplate: "https://a-link-template-{version_id}.com",
},
},
{
requesters: ["ad_hoc"],
displayName: "periodic build link",
displayTitle: "periodic build link",
urlTemplate: "https://periodic-build-{version_id}.com",
},
],
};

const projectResult: Pick<ProjectSettingsInput, "projectRef"> = {
Expand All @@ -71,7 +78,12 @@ const projectResult: Pick<ProjectSettingsInput, "projectRef"> = {
{
requesters: ["gitter_request", "patch_request"],
displayName: "a link display name",
urlTemplate: "https:/a-link-template-{version_id}.com",
urlTemplate: "https://a-link-template-{version_id}.com",
},
{
requesters: ["ad_hoc"],
displayName: "periodic build link",
urlTemplate: "https://periodic-build-{version_id}.com",
},
],
},
Expand Down Expand Up @@ -107,13 +119,20 @@ const repoForm: PluginsFormState = {
secret: "secret",
},
},
externalLinks: {
metadataPanelLink: {
externalLinks: [
{
requesters: ["gitter_request", "patch_request"],
displayName: "a link display name",
urlTemplate: "https:/a-link-template-{version_id}.com",
displayTitle: "a link display name",
urlTemplate: "https://a-link-template-{version_id}.com",
},
},
{
requesters: ["ad_hoc"],
displayName: "periodic build link",
displayTitle: "periodic build link",
urlTemplate: "https://periodic-build-{version_id}.com",
},
],
};

const repoResult: Pick<RepoSettingsInput, "projectRef"> = {
Expand All @@ -136,7 +155,12 @@ const repoResult: Pick<RepoSettingsInput, "projectRef"> = {
{
requesters: ["gitter_request", "patch_request"],
displayName: "a link display name",
urlTemplate: "https:/a-link-template-{version_id}.com",
urlTemplate: "https://a-link-template-{version_id}.com",
},
{
requesters: ["ad_hoc"],
displayName: "periodic build link",
urlTemplate: "https://periodic-build-{version_id}.com",
},
],
},
Expand Down
20 changes: 12 additions & 8 deletions src/pages/projectSettings/tabs/PluginsTab/transformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,11 @@ export const gqlToForm = ((data) => {
secret: projectRef?.taskAnnotationSettings?.fileTicketWebhook?.secret,
},
},
externalLinks: {
metadataPanelLink: {
requesters: projectRef?.externalLinks?.[0].requesters ?? [],
displayName: projectRef?.externalLinks?.[0].displayName ?? "",
urlTemplate: projectRef?.externalLinks?.[0].urlTemplate ?? "",
},
},
externalLinks:
projectRef?.externalLinks?.map((e) => ({
...e,
displayTitle: e.displayName,
})) ?? [],
};
}) satisfies GqlToFormFunction<Tab>;

Expand All @@ -72,7 +70,13 @@ export const formToGql = ((
.map(({ displayText, field }) => ({ field, displayText }))
.filter((str) => !!str),
},
externalLinks: [externalLinks.metadataPanelLink],
externalLinks: externalLinks.map(
({ displayName, requesters, urlTemplate }) => ({
requesters,
displayName,
urlTemplate,
}),
),
};

return { projectRef };
Expand Down
13 changes: 6 additions & 7 deletions src/pages/projectSettings/tabs/PluginsTab/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@ export interface PluginsFormState {
secret: string;
};
};
externalLinks: {
metadataPanelLink: {
requesters: string[];
displayName: string;
urlTemplate: string;
};
};
externalLinks: Array<{
displayTitle: string;
requesters: string[];
displayName: string;
urlTemplate: string;
}>;
}

export type TabProps = {
Expand Down
Loading