From 50bba8460c82bf83bb81e37b17d04bee3a529ed6 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Wed, 17 Jan 2024 11:39:46 -1000 Subject: [PATCH 01/17] feat(manager): add `GitManager` --- .../src/managers/SliceMachineManager.ts | 47 ++- .../createSliceMachineManagerMiddleware.ts | 9 +- .../manager/src/managers/git/GitManager.ts | 292 ++++++++++++++++++ packages/manager/src/managers/git/types.ts | 18 ++ .../PrismicRepositoryManager.ts | 5 +- ...eManager-git-createGitHubAuthState.test.ts | 66 ++++ ...rismicRepository-fetchEnvironments.test.ts | 6 +- .../test/__testutils__/mockSliceMachineAPI.ts | 38 +++ 8 files changed, 447 insertions(+), 34 deletions(-) create mode 100644 packages/manager/src/managers/git/GitManager.ts create mode 100644 packages/manager/src/managers/git/types.ts create mode 100644 packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts diff --git a/packages/manager/src/managers/SliceMachineManager.ts b/packages/manager/src/managers/SliceMachineManager.ts index 4c2e2d9196..5e7ebdc535 100644 --- a/packages/manager/src/managers/SliceMachineManager.ts +++ b/packages/manager/src/managers/SliceMachineManager.ts @@ -10,6 +10,7 @@ import { } from "@slicemachine/plugin-kit"; import { createContentDigest } from "../lib/createContentDigest"; +import { buildPrismicRepositoryAPIEndpoint } from "../lib/buildPrismicRepositoryAPIEndpoint"; import { PackageManager, SliceMachineConfig } from "../types"; import { @@ -20,24 +21,20 @@ import { createPrismicAuthManager } from "../auth/createPrismicAuthManager"; import { API_ENDPOINTS, APIEndpoints } from "../constants/API_ENDPOINTS"; -import { UserManager } from "./user/UserManager"; -import { PrismicRepositoryManager } from "./prismicRepository/PrismicRepositoryManager"; - +import { CustomTypesManager } from "./customTypes/CustomTypesManager"; +import { DocumentationManager } from "./documentation/DocumentationManager"; +import { GitManager } from "./git/GitManager"; import { PluginsManager } from "./plugins/PluginsManager"; - +import { PrismicRepositoryManager } from "./prismicRepository/PrismicRepositoryManager"; import { ProjectManager } from "./project/ProjectManager"; -import { CustomTypesManager } from "./customTypes/CustomTypesManager"; -import { SlicesManager } from "./slices/SlicesManager"; -import { SnippetsManager } from "./snippets/SnippetsManager"; import { ScreenshotsManager } from "./screenshots/ScreenshotsManager"; import { SimulatorManager } from "./simulator/SimulatorManager"; - -import { VersionsManager } from "./versions/VersionsManager"; - -import { TelemetryManager } from "./telemetry/TelemetryManager"; -import { buildPrismicRepositoryAPIEndpoint } from "../lib/buildPrismicRepositoryAPIEndpoint"; -import { DocumentationManager } from "./documentation/DocumentationManager"; import { SliceTemplateLibraryManager } from "./sliceTemplateLibrary/SliceTemplateLibraryManager"; +import { SlicesManager } from "./slices/SlicesManager"; +import { SnippetsManager } from "./snippets/SnippetsManager"; +import { TelemetryManager } from "./telemetry/TelemetryManager"; +import { UserManager } from "./user/UserManager"; +import { VersionsManager } from "./versions/VersionsManager"; type SliceMachineManagerGetStateReturnType = { env: { @@ -104,15 +101,16 @@ export class SliceMachineManager { cwd: string; customTypes: CustomTypesManager; + documentation: DocumentationManager; + git: GitManager; plugins: PluginsManager; prismicRepository: PrismicRepositoryManager; project: ProjectManager; screenshots: ScreenshotsManager; simulator: SimulatorManager; + sliceTemplateLibrary: SliceTemplateLibraryManager; slices: SlicesManager; snippets: SnippetsManager; - documentation: DocumentationManager; - sliceTemplateLibrary: SliceTemplateLibraryManager; telemetry: TelemetryManager; user: UserManager; versions: VersionsManager; @@ -123,25 +121,22 @@ export class SliceMachineManager { // authentication-related methods. this._prismicAuthManager = createPrismicAuthManager(); - this.user = new UserManager(this); - this.prismicRepository = new PrismicRepositoryManager(this); - + this.customTypes = new CustomTypesManager(this); + this.documentation = new DocumentationManager(this); + this.git = new GitManager(this); this.plugins = new PluginsManager(this, { nativePlugins: args?.nativePlugins, }); - + this.prismicRepository = new PrismicRepositoryManager(this); this.project = new ProjectManager(this); - this.customTypes = new CustomTypesManager(this); - this.slices = new SlicesManager(this); - this.snippets = new SnippetsManager(this); this.screenshots = new ScreenshotsManager(this); this.simulator = new SimulatorManager(this); - this.documentation = new DocumentationManager(this); this.sliceTemplateLibrary = new SliceTemplateLibraryManager(this); - - this.versions = new VersionsManager(this); - + this.slices = new SlicesManager(this); + this.snippets = new SnippetsManager(this); this.telemetry = new TelemetryManager(this); + this.user = new UserManager(this); + this.versions = new VersionsManager(this); this.cwd = args?.cwd ?? process.cwd(); } diff --git a/packages/manager/src/managers/createSliceMachineManagerMiddleware.ts b/packages/manager/src/managers/createSliceMachineManagerMiddleware.ts index 19fc798488..85f55094d7 100644 --- a/packages/manager/src/managers/createSliceMachineManagerMiddleware.ts +++ b/packages/manager/src/managers/createSliceMachineManagerMiddleware.ts @@ -24,22 +24,23 @@ const defineOmits = () => { const omitProcedures = defineOmits()([ "_sliceMachinePluginRunner", "_prismicAuthManager", + "getSliceMachinePluginRunner", + "getPrismicAuthManager", "customTypes._sliceMachineManager", + "documentation._sliceMachineManager", + "git._sliceMachineManager", "plugins._sliceMachineManager", "prismicRepository._sliceMachineManager", "project._sliceMachineManager", "screenshots._sliceMachineManager", "simulator._sliceMachineManager", + "sliceTemplateLibrary._sliceMachineManager", "slices._sliceMachineManager", "snippets._sliceMachineManager", "telemetry._sliceMachineManager", "user._sliceMachineManager", "versions._sliceMachineManager", - "documentation._sliceMachineManager", - "sliceTemplateLibrary._sliceMachineManager", - "getSliceMachinePluginRunner", - "getPrismicAuthManager", ]); export type SliceMachineManagerMiddleware = RPCMiddleware< diff --git a/packages/manager/src/managers/git/GitManager.ts b/packages/manager/src/managers/git/GitManager.ts new file mode 100644 index 0000000000..23f5067507 --- /dev/null +++ b/packages/manager/src/managers/git/GitManager.ts @@ -0,0 +1,292 @@ +import * as t from "io-ts"; +import * as tt from "io-ts-types"; + +import fetch from "../../lib/fetch"; +import { decode } from "../../lib/decode"; + +import { API_ENDPOINTS } from "../../constants/API_ENDPOINTS"; + +import { UnauthorizedError, UnexpectedDataError } from "../../errors"; + +import { BaseManager } from "../BaseManager"; + +import { GitRepo, GitRepoSpcifier, Namespace } from "./types"; + +type CreateGitHubAuthStateReturnType = { + key: string; + expiresAt: Date; +}; + +type GitManagerFetchOwnersReturnType = Namespace[]; + +type GitManagerFetchReposReturnType = GitRepo[]; + +type GitManagerFetchReposArgs = { + platform: "gitHub"; + owner: string; + query?: string; + page?: number; +}; + +type GitManagerFetchLinkedReposArgs = { + prismic: { + domain: string; + }; +}; + +type GitManagerFetchLinkedReposReturnType = GitRepoSpcifier[]; + +type GitManagerLinkRepoArgs = { + prismic: { + domain: string; + }; + git: { + provider: "gitHub"; + owner: string; + name: string; + }; +}; + +type GitManagerUnlinkRepoArgs = { + prismic: { + domain: string; + }; + git: { + provider: "gitHub"; + owner: string; + name: string; + }; +}; + +export class GitManager extends BaseManager { + async createGitHubAuthState(): Promise { + const url = new URL( + "./git/github/create-auth-state", + API_ENDPOINTS.SliceMachineV1, + ); + const res = await this.#fetch(url); + + if (!res.ok) { + switch (res.status) { + case 401: + throw new UnauthorizedError(); + default: + throw new Error("Failed to create GutHub auth state."); + } + } + + const json = await res.json(); + const { value, error } = decode( + t.type({ + key: t.string, + expiresAt: tt.DateFromISOString, + }), + json, + ); + + if (error) { + throw new UnexpectedDataError( + `Failed to decode GitHub auth state: ${error.errors.join(", ")}`, + { cause: error }, + ); + } + + return value; + } + + async fetchOwners(): Promise { + const url = new URL("./git/owners", API_ENDPOINTS.SliceMachineV1); + const res = await this.#fetch(url); + + if (!res.ok) { + switch (res.status) { + case 403: + throw new UnauthorizedError(); + default: + throw new Error("Failed to fetch owners."); + } + } + + const json = await res.json(); + const { value, error } = decode( + t.array( + t.type({ + platform: t.literal("gitHub"), + id: t.string, + name: t.string, + type: t.union([t.literal("user"), t.literal("team"), t.null]), + }), + ), + json, + ); + + if (error) { + throw new UnexpectedDataError( + `Failed to decode owners: ${error.errors.join(", ")}`, + { cause: error }, + ); + } + + return value; + } + + async fetchRepos( + args: GitManagerFetchReposArgs, + ): Promise { + const url = new URL("./git/repos", API_ENDPOINTS.SliceMachineV1); + url.searchParams.set("platform", args.platform); + url.searchParams.set("owner", args.owner); + if (args.query) { + url.searchParams.set("q", args.query); + } + if (args.page) { + url.searchParams.set("page", args.page.toString()); + } + + const res = await this.#fetch(url); + + if (!res.ok) { + switch (res.status) { + case 403: + throw new UnauthorizedError(); + default: + throw new Error("Failed to fetch repos."); + } + } + + const json = await res.json(); + const { value, error } = decode( + t.array( + t.type({ + platform: t.literal("gitHub"), + id: t.string, + owner: t.string, + name: t.string, + url: t.string, + pushedAt: tt.DateFromISOString, + }), + ), + json, + ); + + if (error) { + throw new UnexpectedDataError( + `Failed to decode repos: ${error.errors.join(", ")}`, + { cause: error }, + ); + } + + return value; + } + + async fetchLinkedRepos( + args: GitManagerFetchLinkedReposArgs, + ): Promise { + const url = new URL("./git/linked-repos", API_ENDPOINTS.SliceMachineV1); + url.searchParams.set("repository", args.prismic.domain); + + const res = await this.#fetch(url); + + if (!res.ok) { + switch (res.status) { + case 403: + throw new UnauthorizedError(); + default: + throw new Error("Failed to fetch linked repos."); + } + } + + const json = await res.json(); + const { value, error } = decode( + t.type({ + gitHubRepos: t.array( + t.type({ + provider: t.literal("gitHub"), + owner: t.string, + name: t.string, + }), + ), + }), + json, + ); + + if (error) { + throw new UnexpectedDataError( + `Failed to decode linked repos: ${error.errors.join(", ")}`, + { cause: error }, + ); + } + + return value.gitHubRepos; + } + + async linkRepo(args: GitManagerLinkRepoArgs): Promise { + const url = new URL("./git/linked-repos", API_ENDPOINTS.SliceMachineV1); + const res = await this.#fetch(url, { + method: "PUT", + body: JSON.stringify({ + prismic: { + domain: args.prismic.domain, + }, + git: { + provider: args.git.provider, + owner: args.git.owner, + name: args.git.name, + }, + }), + }); + + if (!res.ok) { + switch (res.status) { + case 403: + throw new UnauthorizedError(); + default: + throw new Error("Failed to link repos."); + } + } + } + + async unlinkRepo(args: GitManagerUnlinkRepoArgs): Promise { + const url = new URL("./git/linked-repos", API_ENDPOINTS.SliceMachineV1); + const res = await this.#fetch(url, { + method: "DELETE", + body: JSON.stringify({ + prismic: { + domain: args.prismic.domain, + }, + git: { + provider: args.git.provider, + owner: args.git.owner, + name: args.git.name, + }, + }), + }); + + if (!res.ok) { + switch (res.status) { + case 403: + throw new UnauthorizedError(); + default: + throw new Error("Failed to ulink repos."); + } + } + } + + async #fetch( + url: URL, + config?: { + method?: "POST" | "PUT" | "DELETE"; + body?: unknown; + }, + ) { + const authenticationToken = await this.user.getAuthenticationToken(); + + return await fetch(url, { + method: config?.method, + body: config?.body ? JSON.stringify(config?.body) : undefined, + headers: { + Authorization: `Bearer ${authenticationToken}`, + }, + }); + } +} diff --git a/packages/manager/src/managers/git/types.ts b/packages/manager/src/managers/git/types.ts new file mode 100644 index 0000000000..ca02175bc3 --- /dev/null +++ b/packages/manager/src/managers/git/types.ts @@ -0,0 +1,18 @@ +export type Namespace = { + id: string; + name: string; + type: "user" | "team" | null; +}; + +export type GitRepo = { + id: string; + owner: string; + name: string; + url: string; + pushedAt: Date; +}; + +export type GitRepoSpcifier = { + owner: string; + name: string; +}; diff --git a/packages/manager/src/managers/prismicRepository/PrismicRepositoryManager.ts b/packages/manager/src/managers/prismicRepository/PrismicRepositoryManager.ts index 3815160517..88b25f2de7 100644 --- a/packages/manager/src/managers/prismicRepository/PrismicRepositoryManager.ts +++ b/packages/manager/src/managers/prismicRepository/PrismicRepositoryManager.ts @@ -454,7 +454,10 @@ export class PrismicRepositoryManager extends BaseManager { ): Promise { const repositoryName = await this.project.getRepositoryName(); - const url = new URL(`./environments`, API_ENDPOINTS.SliceMachineV1); + const url = new URL( + `./environments`, + API_ENDPOINTS.SliceMachineEnvironmentsV1, + ); url.searchParams.set("repository", repositoryName); const res = await this._fetch({ url }); diff --git a/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts b/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts new file mode 100644 index 0000000000..913f3ec30a --- /dev/null +++ b/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts @@ -0,0 +1,66 @@ +import { expect, it } from "vitest"; + +import { createPrismicAuthLoginResponse } from "./__testutils__/createPrismicAuthLoginResponse"; +import { createTestPlugin } from "./__testutils__/createTestPlugin"; +import { createTestProject } from "./__testutils__/createTestProject"; +import { mockPrismicAuthAPI } from "./__testutils__/mockPrismicAuthAPI"; +import { mockPrismicUserAPI } from "./__testutils__/mockPrismicUserAPI"; + +import { createSliceMachineManager, SliceMachineManager } from "../src"; +import { rest, RestRequest } from "msw"; + +it("returns a GitHub auth state token", async (ctx) => { + const adapter = createTestPlugin(); + const cwd = await createTestProject({ adapter }); + const manager = createSliceMachineManager({ + nativePlugins: { [adapter.meta.name]: adapter }, + cwd, + }); + + await manager.plugins.initPlugins(); + + mockPrismicUserAPI(ctx); + mockPrismicAuthAPI(ctx); + + await manager.user.login(createPrismicAuthLoginResponse()); + + const authenticationToken = await manager.user.getAuthenticationToken(); + const key = "foo"; + const expiresAt = new Date(Date.now() + 1000 * 60 * 5); + + const checkAuthorizationToken = ( + req: RestRequest, + authenticationToken: string, + ): boolean => { + return req.headers.get("Authorization") === `Bearer ${authenticationToken}`; + }; + + const buildURL = (endpoint: string, manager: SliceMachineManager): string => { + return new URL( + endpoint, + manager.getAPIEndpoints().SliceMachineV1, + ).toString(); + }; + + ctx.msw.use( + rest.get( + buildURL("./git/github/create-auth-state", manager), + (req, res, ctx) => { + if (!checkAuthorizationToken(req, authenticationToken)) { + return; + } + + return res( + ctx.json({ + key, + expiresAt: expiresAt.toISOString(), + }), + ); + }, + ), + ); + + const result = await manager.git.createGitHubAuthState(); + + expect(result).toStrictEqual({ key, expiresAt }); +}); diff --git a/packages/manager/test/SliceMachineManager-prismicRepository-fetchEnvironments.test.ts b/packages/manager/test/SliceMachineManager-prismicRepository-fetchEnvironments.test.ts index a561eb6b43..3cd4edb721 100644 --- a/packages/manager/test/SliceMachineManager-prismicRepository-fetchEnvironments.test.ts +++ b/packages/manager/test/SliceMachineManager-prismicRepository-fetchEnvironments.test.ts @@ -293,7 +293,7 @@ it("throws UnauthenticatedError if the API returns 400", async (ctx) => { rest.get( new URL( "./environments", - manager.getAPIEndpoints().SliceMachineV1, + manager.getAPIEndpoints().SliceMachineEnvironmentsV1, ).toString(), (_req, res, ctx) => { return res(ctx.status(400)); @@ -324,7 +324,7 @@ it("throws UnauthenticatedError if the API returns 401", async (ctx) => { rest.get( new URL( "./environments", - manager.getAPIEndpoints().SliceMachineV1, + manager.getAPIEndpoints().SliceMachineEnvironmentsV1, ).toString(), (_req, res, ctx) => { return res(ctx.status(401)); @@ -355,7 +355,7 @@ it("throws UnauthorizedError if the API returns 403", async (ctx) => { rest.get( new URL( "./environments", - manager.getAPIEndpoints().SliceMachineV1, + manager.getAPIEndpoints().SliceMachineEnvironmentsV1, ).toString(), (_req, res, ctx) => { return res(ctx.status(403)); diff --git a/packages/manager/test/__testutils__/mockSliceMachineAPI.ts b/packages/manager/test/__testutils__/mockSliceMachineAPI.ts index d5e3cb5856..8aae64c131 100644 --- a/packages/manager/test/__testutils__/mockSliceMachineAPI.ts +++ b/packages/manager/test/__testutils__/mockSliceMachineAPI.ts @@ -11,6 +11,12 @@ type MockSliceMachineAPIConfig = { expectedCookies: string[]; environments: Environment[]; }; + gitGitHubCreateAuthStateV1Endpoint?: { + isSuccessful?: boolean; + expectedAuthenticationToken: string; + key: string; + expiresAt: Date; + }; }; export const mockSliceMachineAPI = ( @@ -50,4 +56,36 @@ export const mockSliceMachineAPI = ( ), ); } + + if (config.gitGitHubCreateAuthStateV1Endpoint) { + ctx.msw.use( + rest.get( + new URL(`./git/github/create-auth-state`, endpoint).toString(), + (req, res, ctx) => { + if ( + config.gitGitHubCreateAuthStateV1Endpoint && + (config.gitGitHubCreateAuthStateV1Endpoint.isSuccessful ?? true) + ) { + if ( + req.headers.get("Authorization") === + `Bearer ${config.gitGitHubCreateAuthStateV1Endpoint.expectedAuthenticationToken}` + ) { + return res( + ctx.json({ + key: config.gitGitHubCreateAuthStateV1Endpoint.key, + expiresAt: + config.gitGitHubCreateAuthStateV1Endpoint.expiresAt, + }), + ctx.status(200), + ); + } else { + return res(ctx.status(418)); + } + } else { + return res(ctx.status(418)); + } + }, + ), + ); + } }; From 7904dd34fc69b1f0d7a224dd609b998092bce801 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 18 Jan 2024 08:39:27 -1000 Subject: [PATCH 02/17] test: add initial tests --- packages/manager/package.json | 2 +- .../manager/src/managers/git/GitManager.ts | 60 ++++++----- .../PrismicRepositoryManager.ts | 5 +- .../test/PrismicAuthManager-login.test.ts | 3 + .../test/SliceMachineManager-getState.test.ts | 1 + ...eManager-git-createGitHubAuthState.test.ts | 83 +++++--------- ...achineManager-git-fetchLinkedRepos.test.ts | 38 +++++++ ...liceMachineManager-git-fetchOwners.test.ts | 34 ++++++ ...SliceMachineManager-git-fetchRepos.test.ts | 55 ++++++++++ .../SliceMachineManager-git-linkRepo.test.ts | 52 +++++++++ ...SliceMachineManager-git-unlinkRepo.test.ts | 52 +++++++++ ...rismicRepository-fetchEnvironments.test.ts | 6 +- ...liceMachineManager-telemetry-track.test.ts | 2 +- .../SliceMachineManager-user-login.test.ts | 1 + packages/manager/test/__setup__.ts | 45 +++++++- .../test/__testutils__/createAPIFixture.ts | 101 ++++++++++++++++++ yarn.lock | 27 ++++- 17 files changed, 472 insertions(+), 95 deletions(-) create mode 100644 packages/manager/test/SliceMachineManager-git-fetchLinkedRepos.test.ts create mode 100644 packages/manager/test/SliceMachineManager-git-fetchOwners.test.ts create mode 100644 packages/manager/test/SliceMachineManager-git-fetchRepos.test.ts create mode 100644 packages/manager/test/SliceMachineManager-git-linkRepo.test.ts create mode 100644 packages/manager/test/SliceMachineManager-git-unlinkRepo.test.ts create mode 100644 packages/manager/test/__testutils__/createAPIFixture.ts diff --git a/packages/manager/package.json b/packages/manager/package.json index 8a373eeffa..a0aa28f5ad 100644 --- a/packages/manager/package.json +++ b/packages/manager/package.json @@ -110,7 +110,7 @@ "eslint-config-prettier": "9.0.0", "eslint-plugin-prettier": "5.0.1", "eslint-plugin-tsdoc": "0.2.17", - "express": "4.18.2", + "express": "^1.0.0", "hook-std": "3.0.0", "memfs": "3.4.13", "msw": "1.1.0", diff --git a/packages/manager/src/managers/git/GitManager.ts b/packages/manager/src/managers/git/GitManager.ts index 23f5067507..e49ae15f5e 100644 --- a/packages/manager/src/managers/git/GitManager.ts +++ b/packages/manager/src/managers/git/GitManager.ts @@ -22,7 +22,7 @@ type GitManagerFetchOwnersReturnType = Namespace[]; type GitManagerFetchReposReturnType = GitRepo[]; type GitManagerFetchReposArgs = { - platform: "gitHub"; + provider: "gitHub"; owner: string; query?: string; page?: number; @@ -109,14 +109,16 @@ export class GitManager extends BaseManager { const json = await res.json(); const { value, error } = decode( - t.array( - t.type({ - platform: t.literal("gitHub"), - id: t.string, - name: t.string, - type: t.union([t.literal("user"), t.literal("team"), t.null]), - }), - ), + t.type({ + owners: t.array( + t.type({ + provider: t.literal("gitHub"), + id: t.string, + name: t.string, + type: t.union([t.literal("user"), t.literal("team"), t.null]), + }), + ), + }), json, ); @@ -127,14 +129,14 @@ export class GitManager extends BaseManager { ); } - return value; + return value.owners; } async fetchRepos( args: GitManagerFetchReposArgs, ): Promise { const url = new URL("./git/repos", API_ENDPOINTS.SliceMachineV1); - url.searchParams.set("platform", args.platform); + url.searchParams.set("provider", args.provider); url.searchParams.set("owner", args.owner); if (args.query) { url.searchParams.set("q", args.query); @@ -156,16 +158,18 @@ export class GitManager extends BaseManager { const json = await res.json(); const { value, error } = decode( - t.array( - t.type({ - platform: t.literal("gitHub"), - id: t.string, - owner: t.string, - name: t.string, - url: t.string, - pushedAt: tt.DateFromISOString, - }), - ), + t.type({ + repos: t.array( + t.type({ + provider: t.literal("gitHub"), + id: t.string, + owner: t.string, + name: t.string, + url: t.string, + pushedAt: tt.DateFromISOString, + }), + ), + }), json, ); @@ -176,7 +180,7 @@ export class GitManager extends BaseManager { ); } - return value; + return value.repos; } async fetchLinkedRepos( @@ -199,7 +203,7 @@ export class GitManager extends BaseManager { const json = await res.json(); const { value, error } = decode( t.type({ - gitHubRepos: t.array( + repos: t.array( t.type({ provider: t.literal("gitHub"), owner: t.string, @@ -217,14 +221,14 @@ export class GitManager extends BaseManager { ); } - return value.gitHubRepos; + return value.repos; } async linkRepo(args: GitManagerLinkRepoArgs): Promise { const url = new URL("./git/linked-repos", API_ENDPOINTS.SliceMachineV1); const res = await this.#fetch(url, { method: "PUT", - body: JSON.stringify({ + body: { prismic: { domain: args.prismic.domain, }, @@ -233,7 +237,7 @@ export class GitManager extends BaseManager { owner: args.git.owner, name: args.git.name, }, - }), + }, }); if (!res.ok) { @@ -250,7 +254,7 @@ export class GitManager extends BaseManager { const url = new URL("./git/linked-repos", API_ENDPOINTS.SliceMachineV1); const res = await this.#fetch(url, { method: "DELETE", - body: JSON.stringify({ + body: { prismic: { domain: args.prismic.domain, }, @@ -259,7 +263,7 @@ export class GitManager extends BaseManager { owner: args.git.owner, name: args.git.name, }, - }), + }, }); if (!res.ok) { diff --git a/packages/manager/src/managers/prismicRepository/PrismicRepositoryManager.ts b/packages/manager/src/managers/prismicRepository/PrismicRepositoryManager.ts index 88b25f2de7..3815160517 100644 --- a/packages/manager/src/managers/prismicRepository/PrismicRepositoryManager.ts +++ b/packages/manager/src/managers/prismicRepository/PrismicRepositoryManager.ts @@ -454,10 +454,7 @@ export class PrismicRepositoryManager extends BaseManager { ): Promise { const repositoryName = await this.project.getRepositoryName(); - const url = new URL( - `./environments`, - API_ENDPOINTS.SliceMachineEnvironmentsV1, - ); + const url = new URL(`./environments`, API_ENDPOINTS.SliceMachineV1); url.searchParams.set("repository", repositoryName); const res = await this._fetch({ url }); diff --git a/packages/manager/test/PrismicAuthManager-login.test.ts b/packages/manager/test/PrismicAuthManager-login.test.ts index a1eace8163..10bf1c4597 100644 --- a/packages/manager/test/PrismicAuthManager-login.test.ts +++ b/packages/manager/test/PrismicAuthManager-login.test.ts @@ -32,6 +32,9 @@ it("retains existing cookies in the auth state file", async (ctx) => { mockPrismicUserAPI(ctx); + // Clear all cookies + await prismicAuthManager.logout(); + await prismicAuthManager.login({ email: "name@example.com", cookies: ["foo=bar"], diff --git a/packages/manager/test/SliceMachineManager-getState.test.ts b/packages/manager/test/SliceMachineManager-getState.test.ts index be9df5d6b8..dc5e700378 100644 --- a/packages/manager/test/SliceMachineManager-getState.test.ts +++ b/packages/manager/test/SliceMachineManager-getState.test.ts @@ -13,6 +13,7 @@ it("returns global Slice Machine state", async () => { }, }); await manager.plugins.initPlugins(); + await manager.user.logout(); const result = await manager.getState(); expect(result.env.endpoints).toStrictEqual({ diff --git a/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts b/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts index 913f3ec30a..6d38b27010 100644 --- a/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts +++ b/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts @@ -1,66 +1,41 @@ import { expect, it } from "vitest"; -import { createPrismicAuthLoginResponse } from "./__testutils__/createPrismicAuthLoginResponse"; -import { createTestPlugin } from "./__testutils__/createTestPlugin"; -import { createTestProject } from "./__testutils__/createTestProject"; -import { mockPrismicAuthAPI } from "./__testutils__/mockPrismicAuthAPI"; -import { mockPrismicUserAPI } from "./__testutils__/mockPrismicUserAPI"; +import { UnauthenticatedError, UnauthorizedError } from "../src"; -import { createSliceMachineManager, SliceMachineManager } from "../src"; -import { rest, RestRequest } from "msw"; - -it("returns a GitHub auth state token", async (ctx) => { - const adapter = createTestPlugin(); - const cwd = await createTestProject({ adapter }); - const manager = createSliceMachineManager({ - nativePlugins: { [adapter.meta.name]: adapter }, - cwd, - }); - - await manager.plugins.initPlugins(); - - mockPrismicUserAPI(ctx); - mockPrismicAuthAPI(ctx); - - await manager.user.login(createPrismicAuthLoginResponse()); - - const authenticationToken = await manager.user.getAuthenticationToken(); +it("returns a GitHub auth state token", async ({ manager, api }) => { const key = "foo"; - const expiresAt = new Date(Date.now() + 1000 * 60 * 5); + const expiresAt = new Date(); - const checkAuthorizationToken = ( - req: RestRequest, - authenticationToken: string, - ): boolean => { - return req.headers.get("Authorization") === `Bearer ${authenticationToken}`; - }; + api.mockSliceMachineV1( + "./git/github/create-auth-state", + { key, expiresAt: expiresAt.toISOString() }, + { checkAuthentication: true }, + ); + + const res = await manager.git.createGitHubAuthState(); - const buildURL = (endpoint: string, manager: SliceMachineManager): string => { - return new URL( - endpoint, - manager.getAPIEndpoints().SliceMachineV1, - ).toString(); - }; + expect(res).toStrictEqual({ key, expiresAt }); +}); - ctx.msw.use( - rest.get( - buildURL("./git/github/create-auth-state", manager), - (req, res, ctx) => { - if (!checkAuthorizationToken(req, authenticationToken)) { - return; - } +it("throws UnauthorizedError if the API returns 401", async ({ + manager, + api, +}) => { + api.mockSliceMachineV1("./git/github/create-auth-state", undefined, { + statusCode: 401, + }); - return res( - ctx.json({ - key, - expiresAt: expiresAt.toISOString(), - }), - ); - }, - ), + await expect(() => manager.git.createGitHubAuthState()).rejects.toThrow( + UnauthorizedError, ); +}); - const result = await manager.git.createGitHubAuthState(); +it("throws UnauthenticatedError if the user is logged out", async ({ + manager, +}) => { + await manager.user.logout(); - expect(result).toStrictEqual({ key, expiresAt }); + await expect(() => manager.git.createGitHubAuthState()).rejects.toThrow( + UnauthenticatedError, + ); }); diff --git a/packages/manager/test/SliceMachineManager-git-fetchLinkedRepos.test.ts b/packages/manager/test/SliceMachineManager-git-fetchLinkedRepos.test.ts new file mode 100644 index 0000000000..226b116cca --- /dev/null +++ b/packages/manager/test/SliceMachineManager-git-fetchLinkedRepos.test.ts @@ -0,0 +1,38 @@ +import { expect, it } from "vitest"; +import { UnauthenticatedError, UnauthorizedError } from "../src"; + +it("returns a list of linked repos for a repo", async ({ manager, api }) => { + const domain = "domain"; + const repos = [{ provider: "gitHub", owner: "owner", name: "name" }]; + + api.mockSliceMachineV1( + "./git/linked-repos", + { repos }, + { searchParams: { repository: domain } }, + ); + + const res = await manager.git.fetchLinkedRepos({ prismic: { domain } }); + + expect(res).toStrictEqual(repos); +}); + +it("throws UnauthorizedError if the API returns 403", async ({ + manager, + api, +}) => { + api.mockSliceMachineV1("./git/linked-repos", undefined, { statusCode: 403 }); + + await expect(() => + manager.git.fetchLinkedRepos({ prismic: { domain: "domain" } }), + ).rejects.toThrow(UnauthorizedError); +}); + +it("throws UnauthenticatedError if the user is logged out", async ({ + manager, +}) => { + await manager.user.logout(); + + await expect(() => + manager.git.fetchLinkedRepos({ prismic: { domain: "domain" } }), + ).rejects.toThrow(UnauthenticatedError); +}); diff --git a/packages/manager/test/SliceMachineManager-git-fetchOwners.test.ts b/packages/manager/test/SliceMachineManager-git-fetchOwners.test.ts new file mode 100644 index 0000000000..1ea27a5ff1 --- /dev/null +++ b/packages/manager/test/SliceMachineManager-git-fetchOwners.test.ts @@ -0,0 +1,34 @@ +import { expect, it } from "vitest"; + +import { UnauthenticatedError, UnauthorizedError } from "../src"; + +it("returns a list of owners for the user", async ({ manager, api }) => { + const owners = [{ provider: "gitHub", id: "id", name: "name", type: "user" }]; + + api.mockSliceMachineV1("./git/owners", { owners }); + + const res = await manager.git.fetchOwners(); + + expect(res).toStrictEqual(owners); +}); + +it("throws UnauthorizedError if the API returns 403", async ({ + manager, + api, +}) => { + api.mockSliceMachineV1("./git/owners", undefined, { statusCode: 403 }); + + await expect(() => manager.git.fetchOwners()).rejects.toThrow( + UnauthorizedError, + ); +}); + +it("throws UnauthenticatedError if the user is logged out", async ({ + manager, +}) => { + await manager.user.logout(); + + await expect(() => manager.git.fetchOwners()).rejects.toThrow( + UnauthenticatedError, + ); +}); diff --git a/packages/manager/test/SliceMachineManager-git-fetchRepos.test.ts b/packages/manager/test/SliceMachineManager-git-fetchRepos.test.ts new file mode 100644 index 0000000000..7af847068a --- /dev/null +++ b/packages/manager/test/SliceMachineManager-git-fetchRepos.test.ts @@ -0,0 +1,55 @@ +import { expect, it } from "vitest"; +import { UnauthenticatedError, UnauthorizedError } from "../src"; + +it("returns a list of repos for an owner", async ({ manager, api }) => { + const repos = [ + { + provider: "gitHub", + id: "id", + owner: "owner", + name: "name", + url: "url", + pushedAt: new Date(), + }, + ]; + + api.mockSliceMachineV1( + "./git/repos", + { repos }, + { searchParams: { provider: "gitHub", owner: "owner" } }, + ); + + const res = await manager.git.fetchRepos({ + provider: "gitHub", + owner: "owner", + }); + + expect(res).toStrictEqual(repos); +}); + +it("throws UnauthorizedError if the API returns 403", async ({ + manager, + api, +}) => { + api.mockSliceMachineV1("./git/repos", undefined, { statusCode: 403 }); + + await expect(() => + manager.git.fetchRepos({ + provider: "gitHub", + owner: "owner", + }), + ).rejects.toThrow(UnauthorizedError); +}); + +it("throws UnauthenticatedError if the user is logged out", async ({ + manager, +}) => { + await manager.user.logout(); + + await expect(() => + manager.git.fetchRepos({ + provider: "gitHub", + owner: "owner", + }), + ).rejects.toThrow(UnauthenticatedError); +}); diff --git a/packages/manager/test/SliceMachineManager-git-linkRepo.test.ts b/packages/manager/test/SliceMachineManager-git-linkRepo.test.ts new file mode 100644 index 0000000000..ba988c3515 --- /dev/null +++ b/packages/manager/test/SliceMachineManager-git-linkRepo.test.ts @@ -0,0 +1,52 @@ +import { expect, it } from "vitest"; +import { UnauthenticatedError, UnauthorizedError } from "../src"; + +it("links a Git repository to a Prismic repository", async ({ + manager, + api, +}) => { + api.mockSliceMachineV1("./git/linked-repos", undefined, { + method: "put", + body: { + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }, + }); + + await expect( + manager.git.linkRepo({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).resolves.not.toThrow(); +}); + +it("throws UnauthorizedError if the API returns 403", async ({ + manager, + api, +}) => { + api.mockSliceMachineV1("./git/linked-repos", undefined, { + method: "put", + statusCode: 403, + }); + + await expect(() => + manager.git.linkRepo({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).rejects.toThrow(UnauthorizedError); +}); + +it("throws UnauthenticatedError if the user is logged out", async ({ + manager, +}) => { + await manager.user.logout(); + + await expect(() => + manager.git.linkRepo({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).rejects.toThrow(UnauthenticatedError); +}); diff --git a/packages/manager/test/SliceMachineManager-git-unlinkRepo.test.ts b/packages/manager/test/SliceMachineManager-git-unlinkRepo.test.ts new file mode 100644 index 0000000000..900e15d948 --- /dev/null +++ b/packages/manager/test/SliceMachineManager-git-unlinkRepo.test.ts @@ -0,0 +1,52 @@ +import { expect, it } from "vitest"; +import { UnauthenticatedError, UnauthorizedError } from "../src"; + +it("unlinks a Git repository from a Prismic repository", async ({ + manager, + api, +}) => { + api.mockSliceMachineV1("./git/linked-repos", undefined, { + method: "delete", + body: { + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }, + }); + + await expect( + manager.git.unlinkRepo({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).resolves.not.toThrow(); +}); + +it("throws UnauthorizedError if the API returns 403", async ({ + manager, + api, +}) => { + api.mockSliceMachineV1("./git/linked-repos", undefined, { + method: "delete", + statusCode: 403, + }); + + await expect(() => + manager.git.unlinkRepo({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).rejects.toThrow(UnauthorizedError); +}); + +it("throws UnauthenticatedError if the user is logged out", async ({ + manager, +}) => { + await manager.user.logout(); + + await expect(() => + manager.git.unlinkRepo({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).rejects.toThrow(UnauthenticatedError); +}); diff --git a/packages/manager/test/SliceMachineManager-prismicRepository-fetchEnvironments.test.ts b/packages/manager/test/SliceMachineManager-prismicRepository-fetchEnvironments.test.ts index 3cd4edb721..a561eb6b43 100644 --- a/packages/manager/test/SliceMachineManager-prismicRepository-fetchEnvironments.test.ts +++ b/packages/manager/test/SliceMachineManager-prismicRepository-fetchEnvironments.test.ts @@ -293,7 +293,7 @@ it("throws UnauthenticatedError if the API returns 400", async (ctx) => { rest.get( new URL( "./environments", - manager.getAPIEndpoints().SliceMachineEnvironmentsV1, + manager.getAPIEndpoints().SliceMachineV1, ).toString(), (_req, res, ctx) => { return res(ctx.status(400)); @@ -324,7 +324,7 @@ it("throws UnauthenticatedError if the API returns 401", async (ctx) => { rest.get( new URL( "./environments", - manager.getAPIEndpoints().SliceMachineEnvironmentsV1, + manager.getAPIEndpoints().SliceMachineV1, ).toString(), (_req, res, ctx) => { return res(ctx.status(401)); @@ -355,7 +355,7 @@ it("throws UnauthorizedError if the API returns 403", async (ctx) => { rest.get( new URL( "./environments", - manager.getAPIEndpoints().SliceMachineEnvironmentsV1, + manager.getAPIEndpoints().SliceMachineV1, ).toString(), (_req, res, ctx) => { return res(ctx.status(403)); diff --git a/packages/manager/test/SliceMachineManager-telemetry-track.test.ts b/packages/manager/test/SliceMachineManager-telemetry-track.test.ts index 5cd9c41b26..986fb6df28 100644 --- a/packages/manager/test/SliceMachineManager-telemetry-track.test.ts +++ b/packages/manager/test/SliceMachineManager-telemetry-track.test.ts @@ -28,7 +28,7 @@ vi.mock("@segment/analytics-node", () => { }; }); -it("sends a given event to Segment", async () => { +it.only("sends a given event to Segment", async () => { const adapter = createTestPlugin(); const cwd = await createTestProject({ adapter }); const manager = createSliceMachineManager({ diff --git a/packages/manager/test/SliceMachineManager-user-login.test.ts b/packages/manager/test/SliceMachineManager-user-login.test.ts index d50b3e939b..932237d03f 100644 --- a/packages/manager/test/SliceMachineManager-user-login.test.ts +++ b/packages/manager/test/SliceMachineManager-user-login.test.ts @@ -42,6 +42,7 @@ it("retains existing cookies in the auth state file", async (ctx) => { mockPrismicUserAPI(ctx); + await manager.user.logout(); await manager.user.login({ email: "name@example.com", cookies: ["foo=bar"], diff --git a/packages/manager/test/__setup__.ts b/packages/manager/test/__setup__.ts index 75040b5b5c..521e919f1d 100644 --- a/packages/manager/test/__setup__.ts +++ b/packages/manager/test/__setup__.ts @@ -4,12 +4,18 @@ import { createMockFactory, MockFactory } from "@prismicio/mock"; import * as fs from "node:fs/promises"; import * as path from "node:path"; import * as os from "node:os"; +import { createSliceMachineManager, SliceMachineManager } from "../src"; +import { createTestPlugin } from "./__testutils__/createTestPlugin"; +import { createTestProject } from "./__testutils__/createTestProject"; +import { APIFixture, createAPIFixture } from "./__testutils__/createAPIFixture"; declare module "vitest" { export interface TestContext { mockPrismic: MockFactory; msw: SetupServer; sliceMachineUIDirectory: string; + manager: SliceMachineManager; + api: APIFixture; } } @@ -137,7 +143,7 @@ beforeAll(() => { }); beforeEach(async (ctx) => { - ctx.mockPrismic = createMockFactory({ seed: ctx.meta.name }); + ctx.mockPrismic = createMockFactory({ seed: ctx.task.name }); ctx.msw = mswServer; ctx.msw.resetHandlers(); @@ -151,6 +157,43 @@ beforeEach(async (ctx) => { ctx.sliceMachineUIDirectory = path.dirname( MOCK_SLICE_MACHINE_PACKAGE_JSON_PATH, ); + + const adapter = createTestPlugin(); + const cwd = await createTestProject({ adapter }); + const manager = createSliceMachineManager({ + nativePlugins: { [adapter.meta.name]: adapter }, + cwd, + }); + await manager.plugins.initPlugins(); + + ctx.manager = manager; + + const api = createAPIFixture({ manager, mswServer }); + api.mockPrismicUser( + "./profile", + { + userId: "userId", + shortId: "shortId", + intercomHash: "intercomHash", + email: "email", + firstName: "firstName", + lastName: "lastName", + }, + { checkAuthentication: false }, + ); + api.mockPrismicAuthentication("./validate", undefined, { + checkAuthentication: false, + }); + api.mockPrismicAuthentication("./refreshtoken", undefined, { + checkAuthentication: false, + }); + + ctx.api = api; + + await manager.user.login({ + email: `name@example.com`, + cookies: ["prismic-auth=token", "SESSION=session"], + }); }); afterAll(() => { diff --git a/packages/manager/test/__testutils__/createAPIFixture.ts b/packages/manager/test/__testutils__/createAPIFixture.ts new file mode 100644 index 0000000000..e6b6f427f3 --- /dev/null +++ b/packages/manager/test/__testutils__/createAPIFixture.ts @@ -0,0 +1,101 @@ +import { SetupServer } from "msw/lib/node"; +import { rest } from "msw"; + +import { SliceMachineManager } from "../../src"; + +type MockOptions = { + method?: keyof typeof rest; + statusCode?: number; + checkAuthentication?: boolean; + searchParams?: Record; + body?: SerializableValue; +}; + +export type SerializableValueObject = { [Key in string]: SerializableValue } & { + [Key in string]?: SerializableValue | undefined; +}; +export type SerializableValueArray = + | SerializableValue[] + | readonly SerializableValue[]; +export type SerializableValuePrimitive = + | string + | number + | boolean + | Date + | null; +export type SerializableValue = + | SerializableValuePrimitive + | SerializableValueObject + | SerializableValueArray; + +export type APIFixture = { + [P in keyof ReturnType< + SliceMachineManager["getAPIEndpoints"] + > as `mock${P}`]: ( + path: string, + response?: SerializableValue, + options?: MockOptions, + ) => void; +}; + +export const createAPIFixture = (args: { + manager: SliceMachineManager; + mswServer: SetupServer; +}): APIFixture => { + const apiEndpoints = args.manager.getAPIEndpoints(); + + const api = {} as APIFixture; + + for (const key in apiEndpoints) { + api[`mock${key}` as keyof typeof api] = (path, response, options) => { + const apiEndpoint = apiEndpoints[key as keyof typeof apiEndpoints]; + const method = options?.method ?? "get"; + + const handler = rest[method]( + new URL(path, apiEndpoint).toString(), + async (req, res, ctx) => { + // TODO: Enable by default after fixing "Error [ERR_STREAM_PREMATURE_CLOSE]: Premature close" error. + if (options?.checkAuthentication) { + const authenticationToken = + await args.manager.user.getAuthenticationToken(); + if ( + req.headers.get("Authorization") !== + `Bearer ${authenticationToken}` + ) { + return; + } + } + + if (options?.searchParams) { + for (const name in options.searchParams) { + if ( + req.url.searchParams.get(name) !== options.searchParams[name] + ) { + return; + } + } + } + + if (options?.body) { + if ( + JSON.stringify(await req.json()) !== JSON.stringify(options.body) + ) { + return; + } + } + + return res( + typeof response === "object" + ? ctx.json(response) + : ctx.body(response), + ctx.status(options?.statusCode ?? 200), + ); + }, + ); + + args.mswServer.use(handler); + }; + } + + return api; +}; diff --git a/yarn.lock b/yarn.lock index 3eddd866ae..3c04100626 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8420,7 +8420,7 @@ __metadata: eslint-plugin-prettier: 5.0.1 eslint-plugin-tsdoc: 0.2.17 execa: ^7.1.1 - express: 4.18.2 + express: ^1.0.0 file-type: ^18.2.1 fp-ts: ^2.13.1 get-port: ^6.1.2 @@ -14877,6 +14877,15 @@ __metadata: languageName: node linkType: hard +"connect@npm:>= 0.5.0 < 1.0.0": + version: 0.5.10 + resolution: "connect@npm:0.5.10" + dependencies: + qs: ">= 0.0.6" + checksum: 53c9fe4b4db07dfe4aad7835e44af43fa8d7f2543a4cbc59c27e44dfd78edfd468988eb9ef6f36c89b1dca4ac4560d81c7f537876653e0e6ddac1d6f5b523253 + languageName: node + linkType: hard + "connect@npm:^3.7.0": version: 3.7.0 resolution: "connect@npm:3.7.0" @@ -17958,7 +17967,19 @@ __metadata: languageName: node linkType: hard -"express@npm:4.18.2, express@npm:^4.17.3, express@npm:^4.18.2": +"express@npm:^1.0.0": + version: 1.0.8 + resolution: "express@npm:1.0.8" + dependencies: + connect: ">= 0.5.0 < 1.0.0" + qs: ">= 0.0.5" + bin: + express: ./bin/express + checksum: 419e150c47a019d262bad79d9ea81ee3a339a2711fe51f6bfd579ba9b6042b088e364625a4f83a38d5dd4093734a154dfbcfebe78803baf4f96f6f518a6cc383 + languageName: node + linkType: hard + +"express@npm:^4.17.3, express@npm:^4.18.2": version: 4.18.2 resolution: "express@npm:4.18.2" dependencies: @@ -27958,7 +27979,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:^6.10.0, qs@npm:^6.11.0": +"qs@npm:>= 0.0.5, qs@npm:>= 0.0.6, qs@npm:^6.10.0, qs@npm:^6.11.0": version: 6.11.2 resolution: "qs@npm:6.11.2" dependencies: From c1519156099ed575fadc33cdc49607bc6906295d Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 18 Jan 2024 12:41:31 -1000 Subject: [PATCH 03/17] test: fix broken tests --- ...eManager-git-createGitHubAuthState.test.ts | 5 +- ...achineManager-git-fetchLinkedRepos.test.ts | 9 ++- ...liceMachineManager-git-fetchOwners.test.ts | 5 +- ...SliceMachineManager-git-fetchRepos.test.ts | 5 +- .../SliceMachineManager-git-linkRepo.test.ts | 4 ++ ...SliceMachineManager-git-unlinkRepo.test.ts | 4 ++ ...anager-project-installDependencies.test.ts | 12 ---- ...liceMachineManager-telemetry-group.test.ts | 18 ------ ...eMachineManager-telemetry-identify.test.ts | 18 ------ ...liceMachineManager-telemetry-track.test.ts | 20 +------ packages/manager/test/__setup__.ts | 59 +++++++++++++++++-- 11 files changed, 83 insertions(+), 76 deletions(-) diff --git a/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts b/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts index 6d38b27010..cc6d7d2d0e 100644 --- a/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts +++ b/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts @@ -2,7 +2,7 @@ import { expect, it } from "vitest"; import { UnauthenticatedError, UnauthorizedError } from "../src"; -it("returns a GitHub auth state token", async ({ manager, api }) => { +it("returns a GitHub auth state token", async ({ manager, api, login }) => { const key = "foo"; const expiresAt = new Date(); @@ -12,6 +12,7 @@ it("returns a GitHub auth state token", async ({ manager, api }) => { { checkAuthentication: true }, ); + await login(); const res = await manager.git.createGitHubAuthState(); expect(res).toStrictEqual({ key, expiresAt }); @@ -20,11 +21,13 @@ it("returns a GitHub auth state token", async ({ manager, api }) => { it("throws UnauthorizedError if the API returns 401", async ({ manager, api, + login, }) => { api.mockSliceMachineV1("./git/github/create-auth-state", undefined, { statusCode: 401, }); + await login(); await expect(() => manager.git.createGitHubAuthState()).rejects.toThrow( UnauthorizedError, ); diff --git a/packages/manager/test/SliceMachineManager-git-fetchLinkedRepos.test.ts b/packages/manager/test/SliceMachineManager-git-fetchLinkedRepos.test.ts index 226b116cca..5313b47a67 100644 --- a/packages/manager/test/SliceMachineManager-git-fetchLinkedRepos.test.ts +++ b/packages/manager/test/SliceMachineManager-git-fetchLinkedRepos.test.ts @@ -1,7 +1,11 @@ import { expect, it } from "vitest"; import { UnauthenticatedError, UnauthorizedError } from "../src"; -it("returns a list of linked repos for a repo", async ({ manager, api }) => { +it("returns a list of linked repos for a repo", async ({ + manager, + api, + login, +}) => { const domain = "domain"; const repos = [{ provider: "gitHub", owner: "owner", name: "name" }]; @@ -11,6 +15,7 @@ it("returns a list of linked repos for a repo", async ({ manager, api }) => { { searchParams: { repository: domain } }, ); + await login(); const res = await manager.git.fetchLinkedRepos({ prismic: { domain } }); expect(res).toStrictEqual(repos); @@ -19,9 +24,11 @@ it("returns a list of linked repos for a repo", async ({ manager, api }) => { it("throws UnauthorizedError if the API returns 403", async ({ manager, api, + login, }) => { api.mockSliceMachineV1("./git/linked-repos", undefined, { statusCode: 403 }); + await login(); await expect(() => manager.git.fetchLinkedRepos({ prismic: { domain: "domain" } }), ).rejects.toThrow(UnauthorizedError); diff --git a/packages/manager/test/SliceMachineManager-git-fetchOwners.test.ts b/packages/manager/test/SliceMachineManager-git-fetchOwners.test.ts index 1ea27a5ff1..7ff9bd8dbf 100644 --- a/packages/manager/test/SliceMachineManager-git-fetchOwners.test.ts +++ b/packages/manager/test/SliceMachineManager-git-fetchOwners.test.ts @@ -2,11 +2,12 @@ import { expect, it } from "vitest"; import { UnauthenticatedError, UnauthorizedError } from "../src"; -it("returns a list of owners for the user", async ({ manager, api }) => { +it("returns a list of owners for the user", async ({ manager, api, login }) => { const owners = [{ provider: "gitHub", id: "id", name: "name", type: "user" }]; api.mockSliceMachineV1("./git/owners", { owners }); + await login(); const res = await manager.git.fetchOwners(); expect(res).toStrictEqual(owners); @@ -15,9 +16,11 @@ it("returns a list of owners for the user", async ({ manager, api }) => { it("throws UnauthorizedError if the API returns 403", async ({ manager, api, + login, }) => { api.mockSliceMachineV1("./git/owners", undefined, { statusCode: 403 }); + await login(); await expect(() => manager.git.fetchOwners()).rejects.toThrow( UnauthorizedError, ); diff --git a/packages/manager/test/SliceMachineManager-git-fetchRepos.test.ts b/packages/manager/test/SliceMachineManager-git-fetchRepos.test.ts index 7af847068a..260c6de6f1 100644 --- a/packages/manager/test/SliceMachineManager-git-fetchRepos.test.ts +++ b/packages/manager/test/SliceMachineManager-git-fetchRepos.test.ts @@ -1,7 +1,7 @@ import { expect, it } from "vitest"; import { UnauthenticatedError, UnauthorizedError } from "../src"; -it("returns a list of repos for an owner", async ({ manager, api }) => { +it("returns a list of repos for an owner", async ({ manager, api, login }) => { const repos = [ { provider: "gitHub", @@ -19,6 +19,7 @@ it("returns a list of repos for an owner", async ({ manager, api }) => { { searchParams: { provider: "gitHub", owner: "owner" } }, ); + await login(); const res = await manager.git.fetchRepos({ provider: "gitHub", owner: "owner", @@ -30,9 +31,11 @@ it("returns a list of repos for an owner", async ({ manager, api }) => { it("throws UnauthorizedError if the API returns 403", async ({ manager, api, + login, }) => { api.mockSliceMachineV1("./git/repos", undefined, { statusCode: 403 }); + await login(); await expect(() => manager.git.fetchRepos({ provider: "gitHub", diff --git a/packages/manager/test/SliceMachineManager-git-linkRepo.test.ts b/packages/manager/test/SliceMachineManager-git-linkRepo.test.ts index ba988c3515..0f57b57d38 100644 --- a/packages/manager/test/SliceMachineManager-git-linkRepo.test.ts +++ b/packages/manager/test/SliceMachineManager-git-linkRepo.test.ts @@ -4,6 +4,7 @@ import { UnauthenticatedError, UnauthorizedError } from "../src"; it("links a Git repository to a Prismic repository", async ({ manager, api, + login, }) => { api.mockSliceMachineV1("./git/linked-repos", undefined, { method: "put", @@ -13,6 +14,7 @@ it("links a Git repository to a Prismic repository", async ({ }, }); + await login(); await expect( manager.git.linkRepo({ prismic: { domain: "domain" }, @@ -24,12 +26,14 @@ it("links a Git repository to a Prismic repository", async ({ it("throws UnauthorizedError if the API returns 403", async ({ manager, api, + login, }) => { api.mockSliceMachineV1("./git/linked-repos", undefined, { method: "put", statusCode: 403, }); + await login(); await expect(() => manager.git.linkRepo({ prismic: { domain: "domain" }, diff --git a/packages/manager/test/SliceMachineManager-git-unlinkRepo.test.ts b/packages/manager/test/SliceMachineManager-git-unlinkRepo.test.ts index 900e15d948..7394f011db 100644 --- a/packages/manager/test/SliceMachineManager-git-unlinkRepo.test.ts +++ b/packages/manager/test/SliceMachineManager-git-unlinkRepo.test.ts @@ -4,6 +4,7 @@ import { UnauthenticatedError, UnauthorizedError } from "../src"; it("unlinks a Git repository from a Prismic repository", async ({ manager, api, + login, }) => { api.mockSliceMachineV1("./git/linked-repos", undefined, { method: "delete", @@ -13,6 +14,7 @@ it("unlinks a Git repository from a Prismic repository", async ({ }, }); + await login(); await expect( manager.git.unlinkRepo({ prismic: { domain: "domain" }, @@ -24,12 +26,14 @@ it("unlinks a Git repository from a Prismic repository", async ({ it("throws UnauthorizedError if the API returns 403", async ({ manager, api, + login, }) => { api.mockSliceMachineV1("./git/linked-repos", undefined, { method: "delete", statusCode: 403, }); + await login(); await expect(() => manager.git.unlinkRepo({ prismic: { domain: "domain" }, diff --git a/packages/manager/test/SliceMachineManager-project-installDependencies.test.ts b/packages/manager/test/SliceMachineManager-project-installDependencies.test.ts index 23dc83db5e..e05011081d 100644 --- a/packages/manager/test/SliceMachineManager-project-installDependencies.test.ts +++ b/packages/manager/test/SliceMachineManager-project-installDependencies.test.ts @@ -6,18 +6,6 @@ import { watchStd } from "./__testutils__/watchStd"; import { createSliceMachineManager } from "../src"; -vi.mock("execa", async () => { - const execa: typeof import("execa") = await vi.importActual("execa"); - - return { - ...execa, - execaCommand: ((command: string, options: Record) => { - // Replace command with simple `echo` - return execa.execaCommand(`echo 'mock command ran: ${command}'`, options); - }) as typeof execa.execaCommand, - }; -}); - it("installs dependencies", async () => { const adapter = createTestPlugin(); const cwd = await createTestProject({ adapter }); diff --git a/packages/manager/test/SliceMachineManager-telemetry-group.test.ts b/packages/manager/test/SliceMachineManager-telemetry-group.test.ts index 074176aa94..95310e3f1c 100644 --- a/packages/manager/test/SliceMachineManager-telemetry-group.test.ts +++ b/packages/manager/test/SliceMachineManager-telemetry-group.test.ts @@ -6,24 +6,6 @@ import { createTestProject } from "./__testutils__/createTestProject"; import { createSliceMachineManager } from "../src"; -vi.mock("@segment/analytics-node", () => { - const MockSegmentClient = vi.fn(); - - MockSegmentClient.prototype.group = vi.fn( - (_message: unknown, callback?: (error?: unknown) => void) => { - if (callback) { - callback(); - } - }, - ); - - MockSegmentClient.prototype.on = vi.fn(); - - return { - Analytics: MockSegmentClient, - }; -}); - it("sends a group payload to Segment", async () => { const adapter = createTestPlugin(); const cwd = await createTestProject({ adapter }); diff --git a/packages/manager/test/SliceMachineManager-telemetry-identify.test.ts b/packages/manager/test/SliceMachineManager-telemetry-identify.test.ts index e15879a2fc..c12be807ed 100644 --- a/packages/manager/test/SliceMachineManager-telemetry-identify.test.ts +++ b/packages/manager/test/SliceMachineManager-telemetry-identify.test.ts @@ -6,24 +6,6 @@ import { createTestProject } from "./__testutils__/createTestProject"; import { createSliceMachineManager } from "../src"; -vi.mock("@segment/analytics-node", () => { - const MockSegmentClient = vi.fn(); - - MockSegmentClient.prototype.identify = vi.fn( - (_message: unknown, callback?: (error?: unknown) => void) => { - if (callback) { - callback(); - } - }, - ); - - MockSegmentClient.prototype.on = vi.fn(); - - return { - Analytics: MockSegmentClient, - }; -}); - it("sends an identification payload to Segment", async () => { const adapter = createTestPlugin(); const cwd = await createTestProject({ adapter }); diff --git a/packages/manager/test/SliceMachineManager-telemetry-track.test.ts b/packages/manager/test/SliceMachineManager-telemetry-track.test.ts index 986fb6df28..19ffba365f 100644 --- a/packages/manager/test/SliceMachineManager-telemetry-track.test.ts +++ b/packages/manager/test/SliceMachineManager-telemetry-track.test.ts @@ -10,25 +10,7 @@ import { mockSliceMachineAPI } from "./__testutils__/mockSliceMachineAPI"; import { createSliceMachineManager, Environment } from "../src"; -vi.mock("@segment/analytics-node", () => { - const MockSegmentClient = vi.fn(); - - MockSegmentClient.prototype.track = vi.fn( - (_message: unknown, callback?: (error?: unknown) => void) => { - if (callback) { - callback(); - } - }, - ); - - MockSegmentClient.prototype.on = vi.fn(); - - return { - Analytics: MockSegmentClient, - }; -}); - -it.only("sends a given event to Segment", async () => { +it("sends a given event to Segment", async () => { const adapter = createTestPlugin(); const cwd = await createTestProject({ adapter }); const manager = createSliceMachineManager({ diff --git a/packages/manager/test/__setup__.ts b/packages/manager/test/__setup__.ts index 521e919f1d..749180aca0 100644 --- a/packages/manager/test/__setup__.ts +++ b/packages/manager/test/__setup__.ts @@ -16,6 +16,7 @@ declare module "vitest" { sliceMachineUIDirectory: string; manager: SliceMachineManager; api: APIFixture; + login: () => Promise; } } @@ -136,6 +137,53 @@ vi.mock("module", async () => { } as typeof actual; }); +vi.mock("@segment/analytics-node", async () => { + const { Analytics }: typeof import("@segment/analytics-node") = + await vi.importActual("@segment/analytics-node"); + + Analytics.prototype.track = vi.fn( + (_message: unknown, callback?: (error?: unknown) => void) => { + if (callback) { + callback(); + } + }, + ); + + Analytics.prototype.identify = vi.fn( + (_message: unknown, callback?: (error?: unknown) => void) => { + if (callback) { + callback(); + } + }, + ); + + Analytics.prototype.group = vi.fn( + (_message: unknown, callback?: (error?: unknown) => void) => { + if (callback) { + callback(); + } + }, + ); + + Analytics.prototype.on = vi.fn(); + + return { + Analytics, + }; +}); + +vi.mock("execa", async () => { + const execa: typeof import("execa") = await vi.importActual("execa"); + + return { + ...execa, + execaCommand: ((command: string, options: Record) => { + // Replace command with simple `echo` + return execa.execaCommand(`echo 'mock command ran: ${command}'`, options); + }) as typeof execa.execaCommand, + }; +}); + const mswServer = setupServer(); beforeAll(() => { @@ -167,6 +215,12 @@ beforeEach(async (ctx) => { await manager.plugins.initPlugins(); ctx.manager = manager; + ctx.login = async () => { + await manager.user.login({ + email: `name@example.com`, + cookies: ["prismic-auth=token", "SESSION=session"], + }); + }; const api = createAPIFixture({ manager, mswServer }); api.mockPrismicUser( @@ -189,11 +243,6 @@ beforeEach(async (ctx) => { }); ctx.api = api; - - await manager.user.login({ - email: `name@example.com`, - cookies: ["prismic-auth=token", "SESSION=session"], - }); }); afterAll(() => { From 675596b74d9e5d66e39a6515b4c5d956d5792d2f Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 18 Jan 2024 13:00:01 -1000 Subject: [PATCH 04/17] chore: revert `express` version change (no idea how that happened) --- packages/manager/package.json | 2 +- yarn.lock | 247 ++++++++++++++++++++++++++++++---- 2 files changed, 219 insertions(+), 30 deletions(-) diff --git a/packages/manager/package.json b/packages/manager/package.json index 4c88c7779d..7d764a8035 100644 --- a/packages/manager/package.json +++ b/packages/manager/package.json @@ -110,7 +110,7 @@ "eslint-config-prettier": "9.0.0", "eslint-plugin-prettier": "5.0.1", "eslint-plugin-tsdoc": "0.2.17", - "express": "^1.0.0", + "express": "4.0.0", "hook-std": "3.0.0", "memfs": "3.4.13", "msw": "1.1.0", diff --git a/yarn.lock b/yarn.lock index 56ad6686ea..cdb37e2798 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8421,7 +8421,7 @@ __metadata: eslint-plugin-prettier: 5.0.1 eslint-plugin-tsdoc: 0.2.17 execa: ^7.1.1 - express: ^1.0.0 + express: 4.0.0 file-type: ^18.2.1 fp-ts: ^2.13.1 get-port: ^6.1.2 @@ -12319,6 +12319,16 @@ __metadata: languageName: node linkType: hard +"accepts@npm:1.0.0": + version: 1.0.0 + resolution: "accepts@npm:1.0.0" + dependencies: + mime: ~1.2.11 + negotiator: ~0.3.0 + checksum: 7689e77f56e4eda9cbd9a2278ef12f2f1d4519112dacdfa80d8c6b22aeb003f5bad7b559dce1c3d92c6111c8f548bbc6c17e47063c6be6bfb1bcee584474ba11 + languageName: node + linkType: hard + "accepts@npm:~1.3.5, accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" @@ -13681,6 +13691,13 @@ __metadata: languageName: node linkType: hard +"buffer-crc32@npm:0.2.1": + version: 0.2.1 + resolution: "buffer-crc32@npm:0.2.1" + checksum: eec890decc253b04ad5d032c0644e6d6cf24606c5a8fd097b8f7b2ac3d9468685d3cc3e041625c59b30c67280fcbaba6a7c916df0cf6c6efbc87d21a2f69edae + languageName: node + linkType: hard + "buffer-crc32@npm:^0.2.1, buffer-crc32@npm:^0.2.13, buffer-crc32@npm:^0.2.5, buffer-crc32@npm:~0.2.3": version: 0.2.13 resolution: "buffer-crc32@npm:0.2.13" @@ -14878,15 +14895,6 @@ __metadata: languageName: node linkType: hard -"connect@npm:>= 0.5.0 < 1.0.0": - version: 0.5.10 - resolution: "connect@npm:0.5.10" - dependencies: - qs: ">= 0.0.6" - checksum: 53c9fe4b4db07dfe4aad7835e44af43fa8d7f2543a4cbc59c27e44dfd78edfd468988eb9ef6f36c89b1dca4ac4560d81c7f537876653e0e6ddac1d6f5b523253 - languageName: node - linkType: hard - "connect@npm:^3.7.0": version: 3.7.0 resolution: "connect@npm:3.7.0" @@ -15010,6 +15018,13 @@ __metadata: languageName: node linkType: hard +"cookie-signature@npm:1.0.3": + version: 1.0.3 + resolution: "cookie-signature@npm:1.0.3" + checksum: 8dc7824ed4368b0c85f13ea392f844ab8875e433dfba8d2542a2f1282b21847cd05ed893f23b3e2ff1eea270975efd7abdfad9956bf09e896d942d5c8d85648a + languageName: node + linkType: hard + "cookie-signature@npm:1.0.6": version: 1.0.6 resolution: "cookie-signature@npm:1.0.6" @@ -15017,6 +15032,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:0.1.0": + version: 0.1.0 + resolution: "cookie@npm:0.1.0" + checksum: f2454ed4da2566be7278812288c18d7eb1416c01c1454f5b8fc6095cc38eeba562c244c2540724100ce89b8be043323bd64bcbe3d0b73cb426381bc0552d84c1 + languageName: node + linkType: hard + "cookie@npm:0.5.0, cookie@npm:^0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" @@ -15856,6 +15878,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:*, debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.2.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": + version: 4.3.4 + resolution: "debug@npm:4.3.4" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 + languageName: node + linkType: hard + "debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -15865,15 +15899,10 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.2.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": - version: 4.3.4 - resolution: "debug@npm:4.3.4" - dependencies: - ms: 2.1.2 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 +"debug@npm:>= 0.7.3 < 1": + version: 0.8.1 + resolution: "debug@npm:0.8.1" + checksum: 13dff5babbf72c5374395e27f0869d10adf9e8076400ea4bdfa6b58a89552718c936d6683fd8f4e26c7d5da75b7ab8e4dd86ad9102d75b7a0d03e48eb230702f languageName: node linkType: hard @@ -17207,6 +17236,13 @@ __metadata: languageName: node linkType: hard +"escape-html@npm:1.0.1": + version: 1.0.1 + resolution: "escape-html@npm:1.0.1" + checksum: 5e8de9fb50e5739f7e09ebcfbc7effc04bf9776e49c1987085121144e7b11bbe7d9ff16bb2771ccb8bea79883ac07a66bedcb39698c469972b857ff4489d4cc6 + languageName: node + linkType: hard + "escape-html@npm:~1.0.3": version: 1.0.3 resolution: "escape-html@npm:1.0.3" @@ -17968,15 +18004,28 @@ __metadata: languageName: node linkType: hard -"express@npm:^1.0.0": - version: 1.0.8 - resolution: "express@npm:1.0.8" - dependencies: - connect: ">= 0.5.0 < 1.0.0" - qs: ">= 0.0.5" - bin: - express: ./bin/express - checksum: 419e150c47a019d262bad79d9ea81ee3a339a2711fe51f6bfd579ba9b6042b088e364625a4f83a38d5dd4093734a154dfbcfebe78803baf4f96f6f518a6cc383 +"express@npm:4.0.0": + version: 4.0.0 + resolution: "express@npm:4.0.0" + dependencies: + accepts: 1.0.0 + buffer-crc32: 0.2.1 + cookie: 0.1.0 + cookie-signature: 1.0.3 + debug: ">= 0.7.3 < 1" + escape-html: 1.0.1 + fresh: 0.2.2 + merge-descriptors: 0.0.2 + methods: 0.1.0 + parseurl: 1.0.1 + path-to-regexp: 0.1.2 + qs: 0.6.6 + range-parser: 1.0.0 + send: 0.2.0 + serve-static: 1.0.1 + type-is: 1.0.0 + utils-merge: 1.0.0 + checksum: 5fc62f42a6f17e50636180819866d3d6ea3561155d73b74a605b2530bb45590e2960cee7837ba3d87c839d17cfcd52c79282b812d202fa67b978e661609d36a4 languageName: node linkType: hard @@ -18672,6 +18721,20 @@ __metadata: languageName: node linkType: hard +"fresh@npm:0.2.0": + version: 0.2.0 + resolution: "fresh@npm:0.2.0" + checksum: 7cc6e5db63a1321e353b3690ec839ecdf75b875ce6e6d60218c36eee546db7ee28e3e51d815d1f7caef5c3f8afdb24d2af6e294602c3a55fb1134f0fde6f19d0 + languageName: node + linkType: hard + +"fresh@npm:0.2.2": + version: 0.2.2 + resolution: "fresh@npm:0.2.2" + checksum: cdaecda3b64ccf95d8a62331e628f02ba76013c0d0c7e62e468e7cec13c98787307aa39f7745182abffed9f258d22208df2774bf0360924f744d0e0a8dbd5e70 + languageName: node + linkType: hard + "fresh@npm:0.5.2, fresh@npm:^0.5.2": version: 0.5.2 resolution: "fresh@npm:0.5.2" @@ -18679,6 +18742,13 @@ __metadata: languageName: node linkType: hard +"fresh@npm:~0.2.1": + version: 0.2.4 + resolution: "fresh@npm:0.2.4" + checksum: 83ce8b2519c9166f54827d3a8bd9d80195faebc770b82f42e8bc9752a379d258b33ab1c2a944ae53199b03150b537d0f480d5b92034ce2fd5b84a085a64ab00c + languageName: node + linkType: hard + "from2@npm:^2.1.0": version: 2.3.0 resolution: "from2@npm:2.3.0" @@ -23119,6 +23189,13 @@ __metadata: languageName: node linkType: hard +"merge-descriptors@npm:0.0.2": + version: 0.0.2 + resolution: "merge-descriptors@npm:0.0.2" + checksum: fed099e06b06b8617e64d5f74154000c9b5e3569adb583419e50bcea946da0fe3b8e3bdf7d32174084007eaec17f849d7c5f2bdd036852ec22cd25a40820720f + languageName: node + linkType: hard + "merge-descriptors@npm:1.0.1": version: 1.0.1 resolution: "merge-descriptors@npm:1.0.1" @@ -23149,6 +23226,13 @@ __metadata: languageName: node linkType: hard +"methods@npm:0.1.0": + version: 0.1.0 + resolution: "methods@npm:0.1.0" + checksum: 0d2a8d95774f0cff1693cec7ccbf63cd95d07a57f3cbe22fe8e9b3803d77887e832d877a23118b91e7ef7991096ba5fd49fc883448f8bf406f0d5bca322f7fa8 + languageName: node + linkType: hard + "methods@npm:~1.1.2": version: 1.1.2 resolution: "methods@npm:1.1.2" @@ -23807,6 +23891,13 @@ __metadata: languageName: node linkType: hard +"mime@npm:~1.2.11, mime@npm:~1.2.9": + version: 1.2.11 + resolution: "mime@npm:1.2.11" + checksum: 68248e74bd65db30d2b59c077cc8794e3caee1497fd7ca40efcf8797c4aea75dc9d5b05d1448fdfb3de09da0b894a47e90cde0985a0c5ec5d7d2305c36aa2c75 + languageName: node + linkType: hard + "mime@npm:~2.5.2": version: 2.5.2 resolution: "mime@npm:2.5.2" @@ -24313,6 +24404,13 @@ __metadata: languageName: node linkType: hard +"negotiator@npm:~0.3.0": + version: 0.3.0 + resolution: "negotiator@npm:0.3.0" + checksum: fde568478562dbac76cb3137c7c9932ddd2c369a545e8ef1e167dc6ce279a61ca41764e513dcf023d0ebff24aebcaa42a7bdb06dde2a4f6541e291cda27378bc + languageName: node + linkType: hard + "neo-async@npm:^2.5.0, neo-async@npm:^2.6.0, neo-async@npm:^2.6.1, neo-async@npm:^2.6.2": version: 2.6.2 resolution: "neo-async@npm:2.6.2" @@ -25718,6 +25816,13 @@ __metadata: languageName: node linkType: hard +"parseurl@npm:1.0.1": + version: 1.0.1 + resolution: "parseurl@npm:1.0.1" + checksum: 136945d0195ee568013e79f9a0fbc5526565bc30aeb4c827489e975e90a146282b87deb1eb3e17786778f9b6799673a869df5ef81f44e0c4d5685a8796194473 + languageName: node + linkType: hard + "parseurl@npm:~1.3.2, parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" @@ -25842,6 +25947,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:0.1.2": + version: 0.1.2 + resolution: "path-to-regexp@npm:0.1.2" + checksum: b913addeb6f3fb1aa5c65f4162ea60e328bd7c5051cd8d3258603be02783157fc5a87e245a68d34eb699728cfc03a64262c901dd094a792d5e2fa943bb2cd52b + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.7": version: 0.1.7 resolution: "path-to-regexp@npm:0.1.7" @@ -27971,6 +28083,13 @@ __metadata: languageName: node linkType: hard +"qs@npm:0.6.6": + version: 0.6.6 + resolution: "qs@npm:0.6.6" + checksum: e332a5520d95a6f3cdbe2eac592535a7e7629fe5dfc431db6672c165407d3fbb606e97d2e72357e60f9dfa2372c9e3eb17732442323d6593a5fc3094756746f9 + languageName: node + linkType: hard + "qs@npm:6.11.0": version: 6.11.0 resolution: "qs@npm:6.11.0" @@ -27980,7 +28099,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:>= 0.0.5, qs@npm:>= 0.0.6, qs@npm:^6.10.0, qs@npm:^6.11.0": +"qs@npm:^6.10.0, qs@npm:^6.11.0": version: 6.11.2 resolution: "qs@npm:6.11.2" dependencies: @@ -28102,6 +28221,20 @@ __metadata: languageName: node linkType: hard +"range-parser@npm:0.0.4": + version: 0.0.4 + resolution: "range-parser@npm:0.0.4" + checksum: 89dc7c4f55c58e9a1a0d4944ff41f5d9bd43ff79793f7980288880cfb43d5dfdae08066e5923704639e1e9f75487595492f3ee0e2df1c7c82834e5863201dedf + languageName: node + linkType: hard + +"range-parser@npm:1.0.0": + version: 1.0.0 + resolution: "range-parser@npm:1.0.0" + checksum: 3e796359ddd725a77fb814342d7ff97cb3b49488b5ecd83445e12e7c0a9d5b087eb5265fd358703becdef02ee22acdc71093694f14249ce7503171cd8940ae44 + languageName: node + linkType: hard + "range-parser@npm:^1.2.1, range-parser@npm:~1.2.1": version: 1.2.1 resolution: "range-parser@npm:1.2.1" @@ -28109,6 +28242,13 @@ __metadata: languageName: node linkType: hard +"range-parser@npm:~1.0.0": + version: 1.0.3 + resolution: "range-parser@npm:1.0.3" + checksum: 35c11f79ab0bb8b64e414c2efe794ff23c6f7b0c5bd49e65e4adeeaf714f14224a636044a40f656c4db6384b3218574d5340630911124a96c4a06e4e2199933a + languageName: node + linkType: hard + "raw-body@npm:2.5.1": version: 2.5.1 resolution: "raw-body@npm:2.5.1" @@ -29893,6 +30033,18 @@ __metadata: languageName: node linkType: hard +"send@npm:0.1.4": + version: 0.1.4 + resolution: "send@npm:0.1.4" + dependencies: + debug: "*" + fresh: 0.2.0 + mime: ~1.2.9 + range-parser: 0.0.4 + checksum: d65d46efbe21f91fe7a3630addd7eeca948ed33804d42aa2aebe0313494e1bb1b260b534a12aec174c2e9f50a9d4ea18b78708af940493e297ae3d61f3791bed + languageName: node + linkType: hard + "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" @@ -29914,6 +30066,18 @@ __metadata: languageName: node linkType: hard +"send@npm:0.2.0": + version: 0.2.0 + resolution: "send@npm:0.2.0" + dependencies: + debug: "*" + fresh: ~0.2.1 + mime: ~1.2.9 + range-parser: ~1.0.0 + checksum: 49cb2bf38f8fa2761c822769ed63f3600b222515621da919f8c3b8b88a3c59e4298d418fb66fc6a890e3fa98592fbca6ec113ab81182ebaf65a6fb4480b1793c + languageName: node + linkType: hard + "sentence-case@npm:^3.0.4": version: 3.0.4 resolution: "sentence-case@npm:3.0.4" @@ -29974,6 +30138,15 @@ __metadata: languageName: node linkType: hard +"serve-static@npm:1.0.1": + version: 1.0.1 + resolution: "serve-static@npm:1.0.1" + dependencies: + send: 0.1.4 + checksum: 081b193a03185a8721b6a91e61654c8cea93c5d489a1ba4648bf68a2a776bedd79a6e16d3164e3a58aa075242cda9ed5054f9cf5dffe05fe9fb2b8d3e834f4b0 + languageName: node + linkType: hard + "serve-static@npm:1.15.0, serve-static@npm:^1.14.1, serve-static@npm:^1.15.0": version: 1.15.0 resolution: "serve-static@npm:1.15.0" @@ -32325,6 +32498,15 @@ __metadata: languageName: node linkType: hard +"type-is@npm:1.0.0": + version: 1.0.0 + resolution: "type-is@npm:1.0.0" + dependencies: + mime: ~1.2.11 + checksum: 751089878302e2be824104e6ea1f4092b8b3977958ce51bb7d2016c4b66db0bf651b4dfed37334c2ba900472ab2b1469bca9cdbb4b6465bc4296b7523582aeb8 + languageName: node + linkType: hard + "type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -33130,6 +33312,13 @@ __metadata: languageName: node linkType: hard +"utils-merge@npm:1.0.0": + version: 1.0.0 + resolution: "utils-merge@npm:1.0.0" + checksum: 6b7149df35a99bf9894d7320bcc907e190ad296368ac85f252c227782651d9ffa5bd2d6a2e2ab98b7f023f18bc16a3b31f0ee5683e02e1cbcd5c886f6c7e3c1c + languageName: node + linkType: hard + "utils-merge@npm:1.0.1": version: 1.0.1 resolution: "utils-merge@npm:1.0.1" From 20f115dff3004edb98c246b62779bf6b160b148f Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 18 Jan 2024 13:00:41 -1000 Subject: [PATCH 05/17] chore: fix `express` version --- packages/manager/package.json | 2 +- yarn.lock | 232 ++-------------------------------- 2 files changed, 12 insertions(+), 222 deletions(-) diff --git a/packages/manager/package.json b/packages/manager/package.json index 7d764a8035..fa4e2386b9 100644 --- a/packages/manager/package.json +++ b/packages/manager/package.json @@ -110,7 +110,7 @@ "eslint-config-prettier": "9.0.0", "eslint-plugin-prettier": "5.0.1", "eslint-plugin-tsdoc": "0.2.17", - "express": "4.0.0", + "express": "4.18.2", "hook-std": "3.0.0", "memfs": "3.4.13", "msw": "1.1.0", diff --git a/yarn.lock b/yarn.lock index cdb37e2798..cfb5104bb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8421,7 +8421,7 @@ __metadata: eslint-plugin-prettier: 5.0.1 eslint-plugin-tsdoc: 0.2.17 execa: ^7.1.1 - express: 4.0.0 + express: 4.18.2 file-type: ^18.2.1 fp-ts: ^2.13.1 get-port: ^6.1.2 @@ -12319,16 +12319,6 @@ __metadata: languageName: node linkType: hard -"accepts@npm:1.0.0": - version: 1.0.0 - resolution: "accepts@npm:1.0.0" - dependencies: - mime: ~1.2.11 - negotiator: ~0.3.0 - checksum: 7689e77f56e4eda9cbd9a2278ef12f2f1d4519112dacdfa80d8c6b22aeb003f5bad7b559dce1c3d92c6111c8f548bbc6c17e47063c6be6bfb1bcee584474ba11 - languageName: node - linkType: hard - "accepts@npm:~1.3.5, accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" @@ -13691,13 +13681,6 @@ __metadata: languageName: node linkType: hard -"buffer-crc32@npm:0.2.1": - version: 0.2.1 - resolution: "buffer-crc32@npm:0.2.1" - checksum: eec890decc253b04ad5d032c0644e6d6cf24606c5a8fd097b8f7b2ac3d9468685d3cc3e041625c59b30c67280fcbaba6a7c916df0cf6c6efbc87d21a2f69edae - languageName: node - linkType: hard - "buffer-crc32@npm:^0.2.1, buffer-crc32@npm:^0.2.13, buffer-crc32@npm:^0.2.5, buffer-crc32@npm:~0.2.3": version: 0.2.13 resolution: "buffer-crc32@npm:0.2.13" @@ -15018,13 +15001,6 @@ __metadata: languageName: node linkType: hard -"cookie-signature@npm:1.0.3": - version: 1.0.3 - resolution: "cookie-signature@npm:1.0.3" - checksum: 8dc7824ed4368b0c85f13ea392f844ab8875e433dfba8d2542a2f1282b21847cd05ed893f23b3e2ff1eea270975efd7abdfad9956bf09e896d942d5c8d85648a - languageName: node - linkType: hard - "cookie-signature@npm:1.0.6": version: 1.0.6 resolution: "cookie-signature@npm:1.0.6" @@ -15032,13 +15008,6 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.1.0": - version: 0.1.0 - resolution: "cookie@npm:0.1.0" - checksum: f2454ed4da2566be7278812288c18d7eb1416c01c1454f5b8fc6095cc38eeba562c244c2540724100ce89b8be043323bd64bcbe3d0b73cb426381bc0552d84c1 - languageName: node - linkType: hard - "cookie@npm:0.5.0, cookie@npm:^0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" @@ -15878,18 +15847,6 @@ __metadata: languageName: node linkType: hard -"debug@npm:*, debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.2.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": - version: 4.3.4 - resolution: "debug@npm:4.3.4" - dependencies: - ms: 2.1.2 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 - languageName: node - linkType: hard - "debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -15899,10 +15856,15 @@ __metadata: languageName: node linkType: hard -"debug@npm:>= 0.7.3 < 1": - version: 0.8.1 - resolution: "debug@npm:0.8.1" - checksum: 13dff5babbf72c5374395e27f0869d10adf9e8076400ea4bdfa6b58a89552718c936d6683fd8f4e26c7d5da75b7ab8e4dd86ad9102d75b7a0d03e48eb230702f +"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.2.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": + version: 4.3.4 + resolution: "debug@npm:4.3.4" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 languageName: node linkType: hard @@ -17236,13 +17198,6 @@ __metadata: languageName: node linkType: hard -"escape-html@npm:1.0.1": - version: 1.0.1 - resolution: "escape-html@npm:1.0.1" - checksum: 5e8de9fb50e5739f7e09ebcfbc7effc04bf9776e49c1987085121144e7b11bbe7d9ff16bb2771ccb8bea79883ac07a66bedcb39698c469972b857ff4489d4cc6 - languageName: node - linkType: hard - "escape-html@npm:~1.0.3": version: 1.0.3 resolution: "escape-html@npm:1.0.3" @@ -18004,32 +17959,7 @@ __metadata: languageName: node linkType: hard -"express@npm:4.0.0": - version: 4.0.0 - resolution: "express@npm:4.0.0" - dependencies: - accepts: 1.0.0 - buffer-crc32: 0.2.1 - cookie: 0.1.0 - cookie-signature: 1.0.3 - debug: ">= 0.7.3 < 1" - escape-html: 1.0.1 - fresh: 0.2.2 - merge-descriptors: 0.0.2 - methods: 0.1.0 - parseurl: 1.0.1 - path-to-regexp: 0.1.2 - qs: 0.6.6 - range-parser: 1.0.0 - send: 0.2.0 - serve-static: 1.0.1 - type-is: 1.0.0 - utils-merge: 1.0.0 - checksum: 5fc62f42a6f17e50636180819866d3d6ea3561155d73b74a605b2530bb45590e2960cee7837ba3d87c839d17cfcd52c79282b812d202fa67b978e661609d36a4 - languageName: node - linkType: hard - -"express@npm:^4.17.3, express@npm:^4.18.2": +"express@npm:4.18.2, express@npm:^4.17.3, express@npm:^4.18.2": version: 4.18.2 resolution: "express@npm:4.18.2" dependencies: @@ -18721,20 +18651,6 @@ __metadata: languageName: node linkType: hard -"fresh@npm:0.2.0": - version: 0.2.0 - resolution: "fresh@npm:0.2.0" - checksum: 7cc6e5db63a1321e353b3690ec839ecdf75b875ce6e6d60218c36eee546db7ee28e3e51d815d1f7caef5c3f8afdb24d2af6e294602c3a55fb1134f0fde6f19d0 - languageName: node - linkType: hard - -"fresh@npm:0.2.2": - version: 0.2.2 - resolution: "fresh@npm:0.2.2" - checksum: cdaecda3b64ccf95d8a62331e628f02ba76013c0d0c7e62e468e7cec13c98787307aa39f7745182abffed9f258d22208df2774bf0360924f744d0e0a8dbd5e70 - languageName: node - linkType: hard - "fresh@npm:0.5.2, fresh@npm:^0.5.2": version: 0.5.2 resolution: "fresh@npm:0.5.2" @@ -18742,13 +18658,6 @@ __metadata: languageName: node linkType: hard -"fresh@npm:~0.2.1": - version: 0.2.4 - resolution: "fresh@npm:0.2.4" - checksum: 83ce8b2519c9166f54827d3a8bd9d80195faebc770b82f42e8bc9752a379d258b33ab1c2a944ae53199b03150b537d0f480d5b92034ce2fd5b84a085a64ab00c - languageName: node - linkType: hard - "from2@npm:^2.1.0": version: 2.3.0 resolution: "from2@npm:2.3.0" @@ -23189,13 +23098,6 @@ __metadata: languageName: node linkType: hard -"merge-descriptors@npm:0.0.2": - version: 0.0.2 - resolution: "merge-descriptors@npm:0.0.2" - checksum: fed099e06b06b8617e64d5f74154000c9b5e3569adb583419e50bcea946da0fe3b8e3bdf7d32174084007eaec17f849d7c5f2bdd036852ec22cd25a40820720f - languageName: node - linkType: hard - "merge-descriptors@npm:1.0.1": version: 1.0.1 resolution: "merge-descriptors@npm:1.0.1" @@ -23226,13 +23128,6 @@ __metadata: languageName: node linkType: hard -"methods@npm:0.1.0": - version: 0.1.0 - resolution: "methods@npm:0.1.0" - checksum: 0d2a8d95774f0cff1693cec7ccbf63cd95d07a57f3cbe22fe8e9b3803d77887e832d877a23118b91e7ef7991096ba5fd49fc883448f8bf406f0d5bca322f7fa8 - languageName: node - linkType: hard - "methods@npm:~1.1.2": version: 1.1.2 resolution: "methods@npm:1.1.2" @@ -23891,13 +23786,6 @@ __metadata: languageName: node linkType: hard -"mime@npm:~1.2.11, mime@npm:~1.2.9": - version: 1.2.11 - resolution: "mime@npm:1.2.11" - checksum: 68248e74bd65db30d2b59c077cc8794e3caee1497fd7ca40efcf8797c4aea75dc9d5b05d1448fdfb3de09da0b894a47e90cde0985a0c5ec5d7d2305c36aa2c75 - languageName: node - linkType: hard - "mime@npm:~2.5.2": version: 2.5.2 resolution: "mime@npm:2.5.2" @@ -24404,13 +24292,6 @@ __metadata: languageName: node linkType: hard -"negotiator@npm:~0.3.0": - version: 0.3.0 - resolution: "negotiator@npm:0.3.0" - checksum: fde568478562dbac76cb3137c7c9932ddd2c369a545e8ef1e167dc6ce279a61ca41764e513dcf023d0ebff24aebcaa42a7bdb06dde2a4f6541e291cda27378bc - languageName: node - linkType: hard - "neo-async@npm:^2.5.0, neo-async@npm:^2.6.0, neo-async@npm:^2.6.1, neo-async@npm:^2.6.2": version: 2.6.2 resolution: "neo-async@npm:2.6.2" @@ -25816,13 +25697,6 @@ __metadata: languageName: node linkType: hard -"parseurl@npm:1.0.1": - version: 1.0.1 - resolution: "parseurl@npm:1.0.1" - checksum: 136945d0195ee568013e79f9a0fbc5526565bc30aeb4c827489e975e90a146282b87deb1eb3e17786778f9b6799673a869df5ef81f44e0c4d5685a8796194473 - languageName: node - linkType: hard - "parseurl@npm:~1.3.2, parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" @@ -25947,13 +25821,6 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:0.1.2": - version: 0.1.2 - resolution: "path-to-regexp@npm:0.1.2" - checksum: b913addeb6f3fb1aa5c65f4162ea60e328bd7c5051cd8d3258603be02783157fc5a87e245a68d34eb699728cfc03a64262c901dd094a792d5e2fa943bb2cd52b - languageName: node - linkType: hard - "path-to-regexp@npm:0.1.7": version: 0.1.7 resolution: "path-to-regexp@npm:0.1.7" @@ -28083,13 +27950,6 @@ __metadata: languageName: node linkType: hard -"qs@npm:0.6.6": - version: 0.6.6 - resolution: "qs@npm:0.6.6" - checksum: e332a5520d95a6f3cdbe2eac592535a7e7629fe5dfc431db6672c165407d3fbb606e97d2e72357e60f9dfa2372c9e3eb17732442323d6593a5fc3094756746f9 - languageName: node - linkType: hard - "qs@npm:6.11.0": version: 6.11.0 resolution: "qs@npm:6.11.0" @@ -28221,20 +28081,6 @@ __metadata: languageName: node linkType: hard -"range-parser@npm:0.0.4": - version: 0.0.4 - resolution: "range-parser@npm:0.0.4" - checksum: 89dc7c4f55c58e9a1a0d4944ff41f5d9bd43ff79793f7980288880cfb43d5dfdae08066e5923704639e1e9f75487595492f3ee0e2df1c7c82834e5863201dedf - languageName: node - linkType: hard - -"range-parser@npm:1.0.0": - version: 1.0.0 - resolution: "range-parser@npm:1.0.0" - checksum: 3e796359ddd725a77fb814342d7ff97cb3b49488b5ecd83445e12e7c0a9d5b087eb5265fd358703becdef02ee22acdc71093694f14249ce7503171cd8940ae44 - languageName: node - linkType: hard - "range-parser@npm:^1.2.1, range-parser@npm:~1.2.1": version: 1.2.1 resolution: "range-parser@npm:1.2.1" @@ -28242,13 +28088,6 @@ __metadata: languageName: node linkType: hard -"range-parser@npm:~1.0.0": - version: 1.0.3 - resolution: "range-parser@npm:1.0.3" - checksum: 35c11f79ab0bb8b64e414c2efe794ff23c6f7b0c5bd49e65e4adeeaf714f14224a636044a40f656c4db6384b3218574d5340630911124a96c4a06e4e2199933a - languageName: node - linkType: hard - "raw-body@npm:2.5.1": version: 2.5.1 resolution: "raw-body@npm:2.5.1" @@ -30033,18 +29872,6 @@ __metadata: languageName: node linkType: hard -"send@npm:0.1.4": - version: 0.1.4 - resolution: "send@npm:0.1.4" - dependencies: - debug: "*" - fresh: 0.2.0 - mime: ~1.2.9 - range-parser: 0.0.4 - checksum: d65d46efbe21f91fe7a3630addd7eeca948ed33804d42aa2aebe0313494e1bb1b260b534a12aec174c2e9f50a9d4ea18b78708af940493e297ae3d61f3791bed - languageName: node - linkType: hard - "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" @@ -30066,18 +29893,6 @@ __metadata: languageName: node linkType: hard -"send@npm:0.2.0": - version: 0.2.0 - resolution: "send@npm:0.2.0" - dependencies: - debug: "*" - fresh: ~0.2.1 - mime: ~1.2.9 - range-parser: ~1.0.0 - checksum: 49cb2bf38f8fa2761c822769ed63f3600b222515621da919f8c3b8b88a3c59e4298d418fb66fc6a890e3fa98592fbca6ec113ab81182ebaf65a6fb4480b1793c - languageName: node - linkType: hard - "sentence-case@npm:^3.0.4": version: 3.0.4 resolution: "sentence-case@npm:3.0.4" @@ -30138,15 +29953,6 @@ __metadata: languageName: node linkType: hard -"serve-static@npm:1.0.1": - version: 1.0.1 - resolution: "serve-static@npm:1.0.1" - dependencies: - send: 0.1.4 - checksum: 081b193a03185a8721b6a91e61654c8cea93c5d489a1ba4648bf68a2a776bedd79a6e16d3164e3a58aa075242cda9ed5054f9cf5dffe05fe9fb2b8d3e834f4b0 - languageName: node - linkType: hard - "serve-static@npm:1.15.0, serve-static@npm:^1.14.1, serve-static@npm:^1.15.0": version: 1.15.0 resolution: "serve-static@npm:1.15.0" @@ -32498,15 +32304,6 @@ __metadata: languageName: node linkType: hard -"type-is@npm:1.0.0": - version: 1.0.0 - resolution: "type-is@npm:1.0.0" - dependencies: - mime: ~1.2.11 - checksum: 751089878302e2be824104e6ea1f4092b8b3977958ce51bb7d2016c4b66db0bf651b4dfed37334c2ba900472ab2b1469bca9cdbb4b6465bc4296b7523582aeb8 - languageName: node - linkType: hard - "type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -33312,13 +33109,6 @@ __metadata: languageName: node linkType: hard -"utils-merge@npm:1.0.0": - version: 1.0.0 - resolution: "utils-merge@npm:1.0.0" - checksum: 6b7149df35a99bf9894d7320bcc907e190ad296368ac85f252c227782651d9ffa5bd2d6a2e2ab98b7f023f18bc16a3b31f0ee5683e02e1cbcd5c886f6c7e3c1c - languageName: node - linkType: hard - "utils-merge@npm:1.0.1": version: 1.0.1 resolution: "utils-merge@npm:1.0.1" From b07b584d358cf739d62fb3b357e3b4f91fdcb4e4 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 18 Jan 2024 13:03:42 -1000 Subject: [PATCH 06/17] refactor: remove unnecessary edits --- .../src/managers/SliceMachineManager.ts | 51 +++++++++++-------- .../createSliceMachineManagerMiddleware.ts | 10 ++-- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/packages/manager/src/managers/SliceMachineManager.ts b/packages/manager/src/managers/SliceMachineManager.ts index 5e7ebdc535..491d0cb1bf 100644 --- a/packages/manager/src/managers/SliceMachineManager.ts +++ b/packages/manager/src/managers/SliceMachineManager.ts @@ -10,7 +10,6 @@ import { } from "@slicemachine/plugin-kit"; import { createContentDigest } from "../lib/createContentDigest"; -import { buildPrismicRepositoryAPIEndpoint } from "../lib/buildPrismicRepositoryAPIEndpoint"; import { PackageManager, SliceMachineConfig } from "../types"; import { @@ -21,21 +20,26 @@ import { createPrismicAuthManager } from "../auth/createPrismicAuthManager"; import { API_ENDPOINTS, APIEndpoints } from "../constants/API_ENDPOINTS"; -import { CustomTypesManager } from "./customTypes/CustomTypesManager"; -import { DocumentationManager } from "./documentation/DocumentationManager"; -import { GitManager } from "./git/GitManager"; -import { PluginsManager } from "./plugins/PluginsManager"; +import { UserManager } from "./user/UserManager"; import { PrismicRepositoryManager } from "./prismicRepository/PrismicRepositoryManager"; + +import { PluginsManager } from "./plugins/PluginsManager"; + import { ProjectManager } from "./project/ProjectManager"; -import { ScreenshotsManager } from "./screenshots/ScreenshotsManager"; -import { SimulatorManager } from "./simulator/SimulatorManager"; -import { SliceTemplateLibraryManager } from "./sliceTemplateLibrary/SliceTemplateLibraryManager"; +import { CustomTypesManager } from "./customTypes/CustomTypesManager"; import { SlicesManager } from "./slices/SlicesManager"; import { SnippetsManager } from "./snippets/SnippetsManager"; -import { TelemetryManager } from "./telemetry/TelemetryManager"; -import { UserManager } from "./user/UserManager"; +import { ScreenshotsManager } from "./screenshots/ScreenshotsManager"; +import { SimulatorManager } from "./simulator/SimulatorManager"; + import { VersionsManager } from "./versions/VersionsManager"; +import { TelemetryManager } from "./telemetry/TelemetryManager"; +import { buildPrismicRepositoryAPIEndpoint } from "../lib/buildPrismicRepositoryAPIEndpoint"; +import { DocumentationManager } from "./documentation/DocumentationManager"; +import { SliceTemplateLibraryManager } from "./sliceTemplateLibrary/SliceTemplateLibraryManager"; +import { GitManager } from "./git/GitManager"; + type SliceMachineManagerGetStateReturnType = { env: { shortId?: string; @@ -101,19 +105,19 @@ export class SliceMachineManager { cwd: string; customTypes: CustomTypesManager; - documentation: DocumentationManager; - git: GitManager; plugins: PluginsManager; prismicRepository: PrismicRepositoryManager; project: ProjectManager; screenshots: ScreenshotsManager; simulator: SimulatorManager; - sliceTemplateLibrary: SliceTemplateLibraryManager; slices: SlicesManager; snippets: SnippetsManager; + documentation: DocumentationManager; + sliceTemplateLibrary: SliceTemplateLibraryManager; telemetry: TelemetryManager; user: UserManager; versions: VersionsManager; + git: GitManager; constructor(args?: SliceMachineManagerConstructorArgs) { // _prismicAuthManager must be set at least before UserManager @@ -121,23 +125,28 @@ export class SliceMachineManager { // authentication-related methods. this._prismicAuthManager = createPrismicAuthManager(); - this.customTypes = new CustomTypesManager(this); - this.documentation = new DocumentationManager(this); - this.git = new GitManager(this); + this.user = new UserManager(this); + this.prismicRepository = new PrismicRepositoryManager(this); + this.plugins = new PluginsManager(this, { nativePlugins: args?.nativePlugins, }); - this.prismicRepository = new PrismicRepositoryManager(this); + this.project = new ProjectManager(this); + this.customTypes = new CustomTypesManager(this); + this.slices = new SlicesManager(this); + this.snippets = new SnippetsManager(this); this.screenshots = new ScreenshotsManager(this); this.simulator = new SimulatorManager(this); + this.documentation = new DocumentationManager(this); this.sliceTemplateLibrary = new SliceTemplateLibraryManager(this); - this.slices = new SlicesManager(this); - this.snippets = new SnippetsManager(this); - this.telemetry = new TelemetryManager(this); - this.user = new UserManager(this); + this.versions = new VersionsManager(this); + this.telemetry = new TelemetryManager(this); + + this.git = new GitManager(this); + this.cwd = args?.cwd ?? process.cwd(); } diff --git a/packages/manager/src/managers/createSliceMachineManagerMiddleware.ts b/packages/manager/src/managers/createSliceMachineManagerMiddleware.ts index 85f55094d7..d1fa6020c2 100644 --- a/packages/manager/src/managers/createSliceMachineManagerMiddleware.ts +++ b/packages/manager/src/managers/createSliceMachineManagerMiddleware.ts @@ -24,23 +24,23 @@ const defineOmits = () => { const omitProcedures = defineOmits()([ "_sliceMachinePluginRunner", "_prismicAuthManager", - "getSliceMachinePluginRunner", - "getPrismicAuthManager", "customTypes._sliceMachineManager", - "documentation._sliceMachineManager", - "git._sliceMachineManager", "plugins._sliceMachineManager", "prismicRepository._sliceMachineManager", "project._sliceMachineManager", "screenshots._sliceMachineManager", "simulator._sliceMachineManager", - "sliceTemplateLibrary._sliceMachineManager", "slices._sliceMachineManager", "snippets._sliceMachineManager", "telemetry._sliceMachineManager", "user._sliceMachineManager", "versions._sliceMachineManager", + "documentation._sliceMachineManager", + "sliceTemplateLibrary._sliceMachineManager", + "git._sliceMachineManager", + "getSliceMachinePluginRunner", + "getPrismicAuthManager", ]); export type SliceMachineManagerMiddleware = RPCMiddleware< From 7bd88860ae39c31ca67c07da3187df8b06d8d171 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 18 Jan 2024 13:04:05 -1000 Subject: [PATCH 07/17] test: remove unnecessary code --- .../test/__testutils__/mockSliceMachineAPI.ts | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/packages/manager/test/__testutils__/mockSliceMachineAPI.ts b/packages/manager/test/__testutils__/mockSliceMachineAPI.ts index 8aae64c131..d5e3cb5856 100644 --- a/packages/manager/test/__testutils__/mockSliceMachineAPI.ts +++ b/packages/manager/test/__testutils__/mockSliceMachineAPI.ts @@ -11,12 +11,6 @@ type MockSliceMachineAPIConfig = { expectedCookies: string[]; environments: Environment[]; }; - gitGitHubCreateAuthStateV1Endpoint?: { - isSuccessful?: boolean; - expectedAuthenticationToken: string; - key: string; - expiresAt: Date; - }; }; export const mockSliceMachineAPI = ( @@ -56,36 +50,4 @@ export const mockSliceMachineAPI = ( ), ); } - - if (config.gitGitHubCreateAuthStateV1Endpoint) { - ctx.msw.use( - rest.get( - new URL(`./git/github/create-auth-state`, endpoint).toString(), - (req, res, ctx) => { - if ( - config.gitGitHubCreateAuthStateV1Endpoint && - (config.gitGitHubCreateAuthStateV1Endpoint.isSuccessful ?? true) - ) { - if ( - req.headers.get("Authorization") === - `Bearer ${config.gitGitHubCreateAuthStateV1Endpoint.expectedAuthenticationToken}` - ) { - return res( - ctx.json({ - key: config.gitGitHubCreateAuthStateV1Endpoint.key, - expiresAt: - config.gitGitHubCreateAuthStateV1Endpoint.expiresAt, - }), - ctx.status(200), - ); - } else { - return res(ctx.status(418)); - } - } else { - return res(ctx.status(418)); - } - }, - ), - ); - } }; From 1dbaa48344ce4f861fa967411d65dc9c3707ac07 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 18 Jan 2024 13:06:46 -1000 Subject: [PATCH 08/17] test: remove unnecessary `logout` --- packages/manager/test/SliceMachineManager-user-login.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/manager/test/SliceMachineManager-user-login.test.ts b/packages/manager/test/SliceMachineManager-user-login.test.ts index 932237d03f..d50b3e939b 100644 --- a/packages/manager/test/SliceMachineManager-user-login.test.ts +++ b/packages/manager/test/SliceMachineManager-user-login.test.ts @@ -42,7 +42,6 @@ it("retains existing cookies in the auth state file", async (ctx) => { mockPrismicUserAPI(ctx); - await manager.user.logout(); await manager.user.login({ email: "name@example.com", cookies: ["foo=bar"], From 532cf076476578fc809662f96e0af9b9ca542517 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 18 Jan 2024 14:53:01 -1000 Subject: [PATCH 09/17] feat: add placeholder settings page --- .../manager/src/managers/git/GitManager.ts | 2 +- ...eManager-git-createGitHubAuthState.test.ts | 3 +- .../components/Navigation/index.tsx | 34 ++++++---- packages/slice-machine/pages/settings.tsx | 64 +++++++++++++++++++ 4 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 packages/slice-machine/pages/settings.tsx diff --git a/packages/manager/src/managers/git/GitManager.ts b/packages/manager/src/managers/git/GitManager.ts index e49ae15f5e..dfa81bae06 100644 --- a/packages/manager/src/managers/git/GitManager.ts +++ b/packages/manager/src/managers/git/GitManager.ts @@ -64,7 +64,7 @@ export class GitManager extends BaseManager { "./git/github/create-auth-state", API_ENDPOINTS.SliceMachineV1, ); - const res = await this.#fetch(url); + const res = await this.#fetch(url, { method: "POST" }); if (!res.ok) { switch (res.status) { diff --git a/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts b/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts index cc6d7d2d0e..3cf1fdd7bb 100644 --- a/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts +++ b/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts @@ -9,7 +9,7 @@ it("returns a GitHub auth state token", async ({ manager, api, login }) => { api.mockSliceMachineV1( "./git/github/create-auth-state", { key, expiresAt: expiresAt.toISOString() }, - { checkAuthentication: true }, + { method: "post", checkAuthentication: true }, ); await login(); @@ -24,6 +24,7 @@ it("throws UnauthorizedError if the API returns 401", async ({ login, }) => { api.mockSliceMachineV1("./git/github/create-auth-state", undefined, { + method: "post", statusCode: 401, }); diff --git a/packages/slice-machine/components/Navigation/index.tsx b/packages/slice-machine/components/Navigation/index.tsx index 01c1a32b4b..9bc327a10f 100644 --- a/packages/slice-machine/components/Navigation/index.tsx +++ b/packages/slice-machine/components/Navigation/index.tsx @@ -141,17 +141,29 @@ const Navigation: FC = () => { )} - } - onClick={() => { - void telemetry.track({ - event: "users-invite-button-clicked", - }); - }} - target="_blank" - /> + + } + active={router.asPath.startsWith("/settings")} + component={Link} + /> + + + + } + onClick={() => { + void telemetry.track({ + event: "users-invite-button-clicked", + }); + }} + target="_blank" + /> + diff --git a/packages/slice-machine/pages/settings.tsx b/packages/slice-machine/pages/settings.tsx new file mode 100644 index 0000000000..23686f25fc --- /dev/null +++ b/packages/slice-machine/pages/settings.tsx @@ -0,0 +1,64 @@ +import React from "react"; +import Head from "next/head"; +import { BaseStyles } from "theme-ui"; +import { + AppLayout, + AppLayoutBreadcrumb, + AppLayoutContent, + AppLayoutHeader, +} from "@components/AppLayout"; +import { Button } from "@prismicio/editor-ui"; +import { managerClient } from "@src/managerClient"; + +const Settings: React.FC = () => { + const connect = async (provider: "gitHub") => { + switch (provider) { + case "gitHub": { + const state = await managerClient.git.createGitHubAuthState(); + + const url = new URL( + "https://github.com/apps/prismic-push-models-poc/installations/new", + ); + url.searchParams.set("state", state.key); + + window.open(url, "git-hub-app-installation"); + + return; + } + } + }; + + return ( + <> + + Settings - Slice Machine + + + + + + + +
+ Connected Git Repository +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+

Learn more about Prismic for Git

+
+
+
+
+ + ); +}; + +export default Settings; From 1959c662bcf51c3afd483fa45dbbe5166263a4c8 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Mon, 22 Jan 2024 12:25:25 -1000 Subject: [PATCH 10/17] feat: add Settings page with Git demo --- .../manager/src/managers/git/GitManager.ts | 4 +- packages/manager/src/managers/git/types.ts | 5 +- packages/slice-machine/pages/settings.tsx | 36 +-- .../ConnectGitRepository.module.css | 15 + .../ConnectGitRepository.tsx | 286 ++++++++++++++++++ .../src/features/git/useGitOwners.ts | 10 + .../src/features/git/useGitRepos.ts | 16 + .../src/features/git/useLinkedGitRepos.ts | 30 ++ .../features/git/useLinkedGitReposActions.ts | 36 +++ packages/slice-machine/src/hooks/useUser.ts | 14 + 10 files changed, 415 insertions(+), 37 deletions(-) create mode 100644 packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.module.css create mode 100644 packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx create mode 100644 packages/slice-machine/src/features/git/useGitOwners.ts create mode 100644 packages/slice-machine/src/features/git/useGitRepos.ts create mode 100644 packages/slice-machine/src/features/git/useLinkedGitRepos.ts create mode 100644 packages/slice-machine/src/features/git/useLinkedGitReposActions.ts create mode 100644 packages/slice-machine/src/hooks/useUser.ts diff --git a/packages/manager/src/managers/git/GitManager.ts b/packages/manager/src/managers/git/GitManager.ts index dfa81bae06..b260b66237 100644 --- a/packages/manager/src/managers/git/GitManager.ts +++ b/packages/manager/src/managers/git/GitManager.ts @@ -10,14 +10,14 @@ import { UnauthorizedError, UnexpectedDataError } from "../../errors"; import { BaseManager } from "../BaseManager"; -import { GitRepo, GitRepoSpcifier, Namespace } from "./types"; +import { GitRepo, GitRepoSpcifier, Owner } from "./types"; type CreateGitHubAuthStateReturnType = { key: string; expiresAt: Date; }; -type GitManagerFetchOwnersReturnType = Namespace[]; +type GitManagerFetchOwnersReturnType = Owner[]; type GitManagerFetchReposReturnType = GitRepo[]; diff --git a/packages/manager/src/managers/git/types.ts b/packages/manager/src/managers/git/types.ts index ca02175bc3..f1b6ce68aa 100644 --- a/packages/manager/src/managers/git/types.ts +++ b/packages/manager/src/managers/git/types.ts @@ -1,10 +1,12 @@ -export type Namespace = { +export type Owner = { + provider: "gitHub"; id: string; name: string; type: "user" | "team" | null; }; export type GitRepo = { + provider: "gitHub"; id: string; owner: string; name: string; @@ -13,6 +15,7 @@ export type GitRepo = { }; export type GitRepoSpcifier = { + provider: "gitHub"; owner: string; name: string; }; diff --git a/packages/slice-machine/pages/settings.tsx b/packages/slice-machine/pages/settings.tsx index 23686f25fc..0328b190fe 100644 --- a/packages/slice-machine/pages/settings.tsx +++ b/packages/slice-machine/pages/settings.tsx @@ -7,27 +7,9 @@ import { AppLayoutContent, AppLayoutHeader, } from "@components/AppLayout"; -import { Button } from "@prismicio/editor-ui"; -import { managerClient } from "@src/managerClient"; +import { ConnectGitRepository } from "@src/features/git/ConnectGitRepository/ConnectGitRepository"; const Settings: React.FC = () => { - const connect = async (provider: "gitHub") => { - switch (provider) { - case "gitHub": { - const state = await managerClient.git.createGitHubAuthState(); - - const url = new URL( - "https://github.com/apps/prismic-push-models-poc/installations/new", - ); - url.searchParams.set("state", state.key); - - window.open(url, "git-hub-app-installation"); - - return; - } - } - }; - return ( <> @@ -39,21 +21,7 @@ const Settings: React.FC = () => { -
- Connected Git Repository -
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-

Learn more about Prismic for Git

-
+
diff --git a/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.module.css b/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.module.css new file mode 100644 index 0000000000..c3ba8bfad8 --- /dev/null +++ b/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.module.css @@ -0,0 +1,15 @@ +.root { + max-width: 600px; +} + +.buttons { + display: grid; + grid-auto-columns: 1fr; + grid-auto-flow: column; + gap: 8px; +} + +.owners { + display: grid; + gap: 8px; +} diff --git a/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx b/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx new file mode 100644 index 0000000000..d17035e9da --- /dev/null +++ b/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx @@ -0,0 +1,286 @@ +import { PropsWithChildren, Suspense, useState } from "react"; +import { + Button, + ErrorBoundary, + ProgressCircle, + Select, + SelectItem, + Table, + TableBody, + TableCell, + TableRow, + Text, +} from "@prismicio/editor-ui"; + +import useSliceMachineActions from "@src/modules/useSliceMachineActions"; +import { managerClient } from "@src/managerClient"; +import { useSliceMachineConfig } from "@src/hooks/useSliceMachineConfig"; +import { useUser } from "@src/hooks/useUser"; + +import { useGitOwners } from "../useGitOwners"; +import { useGitRepos } from "../useGitRepos"; +import { useLinkedGitRepos } from "../useLinkedGitRepos"; +import { useLinkedGitReposActions } from "../useLinkedGitReposActions"; + +import * as styles from "./ConnectGitRepository.module.css"; + +// TODO: Export types from `@slicemachine/manager` +type GitOwner = Awaited< + ReturnType<(typeof managerClient)["git"]["fetchOwners"]> +>[number]; +type GitRepo = Awaited< + ReturnType<(typeof managerClient)["git"]["fetchRepos"]> +>[number]; + +type ConnectButtonBaseProps = PropsWithChildren<{ + provider: "gitHub" | "gitLab" | "bitbucket"; + disabled?: boolean; +}>; + +function ConnectButton(props: ConnectButtonBaseProps) { + const { provider, children, disabled } = props; + + const [isLoading, setIsLoading] = useState(false); + + const connect = async () => { + switch (provider) { + case "gitHub": { + setIsLoading(true); + + const state = await managerClient.git.createGitHubAuthState(); + + const url = new URL( + "https://github.com/apps/prismic-push-models-poc/installations/new", + ); + url.searchParams.set("state", state.key); + + window.open(url, "git-hub-app-installation"); + + return; + } + } + }; + + return ( + + ); +} + +function ConnectButtons() { + return ( +
    +
  • + GitHub +
  • +
  • + + GitLab (soon) + +
  • +
  • + + Bitbucket (soon) + +
  • +
+ ); +} + +type SelectOwnerBaseProps = { + owners: GitOwner[]; + onSelect: (owner: GitOwner) => void; +}; + +function SelectOwnerBase(props: SelectOwnerBaseProps) { + const { owners, onSelect } = props; + + const onValueChange = (value: string) => { + const [provider, id] = value.split("@"); + const owner = owners.find((o) => o.provider === provider && o.id === id); + + if (owner) { + onSelect(owner); + } + }; + + return ( + + ); +} + +function SelectOwner(props: SelectOwnerBaseProps) { + return ( + }> + + + ); +} + +type SelectRepoBaseProps = { + owner?: { + provider: "gitHub"; + name: string; + }; + onSelect: (repo: GitRepo) => void; +}; + +function SelectRepoBase(props: SelectRepoBaseProps) { + const { onSelect } = props; + + const repos = useGitRepos( + props.owner + ? { + provider: props.owner?.provider, + owner: props.owner?.name, + } + : undefined, + ); + + return ( + + + {repos?.map((repo) => { + return ( + + + + {repo.name} + + + + + + + ); + })} + +
+ ); +} + +function SelectRepo(props: SelectRepoBaseProps) { + return ( + }> + + + ); +} + +function RepoSelector() { + const [config] = useSliceMachineConfig(); + const owners = useGitOwners(); + const { linkRepo } = useLinkedGitReposActions({ + prismic: { domain: config.repositoryName }, + }); + + const [selectedOwner, setSelectedOwner] = useState(); + + if (owners.length < 1) { + return ; + } + + return ( +
+ + setSelectedOwner(owner)} + /> + + {selectedOwner ? ( + void linkRepo(repo)} + /> + ) : ( +
Select a user/owner first
+ )} +
+ ); +} + +function LoggedInContents() { + const [config] = useSliceMachineConfig(); + const linkedGitRepos = useLinkedGitRepos({ + prismic: { domain: config.repositoryName }, + }); + const { unlinkRepo } = useLinkedGitReposActions({ + prismic: { domain: config.repositoryName }, + }); + + if ("error" in linkedGitRepos) { + return
TODO: Handle error
; + } + + if (linkedGitRepos.repos.length === 0) { + return ; + } + + const linkedRepo = linkedGitRepos.repos[0]; + + return ( +
+ Linked: [{linkedRepo.provider}] {linkedRepo.owner}/{linkedRepo.name} + +
+ ); +} + +function LoggedOutContents() { + const { openLoginModal } = useSliceMachineActions(); + + return ( +
+ You must be logged in to connect a Git repository. + +
+ ); +} + +function Contents() { + const { isLoggedIn } = useUser(); + + if (!isLoggedIn) { + return ; + } + + return ; +} + +export function ConnectGitRepository() { + return ( +
+ Connected Git Repository + { + return
Error
; + }} + > + }> + + +
+
+ ); +} diff --git a/packages/slice-machine/src/features/git/useGitOwners.ts b/packages/slice-machine/src/features/git/useGitOwners.ts new file mode 100644 index 0000000000..4ab5e7da9f --- /dev/null +++ b/packages/slice-machine/src/features/git/useGitOwners.ts @@ -0,0 +1,10 @@ +import { useRequest } from "@prismicio/editor-support/Suspense"; +import { managerClient } from "@src/managerClient"; + +const getGitOwners = async () => { + return await managerClient.git.fetchOwners(); +}; + +export const useGitOwners = () => { + return useRequest(getGitOwners, []); +}; diff --git a/packages/slice-machine/src/features/git/useGitRepos.ts b/packages/slice-machine/src/features/git/useGitRepos.ts new file mode 100644 index 0000000000..ca445d3723 --- /dev/null +++ b/packages/slice-machine/src/features/git/useGitRepos.ts @@ -0,0 +1,16 @@ +import { useRequest } from "@prismicio/editor-support/Suspense"; +import { managerClient } from "@src/managerClient"; + +type UseGitReposArgs = Parameters[0]; + +const getGitRepos = async (args?: UseGitReposArgs) => { + if (!args) { + return; + } + + return await managerClient.git.fetchRepos(args); +}; + +export const useGitRepos = (args?: UseGitReposArgs) => { + return useRequest(getGitRepos, [args]); +}; diff --git a/packages/slice-machine/src/features/git/useLinkedGitRepos.ts b/packages/slice-machine/src/features/git/useLinkedGitRepos.ts new file mode 100644 index 0000000000..71188028bf --- /dev/null +++ b/packages/slice-machine/src/features/git/useLinkedGitRepos.ts @@ -0,0 +1,30 @@ +import { useRequest } from "@prismicio/editor-support/Suspense"; +import { managerClient } from "@src/managerClient"; + +type PrismicRepoSpecifier = { + domain: string; +}; + +type UseLinkedGitReposReturnType = + | { + repos: Awaited< + ReturnType<(typeof managerClient)["git"]["fetchLinkedRepos"]> + >; + } + | { error: unknown }; + +export const getLinkedGitRepos = async (args: { + prismic: PrismicRepoSpecifier; +}): Promise => { + try { + const repos = await managerClient.git.fetchLinkedRepos(args); + + return { repos }; + } catch (error) { + return { error }; + } +}; + +export const useLinkedGitRepos = (args: { prismic: PrismicRepoSpecifier }) => { + return useRequest(getLinkedGitRepos, [args]); +}; diff --git a/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts b/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts new file mode 100644 index 0000000000..f6d6ab8467 --- /dev/null +++ b/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts @@ -0,0 +1,36 @@ +import { revalidateData } from "@prismicio/editor-support/Suspense"; +import { managerClient } from "@src/managerClient"; + +import { getLinkedGitRepos } from "./useLinkedGitRepos"; + +type PrismicRepoSpecifier = { + domain: string; +}; +type GitRepoSpecifier = { + provider: "gitHub"; + owner: string; + name: string; +}; + +export const useLinkedGitReposActions = (args: { + prismic: PrismicRepoSpecifier; +}) => { + return { + linkRepo: async (git: GitRepoSpecifier) => { + await managerClient.git.linkRepo({ + prismic: { domain: args.prismic.domain }, + git, + }); + + revalidateData(getLinkedGitRepos, [args]); + }, + unlinkRepo: async (git: GitRepoSpecifier) => { + await managerClient.git.unlinkRepo({ + prismic: { domain: args.prismic.domain }, + git, + }); + + revalidateData(getLinkedGitRepos, [args]); + }, + }; +}; diff --git a/packages/slice-machine/src/hooks/useUser.ts b/packages/slice-machine/src/hooks/useUser.ts new file mode 100644 index 0000000000..280b1f1af0 --- /dev/null +++ b/packages/slice-machine/src/hooks/useUser.ts @@ -0,0 +1,14 @@ +import { useRequest } from "@prismicio/editor-support/Suspense"; +import { managerClient } from "@src/managerClient"; + +const getUser = async () => { + const isLoggedIn = await managerClient.user.checkIsLoggedIn(); + + return { + isLoggedIn, + }; +}; + +export const useUser = () => { + return useRequest(getUser, []); +}; From dd74d5b96dd56c45ad20c5f909d611629386b6c0 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Mon, 22 Jan 2024 14:18:33 -1000 Subject: [PATCH 11/17] feat: add Write API token methods --- .../manager/src/managers/git/GitManager.ts | 158 +++++++++++++++++- .../src/managers/git/buildGitRepoSpecifier.ts | 24 +++ packages/manager/src/managers/git/types.ts | 7 +- ...eManager-git-checkHasWriteAPIToken.test.ts | 102 +++++++++++ ...ineManager-git-deleteWriteAPIToken.test.ts | 80 +++++++++ ...ineManager-git-updateWriteAPIToken.test.ts | 85 ++++++++++ 6 files changed, 450 insertions(+), 6 deletions(-) create mode 100644 packages/manager/src/managers/git/buildGitRepoSpecifier.ts create mode 100644 packages/manager/test/SliceMachineManager-git-checkHasWriteAPIToken.test.ts create mode 100644 packages/manager/test/SliceMachineManager-git-deleteWriteAPIToken.test.ts create mode 100644 packages/manager/test/SliceMachineManager-git-updateWriteAPIToken.test.ts diff --git a/packages/manager/src/managers/git/GitManager.ts b/packages/manager/src/managers/git/GitManager.ts index e49ae15f5e..3a12ff6365 100644 --- a/packages/manager/src/managers/git/GitManager.ts +++ b/packages/manager/src/managers/git/GitManager.ts @@ -6,18 +6,23 @@ import { decode } from "../../lib/decode"; import { API_ENDPOINTS } from "../../constants/API_ENDPOINTS"; -import { UnauthorizedError, UnexpectedDataError } from "../../errors"; +import { + UnauthenticatedError, + UnauthorizedError, + UnexpectedDataError, +} from "../../errors"; import { BaseManager } from "../BaseManager"; -import { GitRepo, GitRepoSpcifier, Namespace } from "./types"; +import { GitRepo, GitRepoSpecifier, Owner } from "./types"; +import { buildGitRepoSpecifier } from "./buildGitRepoSpecifier"; type CreateGitHubAuthStateReturnType = { key: string; expiresAt: Date; }; -type GitManagerFetchOwnersReturnType = Namespace[]; +type GitManagerFetchOwnersReturnType = Owner[]; type GitManagerFetchReposReturnType = GitRepo[]; @@ -34,7 +39,7 @@ type GitManagerFetchLinkedReposArgs = { }; }; -type GitManagerFetchLinkedReposReturnType = GitRepoSpcifier[]; +type GitManagerFetchLinkedReposReturnType = GitRepoSpecifier[]; type GitManagerLinkRepoArgs = { prismic: { @@ -58,6 +63,40 @@ type GitManagerUnlinkRepoArgs = { }; }; +type CheckHasWriteAPITokenArgs = { + prismic: { + domain: string; + }; + git: { + provider: "gitHub"; + owner: string; + name: string; + }; +}; + +type UpdateWriteAPITokenArgs = { + prismic: { + domain: string; + }; + git: { + provider: "gitHub"; + owner: string; + name: string; + }; + token: string; +}; + +type DeleteWriteAPITokenArgs = { + prismic: { + domain: string; + }; + git: { + provider: "gitHub"; + owner: string; + name: string; + }; +}; + export class GitManager extends BaseManager { async createGitHubAuthState(): Promise { const url = new URL( @@ -276,6 +315,117 @@ export class GitManager extends BaseManager { } } + async checkHasWriteAPIToken( + args: CheckHasWriteAPITokenArgs, + ): Promise { + const url = new URL( + "./git/linked-repos/write-api-token", + API_ENDPOINTS.SliceMachineV1, + ); + url.searchParams.set("repository", args.prismic.domain); + url.searchParams.set( + "git", + buildGitRepoSpecifier({ + provider: args.git.provider, + owner: args.git.owner, + name: args.git.name, + }), + ); + + const res = await this.#fetch(url); + + if (!res.ok) { + switch (res.status) { + case 401: + throw new UnauthenticatedError(); + case 403: + throw new UnauthorizedError(); + default: + throw new Error("Failed to check Prismic Write API token."); + } + } + + const json = await res.json(); + const { value, error } = decode( + t.type({ + hasWriteAPIToken: t.boolean, + }), + json, + ); + + if (error) { + throw new UnexpectedDataError( + `Failed to decode: ${error.errors.join(", ")}`, + { cause: error }, + ); + } + + return value.hasWriteAPIToken; + } + + async updateWriteAPIToken(args: UpdateWriteAPITokenArgs): Promise { + const url = new URL( + "./git/linked-repos/write-api-token", + API_ENDPOINTS.SliceMachineV1, + ); + const res = await this.#fetch(url, { + method: "PUT", + body: { + prismic: { + domain: args.prismic.domain, + }, + git: { + provider: args.git.provider, + owner: args.git.owner, + name: args.git.name, + }, + token: args.token, + }, + }); + + if (!res.ok) { + switch (res.status) { + case 401: + throw new UnauthenticatedError(); + case 403: + throw new UnauthorizedError(); + default: + throw new Error("Failed to update Prismic Write API token."); + } + } + } + + async deleteWriteAPIToken(args: DeleteWriteAPITokenArgs): Promise { + const url = new URL( + "./git/linked-repos/write-api-token", + API_ENDPOINTS.SliceMachineV1, + ); + const res = await this.#fetch(url, { + method: "DELETE", + body: { + prismic: { + domain: args.prismic.domain, + }, + git: { + provider: args.git.provider, + owner: args.git.owner, + name: args.git.name, + }, + }, + }); + + if (!res.ok) { + switch (res.status) { + case 401: + throw new UnauthenticatedError(); + case 403: + throw new UnauthorizedError(); + default: + throw new Error("Failed to delete Prismic Write API token."); + } + } + } + async #fetch( url: URL, config?: { diff --git a/packages/manager/src/managers/git/buildGitRepoSpecifier.ts b/packages/manager/src/managers/git/buildGitRepoSpecifier.ts new file mode 100644 index 0000000000..7a9111f4e5 --- /dev/null +++ b/packages/manager/src/managers/git/buildGitRepoSpecifier.ts @@ -0,0 +1,24 @@ +import { GitRepoSpecifier } from "./types"; + +/** + * Builds a Git repository specifier from its individual parts. + * + * @example + * + * ```typescript + * buildGitRepoSpecifier({ + * provider: "gitHub", + * owner: "foo", + * name: "bar", + * }); + * ``` + * + * @param repoSpecifier - The Git repository specifier. + * + * @returns The specifier in the form of `provider@owner/name`. + */ +export const buildGitRepoSpecifier = ( + repoSpecifier: GitRepoSpecifier, +): string => { + return `${repoSpecifier.provider}@${repoSpecifier.owner}/${repoSpecifier.name}`; +}; diff --git a/packages/manager/src/managers/git/types.ts b/packages/manager/src/managers/git/types.ts index ca02175bc3..1dfdfc1107 100644 --- a/packages/manager/src/managers/git/types.ts +++ b/packages/manager/src/managers/git/types.ts @@ -1,10 +1,12 @@ -export type Namespace = { +export type Owner = { + provider: "gitHub"; id: string; name: string; type: "user" | "team" | null; }; export type GitRepo = { + provider: "gitHub"; id: string; owner: string; name: string; @@ -12,7 +14,8 @@ export type GitRepo = { pushedAt: Date; }; -export type GitRepoSpcifier = { +export type GitRepoSpecifier = { + provider: "gitHub"; owner: string; name: string; }; diff --git a/packages/manager/test/SliceMachineManager-git-checkHasWriteAPIToken.test.ts b/packages/manager/test/SliceMachineManager-git-checkHasWriteAPIToken.test.ts new file mode 100644 index 0000000000..c56fb615e7 --- /dev/null +++ b/packages/manager/test/SliceMachineManager-git-checkHasWriteAPIToken.test.ts @@ -0,0 +1,102 @@ +import { expect, it } from "vitest"; + +import { UnauthenticatedError, UnauthorizedError } from "../src"; + +it("returns true if linked repositories have a Prismic Write API token", async ({ + manager, + api, + login, +}) => { + const prismic = { domain: "domain" }; + const git = { provider: "gitHub", owner: "owner", name: "name" } as const; + + api.mockSliceMachineV1( + "./git/linked-repos/write-api-token", + { hasWriteAPIToken: true }, + { + searchParams: { + repository: prismic.domain, + git: `${git.provider}@${git.owner}/${git.name}`, + }, + }, + ); + + await login(); + const res = await manager.git.checkHasWriteAPIToken({ prismic, git }); + + expect(res).toStrictEqual(true); +}); + +it("returns false if linked repositories do not have a Prismic Write API token", async ({ + manager, + api, + login, +}) => { + const prismic = { domain: "domain" }; + const git = { provider: "gitHub", owner: "owner", name: "name" } as const; + + api.mockSliceMachineV1( + "./git/linked-repos/write-api-token", + { hasWriteAPIToken: false }, + { + searchParams: { + repository: prismic.domain, + git: `${git.provider}@${git.owner}/${git.name}`, + }, + }, + ); + + await login(); + const res = await manager.git.checkHasWriteAPIToken({ prismic, git }); + + expect(res).toStrictEqual(false); +}); + +it("throws UnauthenticatedError if the API returns 403", async ({ + manager, + api, + login, +}) => { + api.mockSliceMachineV1("./git/linked-repos/write-api-token", undefined, { + statusCode: 401, + }); + + await login(); + await expect(() => + manager.git.checkHasWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).rejects.toThrow(UnauthenticatedError); +}); + +it("throws UnauthorizedError if the API returns 403", async ({ + manager, + api, + login, +}) => { + api.mockSliceMachineV1("./git/linked-repos/write-api-token", undefined, { + statusCode: 403, + }); + + await login(); + await expect(() => + manager.git.checkHasWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).rejects.toThrow(UnauthorizedError); +}); + +it("throws UnauthenticatedError if the user is logged out", async ({ + manager, +}) => { + await manager.user.logout(); + + await expect(() => + manager.git.checkHasWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).rejects.toThrow(UnauthenticatedError); +}); diff --git a/packages/manager/test/SliceMachineManager-git-deleteWriteAPIToken.test.ts b/packages/manager/test/SliceMachineManager-git-deleteWriteAPIToken.test.ts new file mode 100644 index 0000000000..120f9b663d --- /dev/null +++ b/packages/manager/test/SliceMachineManager-git-deleteWriteAPIToken.test.ts @@ -0,0 +1,80 @@ +import { expect, it } from "vitest"; + +import { UnauthenticatedError, UnauthorizedError } from "../src"; + +it("deletes a pair of linked repositories' Write API token", async ({ + manager, + api, + login, +}) => { + api.mockSliceMachineV1( + "./git/linked-repos/write-api-token", + { hasWriteAPIToken: true }, + { + method: "delete", + body: { + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }, + }, + ); + + await login(); + await expect( + manager.git.deleteWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).resolves.not.toThrow(); +}); + +it("throws UnauthenticatedError if the API returns 403", async ({ + manager, + api, + login, +}) => { + api.mockSliceMachineV1("./git/linked-repos/write-api-token", undefined, { + method: "delete", + statusCode: 401, + }); + + await login(); + await expect(() => + manager.git.deleteWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).rejects.toThrow(UnauthenticatedError); +}); + +it("throws UnauthorizedError if the API returns 403", async ({ + manager, + api, + login, +}) => { + api.mockSliceMachineV1("./git/linked-repos/write-api-token", undefined, { + method: "delete", + statusCode: 403, + }); + + await login(); + await expect(() => + manager.git.deleteWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).rejects.toThrow(UnauthorizedError); +}); + +it("throws UnauthenticatedError if the user is logged out", async ({ + manager, +}) => { + await manager.user.logout(); + + await expect(() => + manager.git.deleteWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + }), + ).rejects.toThrow(UnauthenticatedError); +}); diff --git a/packages/manager/test/SliceMachineManager-git-updateWriteAPIToken.test.ts b/packages/manager/test/SliceMachineManager-git-updateWriteAPIToken.test.ts new file mode 100644 index 0000000000..4f900c30d0 --- /dev/null +++ b/packages/manager/test/SliceMachineManager-git-updateWriteAPIToken.test.ts @@ -0,0 +1,85 @@ +import { expect, it } from "vitest"; + +import { UnauthenticatedError, UnauthorizedError } from "../src"; + +it("updates a pair of linked repositories' Write API token", async ({ + manager, + api, + login, +}) => { + api.mockSliceMachineV1( + "./git/linked-repos/write-api-token", + { hasWriteAPIToken: true }, + { + method: "put", + body: { + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + token: "token", + }, + }, + ); + + await login(); + await expect( + manager.git.updateWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + token: "token", + }), + ).resolves.not.toThrow(); +}); + +it("throws UnauthenticatedError if the API returns 403", async ({ + manager, + api, + login, +}) => { + api.mockSliceMachineV1("./git/linked-repos/write-api-token", undefined, { + method: "put", + statusCode: 401, + }); + + await login(); + await expect(() => + manager.git.updateWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + token: "token", + }), + ).rejects.toThrow(UnauthenticatedError); +}); + +it("throws UnauthorizedError if the API returns 403", async ({ + manager, + api, + login, +}) => { + api.mockSliceMachineV1("./git/linked-repos/write-api-token", undefined, { + method: "put", + statusCode: 403, + }); + + await login(); + await expect(() => + manager.git.updateWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + token: "token", + }), + ).rejects.toThrow(UnauthorizedError); +}); + +it("throws UnauthenticatedError if the user is logged out", async ({ + manager, +}) => { + await manager.user.logout(); + + await expect(() => + manager.git.updateWriteAPIToken({ + prismic: { domain: "domain" }, + git: { provider: "gitHub", owner: "owner", name: "name" }, + token: "token", + }), + ).rejects.toThrow(UnauthenticatedError); +}); From 14249630f5050ef08c9485e4679998a229bcfc49 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Mon, 22 Jan 2024 16:09:41 -1000 Subject: [PATCH 12/17] feat: add Write API token management --- .../ConnectGitRepository.tsx | 133 +++++++++++++++--- .../slice-machine/src/features/git/types.ts | 9 ++ .../src/features/git/useHasWriteAPIToken.ts | 23 +++ .../src/features/git/useLinkedGitRepos.ts | 13 +- .../features/git/useLinkedGitReposActions.ts | 34 +++-- .../features/git/useWriteAPITokenActions.ts | 40 ++++++ 6 files changed, 214 insertions(+), 38 deletions(-) create mode 100644 packages/slice-machine/src/features/git/types.ts create mode 100644 packages/slice-machine/src/features/git/useHasWriteAPIToken.ts create mode 100644 packages/slice-machine/src/features/git/useWriteAPITokenActions.ts diff --git a/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx b/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx index d17035e9da..41e6ff37d6 100644 --- a/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx +++ b/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx @@ -1,7 +1,10 @@ import { PropsWithChildren, Suspense, useState } from "react"; import { Button, + ButtonGroup, ErrorBoundary, + Form, + FormInput, ProgressCircle, Select, SelectItem, @@ -14,13 +17,14 @@ import { import useSliceMachineActions from "@src/modules/useSliceMachineActions"; import { managerClient } from "@src/managerClient"; -import { useSliceMachineConfig } from "@src/hooks/useSliceMachineConfig"; import { useUser } from "@src/hooks/useUser"; import { useGitOwners } from "../useGitOwners"; import { useGitRepos } from "../useGitRepos"; +import { useHasWriteAPIToken } from "../useHasWriteAPIToken"; import { useLinkedGitRepos } from "../useLinkedGitRepos"; import { useLinkedGitReposActions } from "../useLinkedGitReposActions"; +import { useWriteAPITokenActions } from "../useWriteAPITokenActions"; import * as styles from "./ConnectGitRepository.module.css"; @@ -188,11 +192,8 @@ function SelectRepo(props: SelectRepoBaseProps) { } function RepoSelector() { - const [config] = useSliceMachineConfig(); const owners = useGitOwners(); - const { linkRepo } = useLinkedGitReposActions({ - prismic: { domain: config.repositoryName }, - }); + const { linkRepo } = useLinkedGitReposActions(); const [selectedOwner, setSelectedOwner] = useState(); @@ -220,14 +221,115 @@ function RepoSelector() { ); } +type UpdateOrDeleteWriteAPIFormProps = { + repo: { + provider: "gitHub"; + owner: string; + name: string; + }; +}; + +function UpdateOrDeleteWriteAPIForm(props: UpdateOrDeleteWriteAPIFormProps) { + const { repo } = props; + + const { deleteToken } = useWriteAPITokenActions({ git: repo }); + const [isUpdating, setIsUpdating] = useState(false); + + return isUpdating ? ( + setIsUpdating(false)} + /> + ) : ( +
+ You have a Write API token saved. + + + + +
+ ); +} + +type UpdateWriteAPIFormProps = { + repo: { + provider: "gitHub"; + owner: string; + name: string; + }; + withCancel?: boolean; + onCancel?: () => void | Promise; +}; + +function UpdateWriteAPIForm(props: UpdateWriteAPIFormProps) { + const { repo, withCancel, onCancel } = props; + + const { updateToken } = useWriteAPITokenActions({ git: repo }); + + const [token, setToken] = useState(""); + const [isSubmitting, setIsSubmitting] = useState(false); + + const onSubmit = async () => { + if (token) { + setIsSubmitting(true); + await updateToken(token); + } + }; + + return ( +
+
void onSubmit()}> + + + {withCancel === true ? ( + + ) : null} + +
+ ); +} + +type LinkedRepositoryProps = { + linkedRepo: { + provider: "gitHub"; + owner: string; + name: string; + }; +}; + +function LinkedRepository(props: LinkedRepositoryProps) { + const { linkedRepo } = props; + + const { unlinkRepo } = useLinkedGitReposActions(); + const hasWriteAPIToken = useHasWriteAPIToken({ git: linkedRepo }); + + return ( +
+
+ Linked: [{linkedRepo.provider}] {linkedRepo.owner}/{linkedRepo.name} + +
+ {hasWriteAPIToken ? ( + + ) : ( + + )} +
+ ); +} + function LoggedInContents() { - const [config] = useSliceMachineConfig(); - const linkedGitRepos = useLinkedGitRepos({ - prismic: { domain: config.repositoryName }, - }); - const { unlinkRepo } = useLinkedGitReposActions({ - prismic: { domain: config.repositoryName }, - }); + const linkedGitRepos = useLinkedGitRepos(); if ("error" in linkedGitRepos) { return
TODO: Handle error
; @@ -239,12 +341,7 @@ function LoggedInContents() { const linkedRepo = linkedGitRepos.repos[0]; - return ( -
- Linked: [{linkedRepo.provider}] {linkedRepo.owner}/{linkedRepo.name} - -
- ); + return ; } function LoggedOutContents() { diff --git a/packages/slice-machine/src/features/git/types.ts b/packages/slice-machine/src/features/git/types.ts new file mode 100644 index 0000000000..a3395aee9d --- /dev/null +++ b/packages/slice-machine/src/features/git/types.ts @@ -0,0 +1,9 @@ +export type PrismicRepoSpecifier = { + domain: string; +}; + +export type GitRepoSpecifier = { + provider: "gitHub"; + owner: string; + name: string; +}; diff --git a/packages/slice-machine/src/features/git/useHasWriteAPIToken.ts b/packages/slice-machine/src/features/git/useHasWriteAPIToken.ts new file mode 100644 index 0000000000..a72e4d4485 --- /dev/null +++ b/packages/slice-machine/src/features/git/useHasWriteAPIToken.ts @@ -0,0 +1,23 @@ +import { useRequest } from "@prismicio/editor-support/Suspense"; +import { useSliceMachineConfig } from "@src/hooks/useSliceMachineConfig"; +import { managerClient } from "@src/managerClient"; + +import { GitRepoSpecifier, PrismicRepoSpecifier } from "./types"; + +export const getHasWriteAPIToken = async (args: { + prismic: PrismicRepoSpecifier; + git: GitRepoSpecifier; +}): Promise => { + return await managerClient.git.checkHasWriteAPIToken(args); +}; + +export const useHasWriteAPIToken = (args: { git: GitRepoSpecifier }) => { + const [config] = useSliceMachineConfig(); + + return useRequest(getHasWriteAPIToken, [ + { + prismic: { domain: config.repositoryName }, + git: args.git, + }, + ]); +}; diff --git a/packages/slice-machine/src/features/git/useLinkedGitRepos.ts b/packages/slice-machine/src/features/git/useLinkedGitRepos.ts index 71188028bf..2daf5bcf45 100644 --- a/packages/slice-machine/src/features/git/useLinkedGitRepos.ts +++ b/packages/slice-machine/src/features/git/useLinkedGitRepos.ts @@ -1,9 +1,8 @@ import { useRequest } from "@prismicio/editor-support/Suspense"; +import { useSliceMachineConfig } from "@src/hooks/useSliceMachineConfig"; import { managerClient } from "@src/managerClient"; -type PrismicRepoSpecifier = { - domain: string; -}; +import { PrismicRepoSpecifier } from "./types"; type UseLinkedGitReposReturnType = | { @@ -25,6 +24,10 @@ export const getLinkedGitRepos = async (args: { } }; -export const useLinkedGitRepos = (args: { prismic: PrismicRepoSpecifier }) => { - return useRequest(getLinkedGitRepos, [args]); +export const useLinkedGitRepos = () => { + const [config] = useSliceMachineConfig(); + + return useRequest(getLinkedGitRepos, [ + { prismic: { domain: config.repositoryName } }, + ]); }; diff --git a/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts b/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts index f6d6ab8467..fd4da2a611 100644 --- a/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts +++ b/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts @@ -1,36 +1,40 @@ import { revalidateData } from "@prismicio/editor-support/Suspense"; +import { useSliceMachineConfig } from "@src/hooks/useSliceMachineConfig"; import { managerClient } from "@src/managerClient"; +import { GitRepoSpecifier } from "./types"; +import { getHasWriteAPIToken } from "./useHasWriteAPIToken"; import { getLinkedGitRepos } from "./useLinkedGitRepos"; -type PrismicRepoSpecifier = { - domain: string; -}; -type GitRepoSpecifier = { - provider: "gitHub"; - owner: string; - name: string; -}; +export const useLinkedGitReposActions = () => { + const [config] = useSliceMachineConfig(); -export const useLinkedGitReposActions = (args: { - prismic: PrismicRepoSpecifier; -}) => { return { linkRepo: async (git: GitRepoSpecifier) => { await managerClient.git.linkRepo({ - prismic: { domain: args.prismic.domain }, + prismic: { domain: config.repositoryName }, git, }); - revalidateData(getLinkedGitRepos, [args]); + revalidateData(getLinkedGitRepos, [ + { prismic: { domain: config.repositoryName } }, + ]); }, unlinkRepo: async (git: GitRepoSpecifier) => { await managerClient.git.unlinkRepo({ - prismic: { domain: args.prismic.domain }, + prismic: { domain: config.repositoryName }, git, }); - revalidateData(getLinkedGitRepos, [args]); + revalidateData(getLinkedGitRepos, [ + { prismic: { domain: config.repositoryName } }, + ]); + revalidateData(getHasWriteAPIToken, [ + { + prismic: { domain: config.repositoryName }, + git, + }, + ]); }, }; }; diff --git a/packages/slice-machine/src/features/git/useWriteAPITokenActions.ts b/packages/slice-machine/src/features/git/useWriteAPITokenActions.ts new file mode 100644 index 0000000000..bcacad089b --- /dev/null +++ b/packages/slice-machine/src/features/git/useWriteAPITokenActions.ts @@ -0,0 +1,40 @@ +import { revalidateData } from "@prismicio/editor-support/Suspense"; +import { useSliceMachineConfig } from "@src/hooks/useSliceMachineConfig"; +import { managerClient } from "@src/managerClient"; + +import { GitRepoSpecifier } from "./types"; +import { getHasWriteAPIToken } from "./useHasWriteAPIToken"; + +export const useWriteAPITokenActions = (args: { git: GitRepoSpecifier }) => { + const [config] = useSliceMachineConfig(); + + return { + updateToken: async (token: string) => { + await managerClient.git.updateWriteAPIToken({ + prismic: { domain: config.repositoryName }, + git: args.git, + token, + }); + + revalidateData(getHasWriteAPIToken, [ + { + prismic: { domain: config.repositoryName }, + git: args.git, + }, + ]); + }, + deleteToken: async () => { + await managerClient.git.deleteWriteAPIToken({ + prismic: { domain: config.repositoryName }, + git: args.git, + }); + + revalidateData(getHasWriteAPIToken, [ + { + prismic: { domain: config.repositoryName }, + git: args.git, + }, + ]); + }, + }; +}; From 8f8bdc4e562132ddcd6b58aa19d0a2a3780b53a7 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Wed, 24 Jan 2024 09:32:40 -1000 Subject: [PATCH 13/17] fix: update write api token flow --- .../git/ConnectGitRepository/ConnectGitRepository.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx b/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx index 41e6ff37d6..07c3cf956c 100644 --- a/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx +++ b/packages/slice-machine/src/features/git/ConnectGitRepository/ConnectGitRepository.tsx @@ -28,6 +28,8 @@ import { useWriteAPITokenActions } from "../useWriteAPITokenActions"; import * as styles from "./ConnectGitRepository.module.css"; +const GITHUB_APP_SLUG = "prismic-prod-internal-test"; + // TODO: Export types from `@slicemachine/manager` type GitOwner = Awaited< ReturnType<(typeof managerClient)["git"]["fetchOwners"]> @@ -54,7 +56,7 @@ function ConnectButton(props: ConnectButtonBaseProps) { const state = await managerClient.git.createGitHubAuthState(); const url = new URL( - "https://github.com/apps/prismic-push-models-poc/installations/new", + `https://github.com/apps/${GITHUB_APP_SLUG}/installations/new`, ); url.searchParams.set("state", state.key); @@ -240,6 +242,7 @@ function UpdateOrDeleteWriteAPIForm(props: UpdateOrDeleteWriteAPIFormProps) { repo={repo} withCancel={true} onCancel={() => setIsUpdating(false)} + onSuccess={() => setIsUpdating(false)} /> ) : (
@@ -260,10 +263,11 @@ type UpdateWriteAPIFormProps = { }; withCancel?: boolean; onCancel?: () => void | Promise; + onSuccess?: () => void | Promise; }; function UpdateWriteAPIForm(props: UpdateWriteAPIFormProps) { - const { repo, withCancel, onCancel } = props; + const { repo, withCancel, onCancel, onSuccess } = props; const { updateToken } = useWriteAPITokenActions({ git: repo }); @@ -274,6 +278,7 @@ function UpdateWriteAPIForm(props: UpdateWriteAPIFormProps) { if (token) { setIsSubmitting(true); await updateToken(token); + await onSuccess?.(); } }; From 703f2f67102475ba896117746dc83a6c72c4e5cc Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Wed, 24 Jan 2024 12:16:27 -1000 Subject: [PATCH 14/17] fix: use positional arguments for suspense fetcher arguments --- .../src/features/git/useGitRepos.ts | 26 +++++++++++----- .../src/features/git/useHasWriteAPIToken.ts | 31 +++++++++++++------ .../src/features/git/useLinkedGitRepos.ts | 16 +++++----- .../features/git/useLinkedGitReposActions.ts | 16 ++++------ .../features/git/useWriteAPITokenActions.ts | 16 +++++----- 5 files changed, 60 insertions(+), 45 deletions(-) diff --git a/packages/slice-machine/src/features/git/useGitRepos.ts b/packages/slice-machine/src/features/git/useGitRepos.ts index ca445d3723..22ede88d29 100644 --- a/packages/slice-machine/src/features/git/useGitRepos.ts +++ b/packages/slice-machine/src/features/git/useGitRepos.ts @@ -1,16 +1,26 @@ import { useRequest } from "@prismicio/editor-support/Suspense"; import { managerClient } from "@src/managerClient"; -type UseGitReposArgs = Parameters[0]; - -const getGitRepos = async (args?: UseGitReposArgs) => { - if (!args) { +async function getGitRepos( + provider?: "gitHub", + owner?: string, + query?: string, + page?: number, +) { + if (provider === undefined || owner === undefined) { return; } - return await managerClient.git.fetchRepos(args); -}; + return await managerClient.git.fetchRepos({ provider, owner, query, page }); +} -export const useGitRepos = (args?: UseGitReposArgs) => { - return useRequest(getGitRepos, [args]); +export const useGitRepos = ( + args?: Parameters[0], +) => { + return useRequest(getGitRepos, [ + args?.provider, + args?.owner, + args?.query, + args?.page, + ]); }; diff --git a/packages/slice-machine/src/features/git/useHasWriteAPIToken.ts b/packages/slice-machine/src/features/git/useHasWriteAPIToken.ts index a72e4d4485..0a82dfe173 100644 --- a/packages/slice-machine/src/features/git/useHasWriteAPIToken.ts +++ b/packages/slice-machine/src/features/git/useHasWriteAPIToken.ts @@ -2,22 +2,33 @@ import { useRequest } from "@prismicio/editor-support/Suspense"; import { useSliceMachineConfig } from "@src/hooks/useSliceMachineConfig"; import { managerClient } from "@src/managerClient"; -import { GitRepoSpecifier, PrismicRepoSpecifier } from "./types"; +import { GitRepoSpecifier } from "./types"; -export const getHasWriteAPIToken = async (args: { - prismic: PrismicRepoSpecifier; - git: GitRepoSpecifier; -}): Promise => { - return await managerClient.git.checkHasWriteAPIToken(args); +export const getHasWriteAPIToken = async ( + prismicDomain: string, + gitProvider: "gitHub", + gitOwner: string, + gitName: string, +): Promise => { + return await managerClient.git.checkHasWriteAPIToken({ + prismic: { + domain: prismicDomain, + }, + git: { + provider: gitProvider, + owner: gitOwner, + name: gitName, + }, + }); }; export const useHasWriteAPIToken = (args: { git: GitRepoSpecifier }) => { const [config] = useSliceMachineConfig(); return useRequest(getHasWriteAPIToken, [ - { - prismic: { domain: config.repositoryName }, - git: args.git, - }, + config.repositoryName, + args.git.provider, + args.git.owner, + args.git.name, ]); }; diff --git a/packages/slice-machine/src/features/git/useLinkedGitRepos.ts b/packages/slice-machine/src/features/git/useLinkedGitRepos.ts index 2daf5bcf45..d715718a1a 100644 --- a/packages/slice-machine/src/features/git/useLinkedGitRepos.ts +++ b/packages/slice-machine/src/features/git/useLinkedGitRepos.ts @@ -2,8 +2,6 @@ import { useRequest } from "@prismicio/editor-support/Suspense"; import { useSliceMachineConfig } from "@src/hooks/useSliceMachineConfig"; import { managerClient } from "@src/managerClient"; -import { PrismicRepoSpecifier } from "./types"; - type UseLinkedGitReposReturnType = | { repos: Awaited< @@ -12,11 +10,13 @@ type UseLinkedGitReposReturnType = } | { error: unknown }; -export const getLinkedGitRepos = async (args: { - prismic: PrismicRepoSpecifier; -}): Promise => { +export const getLinkedGitRepos = async ( + prismicDomain: string, +): Promise => { try { - const repos = await managerClient.git.fetchLinkedRepos(args); + const repos = await managerClient.git.fetchLinkedRepos({ + prismic: { domain: prismicDomain }, + }); return { repos }; } catch (error) { @@ -27,7 +27,5 @@ export const getLinkedGitRepos = async (args: { export const useLinkedGitRepos = () => { const [config] = useSliceMachineConfig(); - return useRequest(getLinkedGitRepos, [ - { prismic: { domain: config.repositoryName } }, - ]); + return useRequest(getLinkedGitRepos, [config.repositoryName]); }; diff --git a/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts b/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts index fd4da2a611..7b86ab9bc0 100644 --- a/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts +++ b/packages/slice-machine/src/features/git/useLinkedGitReposActions.ts @@ -16,9 +16,7 @@ export const useLinkedGitReposActions = () => { git, }); - revalidateData(getLinkedGitRepos, [ - { prismic: { domain: config.repositoryName } }, - ]); + revalidateData(getLinkedGitRepos, [config.repositoryName]); }, unlinkRepo: async (git: GitRepoSpecifier) => { await managerClient.git.unlinkRepo({ @@ -26,14 +24,12 @@ export const useLinkedGitReposActions = () => { git, }); - revalidateData(getLinkedGitRepos, [ - { prismic: { domain: config.repositoryName } }, - ]); + revalidateData(getLinkedGitRepos, [config.repositoryName]); revalidateData(getHasWriteAPIToken, [ - { - prismic: { domain: config.repositoryName }, - git, - }, + config.repositoryName, + git.provider, + git.owner, + git.name, ]); }, }; diff --git a/packages/slice-machine/src/features/git/useWriteAPITokenActions.ts b/packages/slice-machine/src/features/git/useWriteAPITokenActions.ts index bcacad089b..956dffb2a3 100644 --- a/packages/slice-machine/src/features/git/useWriteAPITokenActions.ts +++ b/packages/slice-machine/src/features/git/useWriteAPITokenActions.ts @@ -17,10 +17,10 @@ export const useWriteAPITokenActions = (args: { git: GitRepoSpecifier }) => { }); revalidateData(getHasWriteAPIToken, [ - { - prismic: { domain: config.repositoryName }, - git: args.git, - }, + config.repositoryName, + args.git.provider, + args.git.owner, + args.git.name, ]); }, deleteToken: async () => { @@ -30,10 +30,10 @@ export const useWriteAPITokenActions = (args: { git: GitRepoSpecifier }) => { }); revalidateData(getHasWriteAPIToken, [ - { - prismic: { domain: config.repositoryName }, - git: args.git, - }, + config.repositoryName, + args.git.provider, + args.git.owner, + args.git.name, ]); }, }; From e8ce65738f07cbda8465d960eb0a7b8260129059 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Wed, 24 Jan 2024 12:18:56 -1000 Subject: [PATCH 15/17] fix: use correct HTTP method for `manager.git.createGitHubAuthState()` --- packages/manager/src/managers/git/GitManager.ts | 2 +- .../test/SliceMachineManager-git-createGitHubAuthState.test.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/manager/src/managers/git/GitManager.ts b/packages/manager/src/managers/git/GitManager.ts index 3a12ff6365..29f53e9a10 100644 --- a/packages/manager/src/managers/git/GitManager.ts +++ b/packages/manager/src/managers/git/GitManager.ts @@ -103,7 +103,7 @@ export class GitManager extends BaseManager { "./git/github/create-auth-state", API_ENDPOINTS.SliceMachineV1, ); - const res = await this.#fetch(url); + const res = await this.#fetch(url, { method: "POST" }); if (!res.ok) { switch (res.status) { diff --git a/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts b/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts index cc6d7d2d0e..3cf1fdd7bb 100644 --- a/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts +++ b/packages/manager/test/SliceMachineManager-git-createGitHubAuthState.test.ts @@ -9,7 +9,7 @@ it("returns a GitHub auth state token", async ({ manager, api, login }) => { api.mockSliceMachineV1( "./git/github/create-auth-state", { key, expiresAt: expiresAt.toISOString() }, - { checkAuthentication: true }, + { method: "post", checkAuthentication: true }, ); await login(); @@ -24,6 +24,7 @@ it("throws UnauthorizedError if the API returns 401", async ({ login, }) => { api.mockSliceMachineV1("./git/github/create-auth-state", undefined, { + method: "post", statusCode: 401, }); From 380a5e8599a22cfae5144a11d548879cad4bf5b7 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Wed, 24 Jan 2024 12:29:38 -1000 Subject: [PATCH 16/17] fix: use internal SM API URL --- packages/manager/src/constants/API_ENDPOINTS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/constants/API_ENDPOINTS.ts b/packages/manager/src/constants/API_ENDPOINTS.ts index 3fa458d0b3..2ca70eb68e 100644 --- a/packages/manager/src/constants/API_ENDPOINTS.ts +++ b/packages/manager/src/constants/API_ENDPOINTS.ts @@ -79,7 +79,7 @@ export const API_ENDPOINTS: APIEndpoints = (() => { PrismicOembed: "https://oembed.prismic.io", PrismicUnsplash: "https://unsplash.prismic.io", SliceMachineV1: - "https://21vvgrh0s6.execute-api.us-east-1.amazonaws.com/v1/", + "https://rrvzk2wqlh.execute-api.us-east-1.amazonaws.com/v1/", }; } } From 131d433a5f44b161cf498365134578a0a9f4d566 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Wed, 24 Jan 2024 15:57:27 -1000 Subject: [PATCH 17/17] fix: per Baptiste's review --- .../manager/src/managers/git/GitManager.ts | 22 ++++++++---- packages/manager/test/__setup__.ts | 34 ++++++++----------- .../test/__testutils__/createAPIFixture.ts | 13 +++---- 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/packages/manager/src/managers/git/GitManager.ts b/packages/manager/src/managers/git/GitManager.ts index 29f53e9a10..0858790ec3 100644 --- a/packages/manager/src/managers/git/GitManager.ts +++ b/packages/manager/src/managers/git/GitManager.ts @@ -17,7 +17,7 @@ import { BaseManager } from "../BaseManager"; import { GitRepo, GitRepoSpecifier, Owner } from "./types"; import { buildGitRepoSpecifier } from "./buildGitRepoSpecifier"; -type CreateGitHubAuthStateReturnType = { +type GitManagerCreateGitHubAuthStateReturnType = { key: string; expiresAt: Date; }; @@ -98,7 +98,7 @@ type DeleteWriteAPITokenArgs = { }; export class GitManager extends BaseManager { - async createGitHubAuthState(): Promise { + async createGitHubAuthState(): Promise { const url = new URL( "./git/github/create-auth-state", API_ENDPOINTS.SliceMachineV1, @@ -110,7 +110,7 @@ export class GitManager extends BaseManager { case 401: throw new UnauthorizedError(); default: - throw new Error("Failed to create GutHub auth state."); + throw new Error("Failed to create GitHub auth state."); } } @@ -139,6 +139,8 @@ export class GitManager extends BaseManager { if (!res.ok) { switch (res.status) { + case 401: + throw new UnauthenticatedError(); case 403: throw new UnauthorizedError(); default: @@ -180,7 +182,7 @@ export class GitManager extends BaseManager { if (args.query) { url.searchParams.set("q", args.query); } - if (args.page) { + if (args.page && args.page > 0) { url.searchParams.set("page", args.page.toString()); } @@ -188,6 +190,8 @@ export class GitManager extends BaseManager { if (!res.ok) { switch (res.status) { + case 401: + throw new UnauthenticatedError(); case 403: throw new UnauthorizedError(); default: @@ -232,6 +236,8 @@ export class GitManager extends BaseManager { if (!res.ok) { switch (res.status) { + case 401: + throw new UnauthenticatedError(); case 403: throw new UnauthorizedError(); default: @@ -281,6 +287,8 @@ export class GitManager extends BaseManager { if (!res.ok) { switch (res.status) { + case 401: + throw new UnauthenticatedError(); case 403: throw new UnauthorizedError(); default: @@ -307,10 +315,12 @@ export class GitManager extends BaseManager { if (!res.ok) { switch (res.status) { + case 401: + throw new UnauthenticatedError(); case 403: throw new UnauthorizedError(); default: - throw new Error("Failed to ulink repos."); + throw new Error("Failed to unlink repos."); } } } @@ -437,7 +447,7 @@ export class GitManager extends BaseManager { return await fetch(url, { method: config?.method, - body: config?.body ? JSON.stringify(config?.body) : undefined, + body: config?.body ? JSON.stringify(config.body) : undefined, headers: { Authorization: `Bearer ${authenticationToken}`, }, diff --git a/packages/manager/test/__setup__.ts b/packages/manager/test/__setup__.ts index 749180aca0..281023e0db 100644 --- a/packages/manager/test/__setup__.ts +++ b/packages/manager/test/__setup__.ts @@ -138,38 +138,32 @@ vi.mock("module", async () => { }); vi.mock("@segment/analytics-node", async () => { - const { Analytics }: typeof import("@segment/analytics-node") = + const actual: typeof import("@segment/analytics-node") = await vi.importActual("@segment/analytics-node"); - Analytics.prototype.track = vi.fn( - (_message: unknown, callback?: (error?: unknown) => void) => { - if (callback) { - callback(); - } + vi.spyOn(actual.Analytics.prototype, "track").mockImplementation( + (_params, callback) => { + callback?.(); }, ); - Analytics.prototype.identify = vi.fn( - (_message: unknown, callback?: (error?: unknown) => void) => { - if (callback) { - callback(); - } + vi.spyOn(actual.Analytics.prototype, "identify").mockImplementation( + (_params, callback) => { + callback?.(); }, ); - Analytics.prototype.group = vi.fn( - (_message: unknown, callback?: (error?: unknown) => void) => { - if (callback) { - callback(); - } + vi.spyOn(actual.Analytics.prototype, "group").mockImplementation( + (_params, callback) => { + callback?.(); }, ); - Analytics.prototype.on = vi.fn(); + vi.spyOn(actual.Analytics.prototype, "on").mockImplementation( + () => actual.Analytics.prototype, + ); - return { - Analytics, - }; + return actual; }); vi.mock("execa", async () => { diff --git a/packages/manager/test/__testutils__/createAPIFixture.ts b/packages/manager/test/__testutils__/createAPIFixture.ts index e6b6f427f3..8f60d3f020 100644 --- a/packages/manager/test/__testutils__/createAPIFixture.ts +++ b/packages/manager/test/__testutils__/createAPIFixture.ts @@ -11,18 +11,13 @@ type MockOptions = { body?: SerializableValue; }; -export type SerializableValueObject = { [Key in string]: SerializableValue } & { +type SerializableValueObject = { [Key in string]: SerializableValue } & { [Key in string]?: SerializableValue | undefined; }; -export type SerializableValueArray = +type SerializableValueArray = | SerializableValue[] | readonly SerializableValue[]; -export type SerializableValuePrimitive = - | string - | number - | boolean - | Date - | null; +type SerializableValuePrimitive = string | number | boolean | Date | null; export type SerializableValue = | SerializableValuePrimitive | SerializableValueObject @@ -54,7 +49,7 @@ export const createAPIFixture = (args: { const handler = rest[method]( new URL(path, apiEndpoint).toString(), async (req, res, ctx) => { - // TODO: Enable by default after fixing "Error [ERR_STREAM_PREMATURE_CLOSE]: Premature close" error. + // TODO(DT-1919): Enable by default after fixing "Error [ERR_STREAM_PREMATURE_CLOSE]: Premature close" error. if (options?.checkAuthentication) { const authenticationToken = await args.manager.user.getAuthenticationToken();