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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
takeFirstOrNull,
} from "@ctrlplane/db";
import { db } from "@ctrlplane/db/client";
import { getResourceChildren } from "@ctrlplane/db/queries";
import * as schema from "@ctrlplane/db/schema";
import { dispatchQueueJob } from "@ctrlplane/events";
import { logger } from "@ctrlplane/logger";
Expand Down Expand Up @@ -68,7 +69,7 @@ const getNewlySatisfiedDependencies = async (
};

const getReleaseTargetsToEvaluate = async (
resourceId: string,
resourceIds: string[],
newlySatisfiedDependencies: schema.VersionDependency[],
) =>
db
Expand All @@ -88,7 +89,7 @@ const getReleaseTargetsToEvaluate = async (
schema.deploymentVersion.id,
newlySatisfiedDependencies.map((d) => d.versionId),
),
eq(schema.releaseTarget.resourceId, resourceId),
inArray(schema.releaseTarget.resourceId, resourceIds),
),
)
.then((rows) => rows.map((row) => row.release_target));
Expand All @@ -103,8 +104,10 @@ export const triggerDependentTargets = async (job: schema.Job) => {
const version = await getVersion(versionId);
const newlySatisfiedDependencies =
await getNewlySatisfiedDependencies(version);
const childResources = await getResourceChildren(db, resourceId);
const childResourceIds = childResources.map(({ target }) => target.id);
const releaseTargetsToEvaluate = await getReleaseTargetsToEvaluate(
resourceId,
[resourceId, ...childResourceIds],
newlySatisfiedDependencies,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,55 @@ export const openapi: Swagger.SwaggerV3 = {
},
},
},
delete: {
summary: "Delete a resource relationship rule",
operationId: "deleteResourceRelationshipRule",
parameters: [
{
name: "ruleId",
in: "path",
required: true,
schema: { type: "string", format: "uuid" },
},
],
responses: {
200: {
description: "The deleted resource relationship rule",
content: {
"application/json": {
schema: {
$ref: "#/components/schemas/ResourceRelationshipRule",
},
},
},
},
404: {
description: "The resource relationship rule was not found",
content: {
"application/json": {
schema: {
type: "object",
properties: { error: { type: "string" } },
required: ["error"],
},
},
},
},
500: {
description:
"An error occurred while deleting the resource relationship rule",
content: {
"application/json": {
schema: {
type: "object",
properties: { error: { type: "string" } },
required: ["error"],
},
},
},
},
},
},
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { NextResponse } from "next/server";
import { INTERNAL_SERVER_ERROR, NOT_FOUND } from "http-status";
import _ from "lodash";

import { eq, takeFirst } from "@ctrlplane/db";
import { eq, takeFirst, takeFirstOrNull } from "@ctrlplane/db";
import * as schema from "@ctrlplane/db/schema";
import { logger } from "@ctrlplane/logger";
import { Permission } from "@ctrlplane/validators/auth";
Expand Down Expand Up @@ -172,3 +172,46 @@ export const PATCH = request()
);
}
});

export const DELETE = request()
.use(authn)
.use(
authz(({ can, params }) =>
can.perform(Permission.ResourceRelationshipRuleDelete).on({
type: "resourceRelationshipRule",
id: params.ruleId ?? "",
}),
),
)
.handle<{ db: Tx }, { params: Promise<{ ruleId: string }> }>(
async ({ db }, { params }) => {
try {
const { ruleId } = await params;

const rule = await db
.select()
.from(schema.resourceRelationshipRule)
.where(eq(schema.resourceRelationshipRule.id, ruleId))
.then(takeFirstOrNull);
if (rule == null)
return NextResponse.json(
{ error: "Resource relationship rule not found" },
{ status: NOT_FOUND },
);

const deletedRule = await db
.delete(schema.resourceRelationshipRule)
.where(eq(schema.resourceRelationshipRule.id, ruleId))
.returning()
.then(takeFirst);

return NextResponse.json(deletedRule);
} catch (error) {
log.error(error);
return NextResponse.json(
{ error: "Failed to delete resource relationship rule" },
{ status: INTERNAL_SERVER_ERROR },
);
}
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,6 @@ export const openapi: Swagger.SwaggerV3 = {
schemas: {
ResourceRelationshipRuleDependencyType: {
type: "string",
enum: [
"depends_on",
"depends_indirectly_on",
"uses_at_runtime",
"created_after",
"provisioned_in",
"inherits_from",
],
},

MetadataEqualsConstraint: {
Expand Down
62 changes: 62 additions & 0 deletions e2e/api/entities-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,59 @@ export class EntitiesBuilder {
return results;
}

async upsertResourceRelationshipsFixtures(): Promise<FetchResultInfo[]> {
if (
!this.fixtures.resourceRelationships ||
this.fixtures.resourceRelationships.length === 0
) {
throw new Error("No resource relationships defined in YAML file");
}

const results: FetchResultInfo[] = [];
const workspaceId = this.workspace.id;

for (const relationship of this.fixtures.resourceRelationships) {
console.log(`Upserting resource relationship: ${relationship.reference}`);

const requestBody = {
...relationship,
workspaceId,
sourceKind: relationship.source.kind,
sourceVersion: relationship.source.version,
targetKind: relationship.target?.kind,
targetVersion: relationship.target?.version,
name: relationship.reference,
};
const fetchResponse = await this.api.POST(
"/v1/resource-relationship-rules",
{ body: requestBody },
);

results.push({ fetchResponse, requestBody });

this.refs.resourceRelationships.push({
id: fetchResponse.data!.id,
reference: fetchResponse.data!.reference,
dependencyType: fetchResponse.data!.dependencyType,
source: {
kind: fetchResponse.data!.sourceKind,
version: fetchResponse.data!.sourceVersion,
},
...(fetchResponse.data?.targetKind != null ||
fetchResponse.data?.targetVersion != null
? {
target: {
kind: fetchResponse.data!.targetKind,
version: fetchResponse.data!.targetVersion,
},
}
: {}),
});
}

return results;
}

async upsertEnvironmentFixtures(): Promise<FetchResultInfo[]> {
if (
!this.fixtures.environments ||
Expand Down Expand Up @@ -651,5 +704,14 @@ export async function cleanupImportedEntities(
),
});
}

for (const resourceRelationship of refs.resourceRelationships ?? []) {
results.push({
fetchResponse: await api.DELETE(
"/v1/resource-relationship-rules/{ruleId}",
{ params: { path: { ruleId: resourceRelationship.id } } },
),
});
}
return results;
}
18 changes: 18 additions & 0 deletions e2e/api/entity-fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ export const ResourceFixture = z.object({
metadata: z.record(z.string()).optional(),
});

export const ResourceRelationshipFixture = z.object({
source: z.object({
kind: z.string(),
version: z.string(),
}),
target: z
.object({
kind: z.string().optional(),
version: z.string().optional(),
})
.optional(),
dependencyType: z.string(),
dependencyDescription: z.string().optional(),
description: z.string().optional(),
reference: z.string(),
});

export const PolicyFixture = z.object({
name: z.string(),
targets: z.array(
Expand Down Expand Up @@ -151,6 +168,7 @@ export const EntityFixtures = z.object({
system: SystemFixture,
environments: z.array(EnvironmentFixture).optional(),
resources: z.array(ResourceFixture).optional(),
resourceRelationships: z.array(ResourceRelationshipFixture).optional(),
deployments: z.array(DeploymentFixture).optional(),
policies: z.array(PolicyFixture).optional(),
agents: z.array(AgentFixture).optional(),
Expand Down
34 changes: 34 additions & 0 deletions e2e/api/entity-refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ export interface ResourceRef {
metadata?: Record<string, string>;
}

export interface ResourceRelationshipRef {
id: string;
reference: string;
dependencyType: string;
source: {
kind: string;
version: string;
};
target?: {
kind?: string;
version?: string;
};
}

export interface PolicyRef {
id: string;
name: string;
Expand All @@ -80,6 +94,8 @@ export class EntityRefs {

public resources: Array<ResourceRef> = [];

public resourceRelationships: Array<ResourceRelationshipRef> = [];

public deployments: Array<DeploymentRef> = [];

public policies: Array<PolicyRef> = [];
Expand Down Expand Up @@ -121,6 +137,24 @@ export class EntityRefs {
);
}

public takeResourceRelationships(
count: number,
): Array<ResourceRelationshipRef> {
return takeRandom(this.resourceRelationships, count);
}

public oneResourceRelationship(): ResourceRelationshipRef {
return takeRandom(this.resourceRelationships, 1)[0];
}

public getResourceRelationshipByReference(
reference: string,
): ResourceRelationshipRef {
return exactlyOne(
this.resourceRelationships.filter((rel) => rel.reference === reference),
);
}

public takeDeployments(count: number): Array<DeploymentRef> {
return takeRandom(this.deployments, count);
}
Expand Down
Loading
Loading