Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(template): utilize new GET /v1/templates/{id} endpoint for template and deployment detail #506

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion .commitlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"repo",
"styling",
"observability",
"analytics"
"analytics",
"template"
]
]
}
Expand Down
28 changes: 15 additions & 13 deletions apps/api/src/routes/v1/templates/byId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,20 @@ const route = createRoute({
content: {
"application/json": {
schema: z.object({
id: z.string(),
name: z.string(),
path: z.string(),
logoUrl: z.string().nullable(),
summary: z.string(),
readme: z.string().nullable(),
deploy: z.string(),
persistentStorageEnabled: z.boolean(),
guide: z.string().nullable(),
githubUrl: z.string(),
config: z.object({
ssh: z.boolean().optional()
data: z.object({
id: z.string(),
name: z.string(),
path: z.string(),
logoUrl: z.string().nullable(),
summary: z.string(),
readme: z.string().nullable(),
deploy: z.string(),
persistentStorageEnabled: z.boolean(),
guide: z.string().nullable(),
githubUrl: z.string(),
config: z.object({
ssh: z.boolean().optional()
})
})
})
}
Expand All @@ -51,5 +53,5 @@ export default new OpenAPIHono().openapi(route, async c => {
return c.text("Template not found", 404);
}

return c.json(template);
return c.json({ data: template });
});
4 changes: 3 additions & 1 deletion apps/deploy-web/env/.env
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ NEXT_PUBLIC_REDIRECT_URI='https://console.akash.network/new-deployment'
NEXT_PUBLIC_GITHUB_APP_INSTALLATION_URL='https://github.com/apps/akash-console-build-and-deploy-app/installations/new'
NEXT_PUBLIC_BITBUCKET_CLIENT_ID=tdH2xfRkTcdqVP6cwW
NEXT_PUBLIC_GITHUB_CLIENT_ID=Iv23lidSwihrsSL7aGew
NEXT_PUBLIC_GITLAB_CLIENT_ID=beb5370aad2fdb6147edb44248d20d30c3e189ddfb40c26f651c77bbe949d5a8
NEXT_PUBLIC_GITLAB_CLIENT_ID=beb5370aad2fdb6147edb44248d20d30c3e189ddfb40c26f651c77bbe949d5a8

NEXT_PUBLIC_CI_CD_IMAGE_NAME=hoomanhq/automation
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { createRef, useEffect, useState } from "react";
import { createRef, FC, useEffect, useState } from "react";
import { Alert, Button, buttonVariants, Spinner, Tabs, TabsList, TabsTrigger } from "@akashnetwork/ui/components";
import { cn } from "@akashnetwork/ui/utils";
import { ArrowLeft } from "iconoir-react";
Expand All @@ -9,10 +9,8 @@ import { useRouter, useSearchParams } from "next/navigation";
import { NextSeo } from "next-seo";
import { event } from "nextjs-google-analytics";

import { CI_CD_TEMPLATE_ID } from "@src/config/remote-deploy.config";
import { useCertificate } from "@src/context/CertificateProvider";
import { useSettings } from "@src/context/SettingsProvider";
import { useTemplates } from "@src/context/TemplatesProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useDeploymentDetail } from "@src/queries/useDeploymentQuery";
import { useDeploymentLeaseList } from "@src/queries/useLeaseQuery";
Expand All @@ -31,17 +29,19 @@ import { DeploymentSubHeader } from "./DeploymentSubHeader";
import { LeaseRow } from "./LeaseRow";
import { ManifestUpdate } from "./ManifestUpdate";

export function DeploymentDetail({ dseq }: React.PropsWithChildren<{ dseq: string }>) {
export interface DeploymentDetailProps {
dseq: string;
}

export const DeploymentDetail: FC<DeploymentDetailProps> = ({ dseq }) => {
const router = useRouter();
const [activeTab, setActiveTab] = useState("LEASES");
const [editedManifest, setEditedManifest] = useState<string | null>(null);
const { address, isWalletLoaded } = useWallet();
const { isSettingsInit } = useSettings();
const [leaseRefs, setLeaseRefs] = useState<Array<any>>([]);
const [deploymentManifest, setDeploymentManifest] = useState<string | null>(null);
const { getTemplateById } = useTemplates();
const remoteDeployTemplate = getTemplateById(CI_CD_TEMPLATE_ID);
const isRemoteDeploy: boolean = !!editedManifest && !!isImageInYaml(editedManifest, remoteDeployTemplate?.deploy);
const isRemoteDeploy: boolean = !!editedManifest && !!isImageInYaml(editedManifest);
const repo: string | null = isRemoteDeploy ? extractRepositoryUrl(editedManifest) : null;

const {
Expand Down Expand Up @@ -255,4 +255,4 @@ export function DeploymentDetail({ dseq }: React.PropsWithChildren<{ dseq: strin
)}
</Layout>
);
}
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";
import { FC, useEffect, useState } from "react";
import { FC, useCallback, useEffect, useState } from "react";
import { TemplateOutput } from "@akashnetwork/http-sdk/src/template/template-http.service";
import { useAtomValue } from "jotai";
import { useRouter, useSearchParams } from "next/navigation";

Expand All @@ -20,15 +21,19 @@ import { ManifestEdit } from "./ManifestEdit";
import { CustomizedSteppers } from "./Stepper";
import { TemplateList } from "./TemplateList";

export const NewDeploymentContainer: FC = () => {
export interface NewDeploymentContainerProps {
template?: TemplateOutput;
templateId?: string;
}

export const NewDeploymentContainer: FC<NewDeploymentContainerProps> = ({ template: requestedTemplate, templateId }) => {
const [isGitProviderTemplate, setIsGitProviderTemplate] = useState<boolean>(false);
const { isLoading: isLoadingTemplates, templates } = useTemplates();
const [activeStep, setActiveStep] = useState<number | null>(null);
const [selectedTemplate, setSelectedTemplate] = useState<TemplateCreation | null>(null);
const [editedManifest, setEditedManifest] = useState<string | null>(null);
const deploySdl = useAtomValue(sdlStore.deploySdl);
const { getDeploymentData } = useLocalNotes();
const { getTemplateById } = useTemplates();
const router = useRouter();
const searchParams = useSearchParams();
const dseq = searchParams?.get("dseq");
Expand Down Expand Up @@ -78,8 +83,7 @@ export const NewDeploymentContainer: FC = () => {
toggleCmp("ssh");
}

const cicdTemplate = getTemplateById(CI_CD_TEMPLATE_ID);
const isRemoteYamlImage = isImageInYaml(template?.content as string, cicdTemplate?.deploy);
const isRemoteYamlImage = isImageInYaml(template?.content as string);
const queryStep = searchParams?.get("step");
if (queryStep !== RouteStep.editDeployment) {
if (isRemoteYamlImage) {
Expand Down Expand Up @@ -113,34 +117,25 @@ export const NewDeploymentContainer: FC = () => {
return template;
};

const getGalleryTemplate = (): Partial<{
code: string;
name: string;
content: string;
valuesToChange: any[];
config: { ssh?: boolean };
}> | null => {
const queryTemplateId = searchParams?.get("templateId");
if (queryTemplateId) {
const templateById = getTemplateById(queryTemplateId as string);
if (templateById) {
return {
const getGalleryTemplate = useCallback(():
| Partial<{
code: string;
name: string;
content: string;
valuesToChange: any[];
config: { ssh?: boolean };
}>
| undefined => {
return requestedTemplate
? {
code: "empty",
name: templateById.name,
content: templateById.deploy,
valuesToChange: templateById.valuesToChange || [],
config: templateById.config
};
}

const hardCodedTemplate = hardcodedTemplates.find(t => t.code === queryTemplateId);
if (hardCodedTemplate) {
return hardCodedTemplate;
}
}

return null;
};
name: requestedTemplate.name,
content: requestedTemplate.deploy,
valuesToChange: [],
config: requestedTemplate.config
}
: hardcodedTemplates.find(t => t.code === templateId);
}, [requestedTemplate, templateId]);

function getStepIndexByParam(step: (typeof RouteStep)[keyof typeof RouteStep] | null) {
switch (step) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { useSnackbar } from "notistack";

import { EnvFormModal } from "@src/components/sdl/EnvFormModal";
import { EnvVarList } from "@src/components/sdl/EnvVarList";
import { CI_CD_TEMPLATE_ID, CURRENT_SERVICE, protectedEnvironmentVariables } from "@src/config/remote-deploy.config";
import { browserEnvConfig } from "@src/config/browser-env.config";
import { CURRENT_SERVICE, protectedEnvironmentVariables } from "@src/config/remote-deploy.config";
import { SdlBuilderProvider } from "@src/context/SdlBuilderProvider";
import { useTemplates } from "@src/context/TemplatesProvider";
import { EnvVarUpdater } from "@src/services/remote-deploy/remote-deployment-controller.service";
import { tokens } from "@src/store/remoteDeployStore";
import { SdlBuilderFormValuesType, ServiceType } from "@src/types";
Expand All @@ -26,8 +26,6 @@ const RemoteDeployUpdate = ({ sdlString, onManifestChange }: { sdlString: string
const [isEditingEnv, setIsEditingEnv] = useState<number | boolean | null>(false);
const { control, watch, setValue } = useForm<SdlBuilderFormValuesType>({ defaultValues: { services: [defaultService] } });
const { fields: services } = useFieldArray({ control, name: "services", keyName: "id" });
const { getTemplateById } = useTemplates();
const remoteDeployTemplate = getTemplateById(CI_CD_TEMPLATE_ID);
const envVarUpdater = useMemo(() => new EnvVarUpdater(services), [services]);

useEffect(() => {
Expand All @@ -51,10 +49,7 @@ const RemoteDeployUpdate = ({ sdlString, onManifestChange }: { sdlString: string

const createAndValidateSdl = (yamlStr: string) => {
try {
if (!yamlStr) return [];
const services = importSimpleSdl(yamlStr);

return services;
return yamlStr ? importSimpleSdl(yamlStr) : [];
} catch (err) {
if (err.name === "YAMLException" || err.name === "CustomValidationError") {
enqueueSnackbar(<Snackbar title={err.message} />, { variant: "error" });
Expand All @@ -65,7 +60,7 @@ const RemoteDeployUpdate = ({ sdlString, onManifestChange }: { sdlString: string
}
}
};
return remoteDeployTemplate?.deploy?.includes(services?.[0]?.image) && services?.[0]?.env && services?.[0]?.env?.length > 0 ? (
return services?.[0]?.image.startsWith(browserEnvConfig.NEXT_PUBLIC_CI_CD_IMAGE_NAME) && services?.[0]?.env && services?.[0]?.env?.length > 0 ? (
<div className="flex flex-col gap-6 rounded border bg-card px-4 py-6 md:px-6">
<div className="flex flex-col gap-3 rounded border bg-card px-6 py-6 text-card-foreground">
<div className="flex items-center justify-between gap-5">
Expand Down
44 changes: 20 additions & 24 deletions apps/deploy-web/src/components/templates/TemplateDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";
import { useState } from "react";

import { FC, useCallback, useState } from "react";
import { Button, buttonVariants, Tabs, TabsList, TabsTrigger } from "@akashnetwork/ui/components";
import { cn } from "@akashnetwork/ui/utils";
import GitHubIcon from "@mui/icons-material/GitHub";
Expand All @@ -8,10 +9,8 @@ import Link from "next/link";
import { useRouter } from "next/navigation";

import { DynamicMonacoEditor } from "@src/components/shared/DynamicMonacoEditor";
import { LinearLoadingSkeleton } from "@src/components/shared/LinearLoadingSkeleton";
import Markdown from "@src/components/shared/Markdown";
import ViewPanel from "@src/components/shared/ViewPanel";
import { useTemplates } from "@src/context/TemplatesProvider";
import { usePreviousRoute } from "@src/hooks/usePreviousRoute";
import { getShortText } from "@src/hooks/useShortText";
import { ApiTemplate } from "@src/types";
Expand All @@ -20,37 +19,35 @@ import { domainName, UrlService } from "@src/utils/urlUtils";
import Layout from "../layout/Layout";
import { CustomNextSeo } from "../shared/CustomNextSeo";

type Props = {
export interface TemplateDetailProps {
templateId: string;
template: ApiTemplate;
};
}

export const TemplateDetail: React.FunctionComponent<Props> = ({ templateId, template }) => {
export const TemplateDetail: FC<TemplateDetailProps> = ({ templateId, template }) => {
const [activeTab, setActiveTab] = useState("README");
const { getTemplateById, isLoading } = useTemplates();
const router = useRouter();
const _template = template || getTemplateById(templateId);
const previousRoute = usePreviousRoute();

function handleBackClick() {
const goBack = useCallback(() => {
if (previousRoute) {
router.back();
} else {
router.push(UrlService.templates());
}
}
}, [previousRoute, router]);

function handleOpenGithub() {
window.open(_template.githubUrl, "_blank");
}
const openGithub = useCallback(() => {
window.open(template.githubUrl, "_blank");
}, [template]);

return (
<Layout disableContainer>
<div className="[&>img]:max-w-full">
<CustomNextSeo
title={`Template detail${_template ? " " + _template?.name : ""}`}
title={`Template detail ${template.name}`}
url={`${domainName}${UrlService.templateDetails(templateId)}`}
description={getShortText(_template.summary || "", 140)}
description={getShortText(template.summary || "", 140)}
/>

<Tabs value={activeTab} onValueChange={setActiveTab}>
Expand All @@ -61,32 +58,31 @@ export const TemplateDetail: React.FunctionComponent<Props> = ({ templateId, tem
<TabsTrigger value="SDL" className={cn({ ["font-bold"]: activeTab === "SDL" })}>
View SDL
</TabsTrigger>
{_template?.guide && (
{template.guide && (
<TabsTrigger value="GUIDE" className={cn({ ["font-bold"]: activeTab === "GUIDE" })}>
Guide
</TabsTrigger>
)}
</TabsList>
<LinearLoadingSkeleton isLoading={isLoading} />

<div className="container flex h-full px-4 py-2 sm:pt-8">
<div className="flex items-center truncate">
<Button aria-label="back" onClick={handleBackClick} size="icon" variant="ghost">
<Button aria-label="back" onClick={goBack} size="icon" variant="ghost">
<NavArrowLeft />
</Button>
<div className="text-truncate">
<h3 className="ml-4 text-xl font-bold sm:text-2xl md:text-3xl">{_template?.name}</h3>
<h3 className="ml-4 text-xl font-bold sm:text-2xl md:text-3xl">{template.name}</h3>
</div>

<div className="ml-4">
<Button aria-label="View on github" title="View on Github" onClick={handleOpenGithub} size="icon" variant="ghost">
<Button aria-label="View on github" title="View on Github" onClick={openGithub} size="icon" variant="ghost">
<GitHubIcon fontSize="medium" />
</Button>
</div>

<Link
className={cn(buttonVariants({ variant: "default" }), "ml-4 md:ml-8")}
href={UrlService.newDeployment({ step: RouteStep.editDeployment, templateId: _template?.id })}
href={UrlService.newDeployment({ step: RouteStep.editDeployment, templateId: template.id })}
>
Deploy&nbsp;
<Rocket className="rotate-45" />
Expand All @@ -97,21 +93,21 @@ export const TemplateDetail: React.FunctionComponent<Props> = ({ templateId, tem
{activeTab === "README" && (
<ViewPanel stickToBottom className="overflow-auto pb-12">
<div className="container pb-8 pt-4 sm:pt-8">
<Markdown hasHtml={_template.id?.startsWith("akash-network-awesome-akash")}>{_template?.readme}</Markdown>
<Markdown hasHtml={template.id?.startsWith("akash-network-awesome-akash")}>{template.readme}</Markdown>
</div>
</ViewPanel>
)}
{activeTab === "SDL" && (
<ViewPanel stickToBottom className="overflow-hidden">
<div className="container h-full pb-8 pt-4 sm:pt-8">
<DynamicMonacoEditor height="100%" language="yaml" value={_template?.deploy || ""} options={{ readOnly: true }} />
<DynamicMonacoEditor height="100%" language="yaml" value={template.deploy || ""} options={{ readOnly: true }} />
</div>
</ViewPanel>
)}
{activeTab === "GUIDE" && (
<ViewPanel stickToBottom className="overflow-auto p-4 pb-12">
<div className="container h-full pb-8 pt-4 sm:pt-8">
<Markdown>{_template?.guide}</Markdown>
<Markdown>{template.guide}</Markdown>
</div>
</ViewPanel>
)}
Expand Down
3 changes: 2 additions & 1 deletion apps/deploy-web/src/config/browser-env.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ export const browserEnvConfig = validateStaticEnvVars({
NEXT_PUBLIC_BITBUCKET_CLIENT_ID: process.env.NEXT_PUBLIC_BITBUCKET_CLIENT_ID,
NEXT_PUBLIC_GITLAB_CLIENT_ID: process.env.NEXT_PUBLIC_GITLAB_CLIENT_ID,
NEXT_PUBLIC_GITHUB_CLIENT_ID: process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID,
NEXT_PUBLIC_GA_MEASUREMENT_ID: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID
NEXT_PUBLIC_GA_MEASUREMENT_ID: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID,
NEXT_PUBLIC_CI_CD_IMAGE_NAME: process.env.NEXT_PUBLIC_CI_CD_IMAGE_NAME
});
Loading