Better handle errors when trying to add a template to github account
This commit is contained in:
@@ -12,6 +12,7 @@ import { Container } from "~/components/layout/Container";
|
||||
import { Panel } from "~/components/layout/Panel";
|
||||
import { PanelWarning } from "~/components/layout/PanelWarning";
|
||||
import { PrimaryButton } from "~/components/primitives/Buttons";
|
||||
import { FormError } from "~/components/primitives/FormError";
|
||||
import { Input } from "~/components/primitives/Input";
|
||||
import { InputGroup } from "~/components/primitives/InputGroup";
|
||||
import { Label } from "~/components/primitives/Label";
|
||||
@@ -52,8 +53,20 @@ export async function action({ params, request }: ActionArgs) {
|
||||
|
||||
const service = new AddTemplateService();
|
||||
|
||||
const validation = service.validate(payload);
|
||||
|
||||
if (!validation.success) {
|
||||
return typedjson(
|
||||
{
|
||||
type: "validationError" as const,
|
||||
errors: validation.error.issues,
|
||||
},
|
||||
{ status: 422 }
|
||||
);
|
||||
}
|
||||
|
||||
const result = await service.call({
|
||||
payload,
|
||||
data: validation.data,
|
||||
organizationSlug,
|
||||
userId,
|
||||
});
|
||||
@@ -61,8 +74,8 @@ export async function action({ params, request }: ActionArgs) {
|
||||
if (result.type === "error") {
|
||||
return typedjson(
|
||||
{
|
||||
type: "error" as const,
|
||||
errors: result.message,
|
||||
type: "serviceError" as const,
|
||||
message: result.message,
|
||||
},
|
||||
{ status: 422 }
|
||||
);
|
||||
@@ -89,15 +102,23 @@ export default function AddTemplatePage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Form method="post" className="max-w-4xl" reloadDocument>
|
||||
<Form method="post" className="max-w-4xl">
|
||||
<Title>You're almost done</Title>
|
||||
|
||||
{actionData?.type === "error" && (
|
||||
{actionData?.type === "serviceError" ? (
|
||||
<PanelWarning
|
||||
message={actionData.errors}
|
||||
message={actionData.message}
|
||||
className="mb-4"
|
||||
></PanelWarning>
|
||||
) : actionData?.type === "validationError" ? (
|
||||
<PanelWarning
|
||||
message="There was a problem with your submission."
|
||||
className="mb-4"
|
||||
></PanelWarning>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{template ? (
|
||||
<SubTitle>
|
||||
Configure GitHub for your{" "}
|
||||
@@ -119,6 +140,13 @@ export default function AddTemplatePage() {
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
{actionData?.type === "validationError" && (
|
||||
<FormError
|
||||
errors={actionData.errors}
|
||||
path={["appAuthorizationId"]}
|
||||
/>
|
||||
)}
|
||||
</InputGroup>
|
||||
|
||||
{template ? (
|
||||
@@ -134,6 +162,10 @@ export default function AddTemplatePage() {
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
{actionData?.type === "validationError" && (
|
||||
<FormError errors={actionData.errors} path={["templateId"]} />
|
||||
)}
|
||||
</InputGroup>
|
||||
)}
|
||||
</div>
|
||||
@@ -148,6 +180,10 @@ export default function AddTemplatePage() {
|
||||
spellCheck={false}
|
||||
className=""
|
||||
/>
|
||||
|
||||
{actionData?.type === "validationError" && (
|
||||
<FormError errors={actionData.errors} path={["name"]} />
|
||||
)}
|
||||
</InputGroup>
|
||||
<div>
|
||||
<p className="mb-1 text-sm text-slate-500">
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Options } from "@octokit/oauth-app/dist-types/types";
|
||||
import type { Endpoints } from "@octokit/types";
|
||||
import { z } from "zod";
|
||||
import { taskQueue } from "../messageBroker.server";
|
||||
import { RequestError } from "@octokit/request-error";
|
||||
|
||||
export const octokit = env.GITHUB_APP_PRIVATE_KEY
|
||||
? new Octokit({
|
||||
@@ -202,19 +203,32 @@ type CreateRepositoryFromTemplateEndpoint =
|
||||
export async function createRepositoryFromTemplate(
|
||||
parameters: CreateRepositoryFromTemplateEndpoint["parameters"],
|
||||
{ installationId }: { installationId?: number }
|
||||
) {
|
||||
): Promise<
|
||||
| {
|
||||
status: "success";
|
||||
data: CreateRepositoryFromTemplateEndpoint["response"]["data"];
|
||||
}
|
||||
| { status: "error"; message: string }
|
||||
> {
|
||||
if (typeof octokit === "undefined") {
|
||||
return;
|
||||
return { status: "error", message: "Octokit not initialized" };
|
||||
}
|
||||
|
||||
const kit = installationId ? await getOctokit(installationId) : octokit;
|
||||
try {
|
||||
const response = await kit.request(
|
||||
"POST /repos/{template_owner}/{template_repo}/generate",
|
||||
parameters
|
||||
);
|
||||
|
||||
const response = await kit.request(
|
||||
"POST /repos/{template_owner}/{template_repo}/generate",
|
||||
parameters
|
||||
);
|
||||
|
||||
return response.data;
|
||||
return { status: "success", data: response.data };
|
||||
} catch (error) {
|
||||
if (error instanceof RequestError) {
|
||||
return { status: "error", message: error.message };
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getOctokitRest(installationId: number) {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { z } from "zod";
|
||||
import { generateErrorMessage } from "zod-error";
|
||||
import type { PrismaClient } from "~/db.server";
|
||||
import { prisma } from "~/db.server";
|
||||
import {
|
||||
AccountSchema,
|
||||
createRepositoryFromTemplate,
|
||||
getOctokitRest,
|
||||
} from "../github/githubApp.server";
|
||||
|
||||
const FormSchema = z.object({
|
||||
@@ -22,26 +20,19 @@ export class AddTemplateService {
|
||||
this.#prismaClient = prismaClient;
|
||||
}
|
||||
|
||||
public validate(payload: unknown) {
|
||||
return FormSchema.safeParse(payload);
|
||||
}
|
||||
|
||||
public async call({
|
||||
userId,
|
||||
organizationSlug,
|
||||
payload,
|
||||
data,
|
||||
}: {
|
||||
userId: string;
|
||||
organizationSlug: string;
|
||||
payload: unknown;
|
||||
data: z.infer<typeof FormSchema>;
|
||||
}) {
|
||||
const parsedPayload = FormSchema.safeParse(payload);
|
||||
|
||||
if (!parsedPayload.success) {
|
||||
return {
|
||||
type: "error" as const,
|
||||
message: generateErrorMessage(parsedPayload.error.issues),
|
||||
};
|
||||
}
|
||||
|
||||
const data = parsedPayload.data;
|
||||
|
||||
const appAuthorization =
|
||||
await this.#prismaClient.gitHubAppAuthorization.findUnique({
|
||||
where: {
|
||||
@@ -85,7 +76,7 @@ export class AddTemplateService {
|
||||
.split("/")
|
||||
.slice(1);
|
||||
|
||||
const githubRepository = await createRepositoryFromTemplate(
|
||||
const createdGithubRepo = await createRepositoryFromTemplate(
|
||||
{
|
||||
template_owner: template_owner,
|
||||
template_repo: template_repo,
|
||||
@@ -96,13 +87,15 @@ export class AddTemplateService {
|
||||
{ installationId: appAuthorization.installationId }
|
||||
);
|
||||
|
||||
if (!githubRepository) {
|
||||
if (createdGithubRepo.status === "error") {
|
||||
return {
|
||||
type: "error" as const,
|
||||
message: "Failed to create repository",
|
||||
message: createdGithubRepo.message,
|
||||
};
|
||||
}
|
||||
|
||||
const githubRepository = createdGithubRepo.data;
|
||||
|
||||
const organizationTemplate =
|
||||
await this.#prismaClient.organizationTemplate.create({
|
||||
data: {
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"@octokit/auth-unauthenticated": "^3.0.4",
|
||||
"@octokit/core": "^4.2.0",
|
||||
"@octokit/oauth-app": "^4.2.0",
|
||||
"@octokit/request-error": "^3.0.3",
|
||||
"@octokit/rest": "^19.0.7",
|
||||
"@octokit/webhooks": "^10.4.0",
|
||||
"@prisma/client": "^4.3.0",
|
||||
|
||||
Generated
+19
-24
@@ -55,6 +55,7 @@ importers:
|
||||
'@octokit/auth-unauthenticated': ^3.0.4
|
||||
'@octokit/core': ^4.2.0
|
||||
'@octokit/oauth-app': ^4.2.0
|
||||
'@octokit/request-error': ^3.0.3
|
||||
'@octokit/rest': ^19.0.7
|
||||
'@octokit/types': ^9.0.0
|
||||
'@octokit/webhooks': ^10.4.0
|
||||
@@ -197,7 +198,7 @@ importers:
|
||||
'@aws-sdk/client-s3': 3.245.0
|
||||
'@aws-sdk/s3-request-presigner': 3.245.0
|
||||
'@cfworker/json-schema': 1.12.5
|
||||
'@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde
|
||||
'@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu
|
||||
'@codemirror/commands': 6.1.3
|
||||
'@codemirror/lang-javascript': 6.1.2
|
||||
'@codemirror/lang-json': 6.0.1
|
||||
@@ -217,6 +218,7 @@ importers:
|
||||
'@octokit/auth-unauthenticated': 3.0.4
|
||||
'@octokit/core': 4.2.0
|
||||
'@octokit/oauth-app': 4.2.0
|
||||
'@octokit/request-error': 3.0.3
|
||||
'@octokit/rest': 19.0.7
|
||||
'@octokit/webhooks': 10.5.1
|
||||
'@prisma/client': 4.8.1_prisma@4.8.1
|
||||
@@ -233,7 +235,7 @@ importers:
|
||||
'@trigger.dev/slack': link:../../integrations/slack
|
||||
'@trigger.dev/whatsapp': link:../../integrations/whatsapp
|
||||
'@typeform/embed-react': 2.14.1_react@18.2.0
|
||||
'@uiw/react-codemirror': 4.19.5_aguurb4bmecpxzejz52amioxne
|
||||
'@uiw/react-codemirror': 4.19.5_k4ec5g7vuuzzonc3d6xbjnmmle
|
||||
bcryptjs: 2.4.3
|
||||
classnames: 2.3.2
|
||||
clsx: 1.2.1
|
||||
@@ -3605,13 +3607,12 @@ packages:
|
||||
prettier: 2.8.2
|
||||
dev: false
|
||||
|
||||
/@codemirror/autocomplete/6.4.0_eo6pz6bvsllvatnnwfprpuflde:
|
||||
/@codemirror/autocomplete/6.4.0_czcfkg2f66rxeiodoti7r2gulu:
|
||||
resolution: {integrity: sha512-HLF2PnZAm1s4kGs30EiqKMgD7XsYaQ0XJnMR0rofEWQ5t5D60SfqpDIkIh1ze5tiEbyUWm8+VJ6W1/erVvBMIA==}
|
||||
peerDependencies:
|
||||
'@codemirror/language': ^6.0.0
|
||||
'@codemirror/state': ^6.0.0
|
||||
'@codemirror/view': ^6.0.0
|
||||
'@lezer/common': ^1.0.0
|
||||
dependencies:
|
||||
'@codemirror/language': 6.3.2
|
||||
'@codemirror/state': 6.2.0
|
||||
@@ -3631,7 +3632,7 @@ packages:
|
||||
/@codemirror/lang-javascript/6.1.2:
|
||||
resolution: {integrity: sha512-OcwLfZXdQ1OHrLiIcKCn7MqZ7nx205CMKlhe+vL88pe2ymhT9+2P+QhwkYGxMICj8TDHyp8HFKVwpiisUT7iEQ==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde
|
||||
'@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu
|
||||
'@codemirror/language': 6.3.2
|
||||
'@codemirror/lint': 6.1.0
|
||||
'@codemirror/state': 6.2.0
|
||||
@@ -5055,7 +5056,7 @@ packages:
|
||||
eslint: 8.31.0
|
||||
eslint-import-resolver-node: 0.3.6
|
||||
eslint-import-resolver-typescript: 3.5.3_hnftvkj7qg3s6bbigj4pr6djxy
|
||||
eslint-plugin-import: 2.27.4_qdjeohovcytra7xto5vgmxssaq
|
||||
eslint-plugin-import: 2.27.4_2ac3tknkazjoq5fxmuugu665ny
|
||||
eslint-plugin-jest: 26.9.0_ohsifnwenhmxgcp7mend4dnv74
|
||||
eslint-plugin-jest-dom: 4.0.3_eslint@8.31.0
|
||||
eslint-plugin-jsx-a11y: 6.7.1_eslint@8.31.0
|
||||
@@ -6223,18 +6224,17 @@ packages:
|
||||
eslint-visitor-keys: 3.3.0
|
||||
dev: true
|
||||
|
||||
/@uiw/codemirror-extensions-basic-setup/4.19.5_tbeldtdcrf45b35pezgkzq2u4e:
|
||||
/@uiw/codemirror-extensions-basic-setup/4.19.5_wd2tsis3in55bkaiwnc2c46tom:
|
||||
resolution: {integrity: sha512-1zt7ZPJ01xKkSW/KDy0FZNga0bngN1fC594wCVG7FBi60ehfcAucpooQ+JSPScKXopxcb+ugPKZvVLzr9/OfzA==}
|
||||
peerDependencies:
|
||||
'@codemirror/autocomplete': '>=6.0.0'
|
||||
'@codemirror/commands': '>=6.0.0'
|
||||
'@codemirror/language': '>=6.0.0'
|
||||
'@codemirror/lint': '>=6.0.0'
|
||||
'@codemirror/search': '>=6.0.0'
|
||||
'@codemirror/state': '>=6.0.0'
|
||||
'@codemirror/view': '>=6.0.0'
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde
|
||||
'@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu
|
||||
'@codemirror/commands': 6.1.3
|
||||
'@codemirror/language': 6.3.2
|
||||
'@codemirror/lint': 6.1.0
|
||||
@@ -6243,14 +6243,11 @@ packages:
|
||||
'@codemirror/view': 6.7.2
|
||||
dev: false
|
||||
|
||||
/@uiw/react-codemirror/4.19.5_aguurb4bmecpxzejz52amioxne:
|
||||
/@uiw/react-codemirror/4.19.5_k4ec5g7vuuzzonc3d6xbjnmmle:
|
||||
resolution: {integrity: sha512-ZCHh8d7beXbF8/t7F1+yHht6A9Y6CdKeOkZq4A09lxJEnyTQrj1FMf2zvfaqc7K23KNjkTCtSlbqKKbVDgrWaw==}
|
||||
peerDependencies:
|
||||
'@babel/runtime': '>=7.11.0'
|
||||
'@codemirror/state': '>=6.0.0'
|
||||
'@codemirror/theme-one-dark': '>=6.0.0'
|
||||
'@codemirror/view': '>=6.0.0'
|
||||
codemirror: '>=6.0.0'
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
dependencies:
|
||||
@@ -6259,14 +6256,13 @@ packages:
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/theme-one-dark': 6.1.0
|
||||
'@codemirror/view': 6.7.2
|
||||
'@uiw/codemirror-extensions-basic-setup': 4.19.5_tbeldtdcrf45b35pezgkzq2u4e
|
||||
codemirror: 6.0.1_@lezer+common@1.0.2
|
||||
'@uiw/codemirror-extensions-basic-setup': 4.19.5_wd2tsis3in55bkaiwnc2c46tom
|
||||
codemirror: 6.0.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
transitivePeerDependencies:
|
||||
- '@codemirror/autocomplete'
|
||||
- '@codemirror/language'
|
||||
- '@codemirror/lint'
|
||||
- '@codemirror/search'
|
||||
dev: false
|
||||
|
||||
@@ -7495,18 +7491,16 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/codemirror/6.0.1_@lezer+common@1.0.2:
|
||||
/codemirror/6.0.1:
|
||||
resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde
|
||||
'@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu
|
||||
'@codemirror/commands': 6.1.3
|
||||
'@codemirror/language': 6.3.2
|
||||
'@codemirror/lint': 6.1.0
|
||||
'@codemirror/search': 6.2.3
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.2
|
||||
transitivePeerDependencies:
|
||||
- '@lezer/common'
|
||||
dev: false
|
||||
|
||||
/collection-visit/1.0.0:
|
||||
@@ -8742,7 +8736,7 @@ packages:
|
||||
debug: 4.3.4
|
||||
enhanced-resolve: 5.12.0
|
||||
eslint: 8.31.0
|
||||
eslint-plugin-import: 2.27.4_qdjeohovcytra7xto5vgmxssaq
|
||||
eslint-plugin-import: 2.27.4_2ac3tknkazjoq5fxmuugu665ny
|
||||
get-tsconfig: 4.3.0
|
||||
globby: 13.1.3
|
||||
is-core-module: 2.11.0
|
||||
@@ -8752,7 +8746,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-module-utils/2.7.4_sqt5xxn4ciiurbqrzlaarm6ama:
|
||||
/eslint-module-utils/2.7.4_v73lhamtbyinynmwa5fn7kpmfq:
|
||||
resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
@@ -8777,6 +8771,7 @@ packages:
|
||||
debug: 3.2.7
|
||||
eslint: 8.31.0
|
||||
eslint-import-resolver-node: 0.3.7
|
||||
eslint-import-resolver-typescript: 3.5.3_hnftvkj7qg3s6bbigj4pr6djxy
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -8801,7 +8796,7 @@ packages:
|
||||
regexpp: 3.2.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-import/2.27.4_qdjeohovcytra7xto5vgmxssaq:
|
||||
/eslint-plugin-import/2.27.4_2ac3tknkazjoq5fxmuugu665ny:
|
||||
resolution: {integrity: sha512-Z1jVt1EGKia1X9CnBCkpAOhWy8FgQ7OmJ/IblEkT82yrFU/xJaxwujaTzLWqigewwynRQ9mmHfX9MtAfhxm0sA==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
@@ -8819,7 +8814,7 @@ packages:
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.31.0
|
||||
eslint-import-resolver-node: 0.3.7
|
||||
eslint-module-utils: 2.7.4_sqt5xxn4ciiurbqrzlaarm6ama
|
||||
eslint-module-utils: 2.7.4_v73lhamtbyinynmwa5fn7kpmfq
|
||||
has: 1.0.3
|
||||
is-core-module: 2.11.0
|
||||
is-glob: 4.0.3
|
||||
|
||||
Reference in New Issue
Block a user