Improvement/AppStore: Remove seeding from app-store-cli. (#8486)

* Remove seeding from cli

* Self review fixes

* Fix TS error
This commit is contained in:
Hariom Balhara 2023-05-12 10:29:15 +05:30 committed by GitHub
parent 607ef71f9f
commit ff859737ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 75 additions and 79 deletions

View File

@ -2,6 +2,7 @@ import fs from "fs";
import matter from "gray-matter";
import MarkdownIt from "markdown-it";
import type { GetStaticPaths, GetStaticPropsContext } from "next";
import Link from "next/link";
import path from "path";
import { z } from "zod";
@ -33,7 +34,26 @@ const sourceSchema = z.object({
}),
});
function SingleAppPage({ data, source }: inferSSRProps<typeof getStaticProps>) {
function SingleAppPage(props: inferSSRProps<typeof getStaticProps>) {
// If it's not production environment, it would be a better idea to inform that the App is disabled.
if (props.isAppDisabled) {
if (process.env.NODE_ENV !== "production") {
// TODO: Improve disabled App UI. This is just a placeholder.
return (
<div className="p-2">
This App seems to be disabled. If you are an admin, you can enable this app from{" "}
<Link href="/settings/admin/apps" className="cursor-pointer text-blue-500 underline">
here
</Link>
</div>
);
}
// Disabled App should give 404 any ways in production.
return null;
}
const { source, data } = props;
return (
<App
name={data.name}
@ -80,29 +100,43 @@ export const getStaticPaths: GetStaticPaths<{ slug: string }> = async () => {
export const getStaticProps = async (ctx: GetStaticPropsContext) => {
if (typeof ctx.params?.slug !== "string") return { notFound: true };
const app = await prisma.app.findUnique({
const appMeta = await getAppWithMetadata({
slug: ctx.params?.slug,
});
const appFromDb = await prisma.app.findUnique({
where: { slug: ctx.params.slug.toLowerCase() },
});
if (!app) return { notFound: true };
const isAppAvailableInFileSystem = appMeta;
const isAppDisabled = isAppAvailableInFileSystem && (!appFromDb || !appFromDb.enabled);
const singleApp = await getAppWithMetadata(app);
if (process.env.NODE_ENV !== "production" && isAppDisabled) {
return {
props: {
isAppDisabled: true as const,
data: {
...appMeta,
},
},
};
}
if (!singleApp) return { notFound: true };
if (!appFromDb || !appMeta || isAppDisabled) return { notFound: true };
const isTemplate = singleApp.isTemplate;
const appDirname = path.join(isTemplate ? "templates" : "", app.dirName);
const isTemplate = appMeta.isTemplate;
const appDirname = path.join(isTemplate ? "templates" : "", appFromDb.dirName);
const README_PATH = path.join(process.cwd(), "..", "..", `packages/app-store/${appDirname}/DESCRIPTION.md`);
const postFilePath = path.join(README_PATH);
let source = "";
try {
source = fs.readFileSync(postFilePath).toString();
source = source.replace(/{DESCRIPTION}/g, singleApp.description);
source = source.replace(/{DESCRIPTION}/g, appMeta.description);
} catch (error) {
/* If the app doesn't have a README we fallback to the package description */
console.log(`No DESCRIPTION.md provided for: ${appDirname}`);
source = singleApp.description;
source = appMeta.description;
}
const result = matter(source);
@ -111,8 +145,8 @@ export const getStaticProps = async (ctx: GetStaticPropsContext) => {
data.items = data.items.map((item) => {
if (typeof item === "string") {
return getAppAssetFullPath(item, {
dirName: singleApp.dirName,
isTemplate: singleApp.isTemplate,
dirName: appMeta.dirName,
isTemplate: appMeta.isTemplate,
});
}
return item;
@ -120,8 +154,9 @@ export const getStaticProps = async (ctx: GetStaticPropsContext) => {
}
return {
props: {
isAppDisabled: false as const,
source: { content, data },
data: singleApp,
data: appMeta,
},
};
};

View File

@ -4,9 +4,9 @@ import SelectInput from "ink-select-input";
import TextInput from "ink-text-input";
import React, { useEffect, useState } from "react";
import { AppMeta } from "@calcom/types/App";
import type { AppMeta } from "@calcom/types/App";
import { getSlugFromAppName, BaseAppFork, Seed, generateAppFiles, getAppDirPath } from "../core";
import { getSlugFromAppName, BaseAppFork, generateAppFiles, getAppDirPath } from "../core";
import { getApp } from "../utils/getApp";
import Templates from "../utils/templates";
import Label from "./Label";
@ -148,8 +148,6 @@ export const AppForm = ({
oldSlug: givenSlug,
});
await Seed.update({ slug, category: category, oldSlug: givenSlug, isTemplate });
await generateAppFiles();
// FIXME: Even after CLI showing this message, it is stuck doing work before exiting
@ -242,6 +240,10 @@ export const AppForm = ({
<Text color="green">Publisher Email: </Text>
<Text>{email}</Text>
</Box>
<Text bold>
Next Step: Enable the app from http://localhost:3000/settings/admin/apps as admin user (Email:
admin@example.com, Pass: ADMINadmin2022!)
</Text>
</Box>
</Box>
)}

View File

@ -4,7 +4,7 @@ import React, { useEffect, useState } from "react";
import { ImportantText } from "../components/ImportantText";
import { Message } from "../components/Message";
import { BaseAppFork, Seed, generateAppFiles } from "../core";
import { BaseAppFork, generateAppFiles } from "../core";
import { getApp } from "../utils/getApp";
export default function DeleteForm({ slug, action }: { slug: string; action: "delete" | "delete-template" }) {
@ -28,7 +28,6 @@ export default function DeleteForm({ slug, action }: { slug: string; action: "de
if (state === "DELETION_CONFIRMATION_SUCCESSFUL") {
(async () => {
await BaseAppFork.delete({ slug, isTemplate });
Seed.revert({ slug });
await generateAppFiles();
// successMsg({ text: `App with slug ${slug} has been deleted`, done: true });
setState("DELETION_COMPLETED");

View File

@ -1,8 +1,6 @@
import fs from "fs";
import path from "path";
import type seedAppStoreConfig from "@calcom/prisma/seed-app-store.config.json";
import { APP_STORE_PATH, TEMPLATES_PATH } from "./constants";
import execSync from "./utils/execSync";
@ -27,10 +25,6 @@ export function getAppDirPath(slug: string, isTemplate: boolean) {
return path.join(TEMPLATES_PATH, `${slug}`);
}
function absolutePath(appRelativePath: string) {
return path.join(APP_STORE_PATH, appRelativePath);
}
const updatePackageJson = ({
slug,
appDescription,
@ -138,58 +132,6 @@ export const BaseAppFork = {
},
};
export const Seed = {
seedConfigPath: absolutePath("../prisma/seed-app-store.config.json"),
update: async function ({
slug,
category,
oldSlug,
isTemplate,
}: {
slug: string;
category: string;
oldSlug: string;
isTemplate: boolean;
}) {
let configContent = "[]";
try {
if (fs.statSync(this.seedConfigPath)) {
configContent = fs.readFileSync(this.seedConfigPath).toString();
}
} catch (e) {}
let seedConfig: typeof seedAppStoreConfig = JSON.parse(configContent);
seedConfig = seedConfig.filter((app) => app.slug !== oldSlug);
if (!seedConfig.find((app) => app.slug === slug)) {
seedConfig.push({
dirName: slug,
categories: [category],
slug: slug,
type: `${slug}_${category}`,
isTemplate: isTemplate,
});
}
// Add the message as a property to first item so that it stays always at the top
seedConfig[0]["/*"] =
"This file is auto-generated and updated by `yarn app-store create/edit`. Don't edit it manually";
// Add the message as a property to first item so that it stays always at the top
seedConfig[0]["/*"] =
"This file is auto-generated and updated by `yarn app-store create/edit`. Don't edit it manually";
fs.writeFileSync(this.seedConfigPath, JSON.stringify(seedConfig, null, 2));
await execSync(`cd ${workspaceDir}/packages/prisma && yarn seed-app-store seed-templates`);
},
revert: async function ({ slug }: { slug: string }) {
let seedConfig: typeof seedAppStoreConfig = JSON.parse(fs.readFileSync(this.seedConfigPath).toString());
seedConfig = seedConfig.filter((app) => app.slug !== slug);
fs.writeFileSync(this.seedConfigPath, JSON.stringify(seedConfig, null, 2));
await execSync(`yarn workspace @calcom/prisma delete-app ${slug}`);
},
};
export const generateAppFiles = async () => {
await execSync(`yarn ts-node --transpile-only src/build.ts`);
};

View File

@ -5,8 +5,22 @@ import { userMetadata } from "@calcom/prisma/zod-utils";
import type { AppFrontendPayload as App } from "@calcom/types/App";
import type { CredentialFrontendPayload as Credential } from "@calcom/types/Credential";
export async function getAppWithMetadata(app: { dirName: string }) {
const appMetadata: App | null = appStoreMetadata[app.dirName as keyof typeof appStoreMetadata] as App;
/**
* Get App metdata either using dirName or slug
*/
export async function getAppWithMetadata(app: { dirName: string } | { slug: string }) {
let appMetadata: App | null;
if ("dirName" in app) {
appMetadata = appStoreMetadata[app.dirName as keyof typeof appStoreMetadata] as App;
} else {
const foundEntry = Object.entries(appStoreMetadata).find(([, meta]) => {
return meta.slug === app.slug;
});
if (!foundEntry) return null;
appMetadata = foundEntry[1] as App;
}
if (!appMetadata) return null;
// Let's not leak api keys to the front end
// eslint-disable-next-line @typescript-eslint/no-unused-vars

View File

@ -1,6 +1,6 @@
[
{
"/*": "This file is auto-generated and updated by `yarn app-store create/edit`. Don't edit it manually",
"/*": "This file is deprecated now. No new entry should be added to it.",
"dirName": "routing-forms",
"categories": ["other"],
"slug": "routing-forms",

View File

@ -1,3 +1,7 @@
/**
* @deprecated
* This file is deprecated. The only use of this file is to seed the database for E2E tests. Each test should take care of seeding it's own data going forward.
*/
import type { Prisma } from "@prisma/client";
import dotEnv from "dotenv";
import fs from "fs";