Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/webservice/src/app/api/openapi/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "swagger-ui-react/swagger-ui.css";
import { SwaggerUI } from "./SwagerUI";

export default function OpenApiPage() {
const openApiPath = path.join(process.cwd(), "../../openapi.v1.yaml");
const openApiPath = path.join(process.cwd(), "../../openapi.v1.json");
const openApiSpec = fs.readFileSync(openApiPath, "utf8");

return (
Expand Down
133 changes: 70 additions & 63 deletions apps/webservice/src/app/api/v1/jobs/[jobId]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import {
} from "@ctrlplane/db/schema";
import { onJobCompletion } from "@ctrlplane/job-dispatch";
import { variablesAES256 } from "@ctrlplane/secrets";
import { Permission } from "@ctrlplane/validators/auth";
import { JobStatus } from "@ctrlplane/validators/jobs";

import { getUser } from "~/app/api/v1/auth";
import { authn, authz } from "~/app/api/v1/auth";
import { request } from "~/app/api/v1/middleware";

type ApprovalJoinResult = {
environment_policy_approval: typeof environmentPolicyApproval.$inferSelect;
Expand Down Expand Up @@ -58,74 +60,79 @@ const mapApprovalResponse = (row: ApprovalJoinResult | null) =>
: null,
};

export const GET = async (
req: NextRequest,
{ params }: { params: { jobId: string } },
) => {
const caller = await getUser(req);
if (!caller)
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

const je = await db
.select()
.from(job)
.leftJoin(runbookJobTrigger, eq(runbookJobTrigger.jobId, job.id))
.leftJoin(runbook, eq(runbookJobTrigger.runbookId, runbook.id))
.leftJoin(releaseJobTrigger, eq(releaseJobTrigger.jobId, job.id))
.leftJoin(environment, eq(environment.id, releaseJobTrigger.environmentId))
.leftJoin(target, eq(target.id, releaseJobTrigger.targetId))
.leftJoin(release, eq(release.id, releaseJobTrigger.releaseId))
.leftJoin(deployment, eq(deployment.id, release.deploymentId))
.where(eq(job.id, params.jobId))
.then(takeFirst)
.then((row) => ({
job: row.job,
runbook: row.runbook,
environment: row.environment,
target: row.target,
deployment: row.deployment,
release: row.release,
}));

const policyId = je.environment?.policyId;

const approval =
je.release?.id && policyId
? await getApprovalDetails(je.release.id, policyId)
: null;

const jobVariableRows = await db
.select()
.from(jobVariable)
.where(eq(jobVariable.jobId, params.jobId));

const variables = Object.fromEntries(
jobVariableRows.map((v) => {
const strval = String(v.value);
const value = v.sensitive ? variablesAES256().decrypt(strval) : strval;
return [v.key, value];
export const GET = request()
.use(authn)
.use(
authz(({ can, extra }) => {
return can
.perform(Permission.JobGet)
.on({ type: "job", id: extra.params.jobId });
}),
);
)
.handle(async ({ db }, { params }: { params: { jobId: string } }) => {
const je = await db
.select()
.from(job)
.leftJoin(runbookJobTrigger, eq(runbookJobTrigger.jobId, job.id))
.leftJoin(runbook, eq(runbookJobTrigger.runbookId, runbook.id))
.leftJoin(releaseJobTrigger, eq(releaseJobTrigger.jobId, job.id))
.leftJoin(
environment,
eq(environment.id, releaseJobTrigger.environmentId),
)
.leftJoin(target, eq(target.id, releaseJobTrigger.targetId))
.leftJoin(release, eq(release.id, releaseJobTrigger.releaseId))
.leftJoin(deployment, eq(deployment.id, release.deploymentId))
.where(eq(job.id, params.jobId))
.then(takeFirst)
.then((row) => ({
job: row.job,
runbook: row.runbook,
environment: row.environment,
target: row.target,
deployment: row.deployment,
release: row.release,
}));

const policyId = je.environment?.policyId;

const approval =
je.release?.id && policyId
? await getApprovalDetails(je.release.id, policyId)
: null;

const jobVariableRows = await db
.select()
.from(jobVariable)
.where(eq(jobVariable.jobId, params.jobId));

const variables = Object.fromEntries(
jobVariableRows.map((v) => {
const strval = String(v.value);
const value = v.sensitive ? variablesAES256().decrypt(strval) : strval;
return [v.key, value];
}),
);

const jobTargetMetadataRows = await db
.select()
.from(targetMetadata)
.where(eq(targetMetadata.targetId, je.target?.id ?? ""));
const jobTargetMetadataRows = await db
.select()
.from(targetMetadata)
.where(eq(targetMetadata.targetId, je.target?.id ?? ""));

const metadata = Object.fromEntries(
jobTargetMetadataRows.map((m) => [m.key, m.value]),
);
const metadata = Object.fromEntries(
jobTargetMetadataRows.map((m) => [m.key, m.value]),
);

const targetWithMetadata = { ...je.target, metadata };
const targetWithMetadata = { ...je.target, metadata };

return NextResponse.json({
...je.job,
...je,
variables,
target: targetWithMetadata,
approval,
return NextResponse.json({
...je.job,
...je,
variables,
target: targetWithMetadata,
approval,
});
});
};

const bodySchema = updateJob;

Expand Down
Loading
Loading