Skip to content

Commit

Permalink
feat: add auth project endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
gregberge committed Aug 23, 2024
1 parent 3a6ecc4 commit ca36c27
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 10 deletions.
55 changes: 55 additions & 0 deletions apps/backend/src/api/handlers/getAuthProject.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import request from "supertest";
import { beforeEach, describe, expect, it } from "vitest";

import type { Build, Project } from "@/database/models/index.js";
import { factory, setupDatabase } from "@/database/testing/index.js";

import { createTestHandlerApp } from "../test-util";
import { getAuthProject } from "./getAuthProject";

const app = createTestHandlerApp(getAuthProject);

describe("getAuthProject", () => {
let project: Project;
let builds: Build[];

beforeEach(async () => {
await setupDatabase();
project = await factory.Project.create({
token: "the-awesome-token",
});
builds = await factory.Build.createMany(3, {
projectId: project.id,
name: "default",
});
// Sort builds by id desc
builds.sort((a: Build, b: Build) => b.id.localeCompare(a.id));
});

describe("without a valid token", () => {
it("returns 401 status code", async () => {
await request(app)
.get("/project")
.set("Authorization", "Bearer invalid-token")

Check failure

Code scanning / CodeQL

Hard-coded credentials Critical

The hard-coded value "Bearer invalid-token" is used as
authorization header
.
.expect((res) => {
expect(res.text).toBe(
`Project not found in Argos. If the issue persists, verify your token. (token: "invalid-token").`,
);
})
.expect(401);
});
});

it("returns a project", async () => {
await request(app)
.get("/project")
.set("Authorization", "Bearer the-awesome-token")

Check failure

Code scanning / CodeQL

Hard-coded credentials Critical

The hard-coded value "Bearer the-awesome-token" is used as
authorization header
.
.expect(200)
.expect((res) => {
expect(res.body).toEqual({
id: project.id,
defaultBaseBranch: "main",
});
});
});
});
19 changes: 19 additions & 0 deletions apps/backend/src/api/handlers/getAuthProject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { repoAuth } from "@/web/middlewares/repoAuth.js";
import { boom } from "@/web/util.js";

import { CreateAPIHandler } from "../util.js";

export const getAuthProject: CreateAPIHandler = ({ get }) => {
return get("/project", repoAuth, async (req, res) => {
if (!req.authProject) {
throw boom(401, "Unauthorized");
}

const referenceBranch = await req.authProject.$getReferenceBranch();

res.send({
id: req.authProject.id,
defaultBaseBranch: referenceBranch,
});
});
};
2 changes: 2 additions & 0 deletions apps/backend/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import cors from "cors";
import { Router } from "express";
import { stringify } from "yaml";

import { getAuthProject } from "./handlers/getAuthProject.js";
import { getAuthProjectBuilds } from "./handlers/getAuthProjectBuilds.js";
import { schema } from "./schema.js";
import { errorHandler, registerHandler } from "./util.js";
Expand All @@ -24,6 +25,7 @@ router.get("/openapi.yaml", (_req, res) => {
});

// Register the handlers.
registerHandler(router, getAuthProject);
registerHandler(router, getAuthProjectBuilds);

// Error handlers
Expand Down
22 changes: 22 additions & 0 deletions apps/backend/src/api/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ const invalidParameters = createErrorResponse("Invalid parameters");
const unauthorized = createErrorResponse("Unauthorized");
const serverError = createErrorResponse("Server error");

const Project = z.object({
id: z.string(),
defaultBaseBranch: z.string(),
});

const Build = z
.object({
id: z.string(),
Expand Down Expand Up @@ -113,6 +118,23 @@ export const zodSchema = {
},
security: [{ tokenAuth: [] }],
paths: {
"/project": {
get: {
operationId: "getAuthProject",
responses: {
"200": {
description: "Project",
content: {
"application/json": {
schema: Project,
},
},
},
"401": unauthorized,
"500": serverError,
},
},
},
"/project/builds": {
get: {
operationId: "getAuthProjectBuilds",
Expand Down
18 changes: 8 additions & 10 deletions apps/backend/src/api/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,18 @@ export const errorHandler: ErrorRequestHandler = (

type HandlerParams<T> = (T | T[])[];

type GetAPIHandler<TPath extends Path> = (
path: TPath,
...handlers: HandlerParams<GetOperationHandler<TPath>>
) => void;
type Context = {
get<TPath extends Path>(
path: TPath,
...handlers: HandlerParams<GetOperationHandler<TPath>>
): void;
};

/**
* Register a GET handler.
*/
function get<TPath extends Path>(router: Router) {
const apiHandler: GetAPIHandler<TPath> = (path, ...handlers) => {
function get(router: Router) {
const apiHandler: Context["get"] = (path, ...handlers) => {
const operation: ZodOpenApiOperationObject = zodSchema.paths[path].get;
const wrappedHandlers = handlers.map((handler) =>
typeof handler === "function"
Expand Down Expand Up @@ -144,10 +146,6 @@ function get<TPath extends Path>(router: Router) {
return apiHandler;
}

type Context = {
get: GetAPIHandler<Path>;
};

export type CreateAPIHandler = (context: Context) => void;

/**
Expand Down

0 comments on commit ca36c27

Please sign in to comment.