Skip to content

Commit 7f39393

Browse files
authored
refactor: Standardize and share store definitions (#8)
1 parent e1625e2 commit 7f39393

31 files changed

Lines changed: 339 additions & 378 deletions

src/apis/extension-store-apis.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { createApp } from "@aklinker1/zeta";
2+
import { z } from "zod";
3+
import { contextPlugin } from "../plugins/context-plugin";
4+
import { NotFoundHttpError } from "@aklinker1/zeta";
5+
import { HttpStatus } from "@aklinker1/zeta";
6+
import { OpenApiTag } from "../enums";
7+
import { ExtensionStoreNameSchema } from "../models";
8+
9+
export const extensionStoreApis = createApp({
10+
tags: [OpenApiTag.ExtensionStores],
11+
})
12+
.use(contextPlugin)
13+
.get(
14+
"/api/rest/:storeName/:id/screenshots/:index",
15+
{
16+
operationId: "redirectToScreenshot",
17+
description:
18+
"Redirect to a screenshot's URL from the Chrome Web Store listing",
19+
params: z.object({
20+
storeName: ExtensionStoreNameSchema,
21+
id: z.string(),
22+
index: z.coerce.number().int().min(0),
23+
}),
24+
},
25+
async ({ params, stores, set }) => {
26+
const screenshotUrl = await stores[params.storeName].getScreenshotUrl(
27+
params.id,
28+
params.index,
29+
);
30+
if (!screenshotUrl)
31+
throw new NotFoundHttpError("Extension or screenshot not found");
32+
33+
set.status = HttpStatus.Found;
34+
set.headers["Location"] = screenshotUrl;
35+
},
36+
);
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { version } from "../../package.json";
44
import { createGraphql } from "../graphql";
55
import { z } from "zod";
66
import dedent from "dedent";
7+
import { OpenApiTag } from "../enums";
78

89
const PLAYGROUND_HTML = (PLAYGROUND_HTML_TEMPLATE as any as string).replace(
910
"{{VERSION}}",
@@ -12,12 +13,13 @@ const PLAYGROUND_HTML = (PLAYGROUND_HTML_TEMPLATE as any as string).replace(
1213

1314
const graphql = createGraphql();
1415

15-
export const graphqlRoutes = createApp()
16+
export const graphqlApis = createApp({
17+
tags: [OpenApiTag.Graphql],
18+
})
1619
.post(
1720
"/api",
1821
{
1922
summary: "Send Query",
20-
tags: ["GraphQL"],
2123
description:
2224
"Send a query to the GraphQL API. You can play around with queries on the [GraphiQL playground](/playground).",
2325
body: z
@@ -73,7 +75,6 @@ export const graphqlRoutes = createApp()
7375
"/playground",
7476
{
7577
operationId: "playground",
76-
tags: ["GraphQL"],
7778
description: dedent`
7879
Open the GraphiQL playground. This is where you can interact and test out
7980
the GraphQL API. It also contains the GraphQL documentation explorer.

src/apis/system-apis.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { createApp } from "@aklinker1/zeta";
2+
import z from "zod";
3+
import { version } from "../version";
4+
5+
export const systemApis = createApp({
6+
tags: ["System"],
7+
})
8+
.get(
9+
"/",
10+
{
11+
operationId: "apiDocsRedirect",
12+
summary: "API Docs Redirect",
13+
description: "Redirect to the API reference when visiting the root URL.",
14+
},
15+
({ set }) => {
16+
set.status = 302;
17+
set.headers.Location = "/scalar";
18+
},
19+
)
20+
.get(
21+
"/api/health",
22+
{
23+
operationId: "healthCheck",
24+
description: "Used to make sure the API is up and running.",
25+
responses: z.object({
26+
status: z.literal("ok"),
27+
version: z.string(),
28+
}),
29+
},
30+
() => ({ status: "ok" as const, version }),
31+
);

src/dependencies.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
import { createIocContainer } from "@aklinker1/zero-ioc";
2-
import { createChromeService } from "./utils/chrome/chrome-service";
3-
import { createFirefoxService } from "./utils/firefox/firefox-service";
4-
import { createEdgeService } from "./utils/edge/edge-service";
2+
import { createChromeWebStore } from "./services/chrome-web-store";
3+
import { createFirefoxAddonStore } from "./services/firefox-addon-store";
4+
import { createEdgeAddonStore } from "./services/edge-addon-store";
5+
import type { ExtensionStores } from "./services/extension-stores";
6+
import { ExtensionStoreName } from "./enums";
57

68
export const dependencies = createIocContainer()
7-
.register("chrome", createChromeService)
8-
.register("firefox", createFirefoxService)
9-
.register("edge", createEdgeService);
9+
.register("chromeWebStore", createChromeWebStore)
10+
.register("firefoxAddonStore", createFirefoxAddonStore)
11+
.register("edgeAddonStore", createEdgeAddonStore)
12+
.register(
13+
"stores",
14+
(deps) =>
15+
({
16+
[ExtensionStoreName.ChromeWebStore]: deps.chromeWebStore,
17+
[ExtensionStoreName.FirefoxAddonStore]: deps.firefoxAddonStore,
18+
[ExtensionStoreName.EdgeAddonStore]: deps.edgeAddonStore,
19+
20+
// Deprecated, but staying around for a while.
21+
[ExtensionStoreName.ChromeExtensions]: deps.chromeWebStore,
22+
[ExtensionStoreName.FirefoxExtensions]: deps.firefoxAddonStore,
23+
[ExtensionStoreName.EdgeExtensions]: deps.edgeAddonStore,
24+
}) satisfies ExtensionStores,
25+
);

src/enums.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export enum ExtensionStoreName {
2+
ChromeWebStore = "chrome-web-store",
3+
FirefoxAddonStore = "firefox-addon-store",
4+
EdgeAddonStore = "edge-addon-store",
5+
6+
/** @deprecated Use {@link ChromeWebStore} instead. */
7+
ChromeExtensions = "chrome-extensions",
8+
/** @deprecated Use {@link FirefoxAddonStore} instead. */
9+
FirefoxExtensions = "firefox-extensions",
10+
/** @deprecated Use {@link EdgeAddonStore} instead. */
11+
EdgeExtensions = "edge-extensions",
12+
}
13+
14+
export enum OpenApiTag {
15+
System = "System",
16+
ExtensionStores = "Extension Stores",
17+
Graphql = "GraphQL",
18+
}

src/graphql/resolvers.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
export const rootResolver: Gql.RootResolver = {
2-
chromeExtension: ({ id }, ctx) => ctx.chrome.getExtension(id),
3-
chromeExtensions: ({ ids }, ctx) => ctx.chrome.getExtensions(ids),
4-
firefoxAddon: ({ id }, ctx) => ctx.firefox.getAddon(id),
5-
firefoxAddons: ({ ids }, ctx) => ctx.firefox.getAddons(ids),
6-
edgeAddon: ({ id }, ctx) => ctx.edge.getAddon(id),
7-
edgeAddons: ({ ids }, ctx) => ctx.edge.getAddons(ids),
2+
chromeExtension: ({ id }, ctx) => ctx.chromeWebStore.getExtension(id),
3+
chromeExtensions: ({ ids }, ctx) => ctx.chromeWebStore.getExtensions(ids),
4+
firefoxAddon: ({ id }, ctx) => ctx.firefoxAddonStore.getExtension(id),
5+
firefoxAddons: ({ ids }, ctx) => ctx.firefoxAddonStore.getExtensions(ids),
6+
edgeAddon: ({ id }, ctx) => ctx.edgeAddonStore.getExtension(id),
7+
edgeAddons: ({ ids }, ctx) => ctx.edgeAddonStore.getExtensions(ids),
88
};

src/models.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import z from "zod";
2+
import { ExtensionStoreName } from "./enums";
3+
4+
export const ExtensionStoreNameSchema = z
5+
.enum(ExtensionStoreName)
6+
.meta({ ref: "ExtensionStoreName" });

src/routes/rest-routes.ts

Lines changed: 0 additions & 80 deletions
This file was deleted.

src/server.ts

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import consola from "consola";
22
import { createApp } from "@aklinker1/zeta";
33
import { corsPlugin } from "./plugins/cors-plugin";
4-
import { graphqlRoutes } from "./routes/grpahql-routes";
5-
import { restRoutes } from "./routes/rest-routes";
4+
import { graphqlApis } from "./apis/graphql-apis";
5+
import { extensionStoreApis } from "./apis/extension-store-apis";
66
import { zodSchemaAdapter } from "@aklinker1/zeta/adapters/zod-schema-adapter";
7-
import { version } from "../package.json";
8-
import { z } from "zod";
7+
import { version } from "./version";
98
import dedent from "dedent";
9+
import { systemApis } from "./apis/system-apis";
10+
import { OpenApiTag } from "./enums";
1011

1112
const app = createApp({
1213
schemaAdapter: zodSchemaAdapter,
@@ -35,47 +36,22 @@ const app = createApp({
3536
},
3637
tags: [
3738
{
38-
name: "GraphQL",
39+
name: OpenApiTag.Graphql,
3940
description: dedent`
4041
To play around with the GraphQL API, checkout the
4142
[GraphiQL Playground](/playground).
4243
`,
4344
},
44-
{ name: "Chrome Extensions" },
45-
{ name: "Firefox Addons" },
46-
{ name: "System" },
45+
{ name: OpenApiTag.ExtensionStores },
46+
{ name: OpenApiTag.System },
4747
],
4848
},
4949
})
5050
.onGlobalError(({ error }) => void consola.error(error))
5151
.use(corsPlugin)
52-
.use(restRoutes)
53-
.use(graphqlRoutes)
54-
.get(
55-
"/",
56-
{
57-
summary: "API Docs Redirect",
58-
tags: ["System"],
59-
description: "Redirect to the API reference when visiting the root URL.",
60-
},
61-
({ set }) => {
62-
set.status = 302;
63-
set.headers.Location = "/scalar";
64-
},
65-
)
66-
.get(
67-
"/api/health",
68-
{
69-
summary: "Health Check",
70-
tags: ["System"],
71-
description: "Used to make sure the API is up and running.",
72-
responses: z.object({
73-
status: z.literal("ok"),
74-
version: z.string(),
75-
}),
76-
},
77-
() => ({ status: "ok" as const, version }),
78-
);
52+
.use(systemApis)
53+
.use(extensionStoreApis)
54+
.use(graphqlApis);
7955

8056
export default app;
8157
export type App = typeof app;

0 commit comments

Comments
 (0)