Skip to content

Commit

Permalink
refactor(template): utilize new GET /v1/templates/{id} endpoint for t…
Browse files Browse the repository at this point in the history
…emplate and deployment detail

refs #477
  • Loading branch information
ygrishajev committed Nov 28, 2024
1 parent 7bd3a89 commit 4555bca
Show file tree
Hide file tree
Showing 15 changed files with 159 additions and 108 deletions.
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 });
});
16 changes: 9 additions & 7 deletions apps/deploy-web/src/components/deployments/DeploymentDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { createRef, useEffect, useState } from "react";
import { createRef, FC, useEffect, useState } from "react";
import type { TemplateOutput } from "@akashnetwork/http-sdk/src/template/template-http.service";
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 +10,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,16 +30,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;
remoteDeployTemplate: TemplateOutput;
}

export const DeploymentDetail: FC<DeploymentDetailProps> = ({ dseq, remoteDeployTemplate }) => {
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 repo: string | null = isRemoteDeploy ? extractRepositoryUrl(editedManifest) : null;

Expand Down Expand Up @@ -255,4 +257,4 @@ export function DeploymentDetail({ dseq }: React.PropsWithChildren<{ dseq: strin
)}
</Layout>
);
}
};
46 changes: 22 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 { 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,37 @@ 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: React.FunctionComponent<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(() => {
if (template?.githubUrl) {
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 ? " " + 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 +60,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 +95,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
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { useManagedWallet } from "@src/hooks/useManagedWallet";
import { useUser } from "@src/hooks/useUser";
import { useWhen } from "@src/hooks/useWhen";
import { useBalances } from "@src/queries/useBalancesQuery";
import { txHttpService } from "@src/services/http/http.service";
import { txHttpService } from "@src/services/http/http-browser.service";
import networkStore from "@src/store/networkStore";
import walletStore from "@src/store/walletStore";
import { AnalyticsCategory, AnalyticsEvents } from "@src/types/analytics";
Expand Down
30 changes: 19 additions & 11 deletions apps/deploy-web/src/pages/deployments/[dseq]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { DeploymentDetail } from "@src/components/deployments/DeploymentDetail";
import type { GetServerSideProps } from "next";
import { z } from "zod";

type Props = {
dseq: string;
};
import { DeploymentDetail, DeploymentDetailProps } from "@src/components/deployments/DeploymentDetail";
import { CI_CD_TEMPLATE_ID } from "@src/config/remote-deploy.config";
import { services } from "@src/services/http/http-server.service";

const DeploymentDetailPage: React.FunctionComponent<Props> = ({ dseq }) => {
return <DeploymentDetail dseq={dseq} />;
};
export default DeploymentDetail;

export default DeploymentDetailPage;
const contextSchema = z.object({
params: z.object({
dseq: z.string()
})
});
type Params = z.infer<typeof contextSchema>["params"];

export const getServerSideProps: GetServerSideProps<DeploymentDetailProps, Params> = async context => {
const { params } = contextSchema.parse(context);
const remoteDeployTemplate = await services.template.findById(CI_CD_TEMPLATE_ID);

export async function getServerSideProps({ params }) {
return {
props: {
dseq: params?.dseq
remoteDeployTemplate,
dseq: params.dseq
}
};
}
};
4 changes: 2 additions & 2 deletions apps/deploy-web/src/pages/profile/[username]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios from "axios";

import { UserProfile } from "@src/components/user/UserProfile";
import { browserEnvConfig } from "@src/config/browser-env.config";
import { serverEnvConfig } from "@src/config/server-env.config";
import { IUserSetting } from "@src/types/user";

type Props = {
Expand Down Expand Up @@ -37,6 +37,6 @@ export async function getServerSideProps({ params }) {
}

async function fetchUser(username: string) {
const response = await axios.get(`${browserEnvConfig.NEXT_PUBLIC_BASE_API_MAINNET_URL}/user/byUsername/${username}`);
const response = await axios.get(`${serverEnvConfig.BASE_API_MAINNET_URL}/user/byUsername/${username}`);
return response.data;
}
37 changes: 15 additions & 22 deletions apps/deploy-web/src/pages/templates/[templateId]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
import axios from "axios";
import type { GetServerSideProps } from "next";
import { z } from "zod";

import { TemplateDetail } from "@src/components/templates/TemplateDetail";
import { serverEnvConfig } from "@src/config/server-env.config";
import { ApiTemplate } from "@src/types";
import { TemplateDetail, TemplateDetailProps } from "@src/components/templates/TemplateDetail";
import { services } from "@src/services/http/http-server.service";

type Props = {
templateId: string;
template: ApiTemplate;
};

const TemplateDetailPage: React.FunctionComponent<Props> = ({ templateId, template }) => {
return <TemplateDetail templateId={templateId} template={template} />;
};
export default TemplateDetail;

export default TemplateDetailPage;
const contextSchema = z.object({
params: z.object({
templateId: z.string()
})
});
type Params = z.infer<typeof contextSchema>["params"];

export async function getServerSideProps({ params }) {
const response = await axios.get(`${serverEnvConfig.BASE_API_MAINNET_URL}/templates`);
const categories = response.data.filter(x => (x.templates || []).length > 0);
categories.forEach(c => {
c.templates.forEach(t => (t.category = c.title));
});
const templates = categories.flatMap(x => x.templates);
const template = templates.find(x => x.id === params?.templateId);
export const getServerSideProps: GetServerSideProps<TemplateDetailProps, Params> = async context => {
const { params } = contextSchema.parse(context);
const template = await services.template.findById(params.templateId);

if (!template) {
return {
Expand All @@ -36,4 +29,4 @@ export async function getServerSideProps({ params }) {
template
}
};
}
};
2 changes: 1 addition & 1 deletion apps/deploy-web/src/queries/useAnonymousUserQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState } from "react";
import * as Sentry from "@sentry/nextjs";

import { useWhen } from "@src/hooks/useWhen";
import { userHttpService } from "@src/services/http/http.service";
import { userHttpService } from "@src/services/http/http-browser.service";

export interface UserOutput {
id: string;
Expand Down
2 changes: 1 addition & 1 deletion apps/deploy-web/src/queries/useStripePricesQuery.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useQuery } from "react-query";

import { stripeService } from "@src/services/http/http.service";
import { stripeService } from "@src/services/http/http-browser.service";

export function useStripePricesQuery({ enabled = true } = {}) {
return useQuery(["StripePrices"], () => stripeService.findPrices(), {
Expand Down
31 changes: 31 additions & 0 deletions apps/deploy-web/src/services/http-factory/http-factory.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { TxHttpService, UserHttpService } from "@akashnetwork/http-sdk";
import { StripeService } from "@akashnetwork/http-sdk/src/stripe/stripe.service";
import { TemplateHttpService } from "@akashnetwork/http-sdk/src/template/template-http.service";
import { event } from "nextjs-google-analytics";

import type { BrowserEnvConfig, ServerEnvConfig } from "@src/config/env-config.schema";
import { authService } from "@src/services/auth/auth.service";
import { AnalyticsCategory, AnalyticsEvents } from "@src/types/analytics";
import { customRegistry } from "@src/utils/customRegistry";

export const createServices = (config: Pick<ServerEnvConfig, "BASE_API_MAINNET_URL"> | Pick<BrowserEnvConfig, "NEXT_PUBLIC_BASE_API_MAINNET_URL">) => {
const apiConfig = { baseURL: "BASE_API_MAINNET_URL" in config ? config.BASE_API_MAINNET_URL : config.NEXT_PUBLIC_BASE_API_MAINNET_URL };

const user = new UserHttpService(apiConfig);
const stripe = new StripeService(apiConfig);
const tx = new TxHttpService(customRegistry, apiConfig);
const template = new TemplateHttpService(apiConfig);

user.interceptors.request.use(authService.withAnonymousUserHeader);
stripe.interceptors.request.use(authService.withAnonymousUserHeader);
tx.interceptors.request.use(authService.withAnonymousUserHeader);

user.interceptors.response.use(response => {
if (response.config.url?.startsWith("/v1/anonymous-users") && response.config.method === "post" && response.status === 200) {
event(AnalyticsEvents.ANONYMOUS_USER_CREATED, { category: AnalyticsCategory.USER, label: "Anonymous User Created" });
}
return response;
});

return { user, stripe, tx, template };
};
Loading

0 comments on commit 4555bca

Please sign in to comment.