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/event-worker/src/workers/delete-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const dispatchAffectedTargetJobs = async (
.where(
inArray(
SCHEMA.releaseTarget.resourceId,
affectedResources.map((r) => r.source.id),
affectedResources.map((r) => r.target.id),
),
);

Expand Down
2 changes: 1 addition & 1 deletion apps/event-worker/src/workers/update-resource-variable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const getResourceChildrenReleaseTargets = async (
eq(schema.deploymentVariable.key, key),
inArray(
schema.releaseTarget.resourceId,
dependentResources.map((dr) => dr.source.id),
dependentResources.map((dr) => dr.target.id),
),
),
)
Expand Down
2 changes: 1 addition & 1 deletion apps/event-worker/src/workers/updated-resources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const getAffectedReleaseTargets = async (resourceId: string) => {
and(
inArray(
schema.releaseTarget.resourceId,
resourceChildren.map((dr) => dr.source.id),
resourceChildren.map((dr) => dr.target.id),
),
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,25 +158,25 @@ export default async function VariablesPage(props: {
{(() => {
const relationship =
resource.relationships[v.reference];
if (!relationship?.target) {
if (!relationship?.source) {
return (
<span className="text-amber-500">
{v.reference} (not found)
</span>
);
}

const target = relationship.target;
const source = relationship.source;
return (
<div className="flex items-center gap-2">
<Link
href={urls
.workspace(workspaceSlug)
.resource(target.id)
.resource(source.id)
.properties()}
className="text-blue-500 underline-offset-1 hover:underline"
>
{target.name || v.reference}
{source.name || v.reference}
</Link>
<IconLink className="h-4 w-4 text-muted-foreground" />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import type * as schema from "@ctrlplane/db/schema";
import type { Dispatch, ReactNode, SetStateAction } from "react";
import type {
EdgeChange,
Expand All @@ -12,8 +13,6 @@ import { createContext, useContext, useState } from "react";
import { MarkerType, useEdgesState, useNodesState } from "reactflow";
import colors from "tailwindcss/colors";

import * as schema from "@ctrlplane/db/schema";

import type { Edge, ResourceNodeData } from "./types";
import {
getLayoutedElementsDagre,
Expand Down Expand Up @@ -57,8 +56,8 @@ const getParentResourceIds = (
currParentResourceIds: Set<string>,
): Set<string> => {
const parentResourceIds = edges
.filter((edge) => edge.sourceId === focusedResource.id)
.map((edge) => edge.targetId);
.filter((edge) => edge.targetId === focusedResource.id)
.map((edge) => edge.sourceId);

const directParentResources = resources.filter((resource) =>
parentResourceIds.includes(resource.id),
Expand All @@ -80,10 +79,10 @@ const getChildResourceIdsWithSystem = (

const children = new Map<string, string[]>();
edges.forEach((edge) => {
if (!children.has(edge.targetId)) {
children.set(edge.targetId, []);
if (!children.has(edge.sourceId)) {
children.set(edge.sourceId, []);
}
children.get(edge.targetId)!.push(edge.sourceId);
children.get(edge.sourceId)!.push(edge.targetId);
});

const hasSystems = (resourceId: string): boolean => {
Expand Down Expand Up @@ -153,17 +152,13 @@ const markerEnd = {
color: colors.neutral[800],
};

/**
* NOTE: we reverse the source and the target because for ctrlplane's logic,
* the target of the relationship is the parent, and the source is the child
*/
const getEdges = (edges: Edge[]) =>
edges.map((e) => ({
id: `${e.targetId}-${e.sourceId}`,
source: e.targetId,
target: e.sourceId,
id: `${e.sourceId}-${e.targetId}`,
source: e.sourceId,
target: e.targetId,
style: { stroke: colors.neutral[800] },
label: schema.ResourceDependencyTypeFlipped[e.relationshipType],
label: e.relationshipType,
markerEnd,
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { useCollapsibleTree } from "../CollapsibleTreeContext";

const getDirectChildrenIds = (resourceId: string, edges: Edge[]) =>
edges
.filter((edge) => edge.targetId === resourceId)
.map((edge) => edge.sourceId);
.filter((edge) => edge.sourceId === resourceId)
.map((edge) => edge.targetId);

const getAllDescendantsIds = (resourceId: string, edges: Edge[]) => {
const descendants = new Set<string>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import type { FieldArrayWithId, UseFormReturn } from "react-hook-form";
import React, { useState } from "react";
import { IconPlus, IconX } from "@tabler/icons-react";
import { capitalCase } from "change-case";
import { z } from "zod";

import * as SCHEMA from "@ctrlplane/db/schema";
Expand All @@ -15,12 +14,6 @@ import {
DialogTitle,
DialogTrigger,
} from "@ctrlplane/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@ctrlplane/ui/dropdown-menu";
import {
Form,
FormControl,
Expand Down Expand Up @@ -151,26 +144,7 @@ const DependencyTypeField: React.FC<{
<FormItem>
<FormLabel>Dependency Type</FormLabel>
<FormControl>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="w-full justify-start font-normal"
>
{capitalCase(field.value)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-full min-w-[200px]">
{Object.values(SCHEMA.ResourceDependencyType).map((type) => (
<DropdownMenuItem
key={type}
onClick={() => field.onChange(type)}
>
{capitalCase(type)}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<Input {...field} placeholder="depends on" />
</FormControl>
<FormMessage />
</FormItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import type { FieldArrayWithId, UseFormReturn } from "react-hook-form";
import React, { useState } from "react";
import { IconX } from "@tabler/icons-react";
import { capitalCase } from "change-case";
import { z } from "zod";

import * as SCHEMA from "@ctrlplane/db/schema";
Expand All @@ -15,12 +14,6 @@ import {
DialogTitle,
DialogTrigger,
} from "@ctrlplane/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@ctrlplane/ui/dropdown-menu";
import {
Form,
FormControl,
Expand Down Expand Up @@ -141,26 +134,7 @@ const DependencyTypeField: React.FC<{
<FormItem>
<FormLabel>Dependency Type</FormLabel>
<FormControl>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="w-full justify-start font-normal"
>
{capitalCase(field.value ?? "")}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-full min-w-[200px]">
{Object.values(SCHEMA.ResourceDependencyType).map((type) => (
<DropdownMenuItem
key={type}
onClick={() => field.onChange(type)}
>
{capitalCase(type)}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<Input {...field} placeholder="depends on" />
</FormControl>
<FormMessage />
</FormItem>
Expand Down
4 changes: 2 additions & 2 deletions apps/webservice/src/app/api/v1/jobs/[jobId]/get-job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ const getResourceWithMetadataAndRelationships = async (
);
const { relationships } = await getResourceParents(db, resource.id);
const resourceRelationships = Object.fromEntries(
Object.entries(relationships).map(([key, { target, ...rule }]) => [
Object.entries(relationships).map(([key, { source, ...rule }]) => [
key,
{ ...target, rule },
{ ...source, rule },
Comment on lines +150 to +152
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Breaking API change: Verify client compatibility.

The change from target to source in the relationship mapping aligns with the systematic flip, but this modifies the API response structure. Since getResourceParents returns parent relationships, using source is semantically correct.

Ensure that:

  1. API consumers (frontend, external clients) are updated to expect source instead of target
  2. API documentation reflects this change
  3. Consider API versioning if this affects external consumers

🏁 Script executed:

#!/bin/bash
# Check for other files that might consume this API response
echo "Searching for references to relationship.target in the codebase..."
rg -A 2 -B 2 "relationship\.target" apps/webservice/src/
rg -A 2 -B 2 "relationships.*target" packages/ apps/

echo "Checking API schema definitions..."
fd -e ts -e json | xargs rg "target.*relationship|relationship.*target"

Length of output: 66515


BREAKING API CHANGE: Update API clients & tests for source field

The mapping in get-job.ts now spreads source instead of target, so any consumer expecting relationship.target will break.

Please update the following:

  • e2e tests in e2e/tests/api/resource-relationships.spec.ts – change references from relationships?.[reference].target to .source.
  • Frontend code/docs that parse the job details API (apps/webservice, external clients) to use relationship.source.
  • API documentation (OpenAPI schema, markdown) to reflect the new source property.
  • Consider bumping the API version or adding a migration note if this impacts external integrations.
- Object.entries(relationships).map(([key, { target, ...rule }]) => [
-   key,
-   { ...target, rule },
+ Object.entries(relationships).map(([key, { source, ...rule }]) => [
+   key,
+   { ...source, rule },

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/webservice/src/app/api/v1/jobs/[jobId]/get-job.ts around lines 150 to
152, the code now spreads the `source` field instead of `target` in the
relationships mapping, which breaks existing consumers expecting
`relationship.target`. To fix this, update all e2e tests in
e2e/tests/api/resource-relationships.spec.ts to reference
`relationships?.[reference].source` instead of `.target`, modify frontend code
and documentation in apps/webservice and any external clients to use
`relationship.source`, update the API documentation including OpenAPI schema and
markdown files to reflect the `source` property, and consider bumping the API
version or adding a migration note for external integrations impacted by this
change.

]),
);
return { ...resource, metadata, relationships: resourceRelationships };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ export const openapi: Swagger.SwaggerV3 = {
ruleId: { type: "string" },
type: { type: "string" },
reference: { type: "string" },
target: { $ref: "#/components/schemas/Resource" },
source: { $ref: "#/components/schemas/Resource" },
},
required: ["ruleId", "type", "reference", "target"],
required: ["ruleId", "type", "reference", "source"],
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion e2e/api/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4818,7 +4818,7 @@ export interface operations {
ruleId: string;
type: string;
reference: string;
target: components["schemas"]["Resource"];
source: components["schemas"]["Resource"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Property rename aligns with coordinated relationship directionality change.

This change from target to source is consistent with the broader PR objective to flip source and target roles across the codebase. The type safety is maintained while updating the semantic meaning of the relationship direction.

Since this affects the API contract for getResourceByIdentifier, verify that all consumers of this endpoint have been updated to use the new property name:


🏁 Script executed:

#!/bin/bash
# Description: Verify all usages of the relationships property have been updated to use 'source' instead of 'target'

# Search for any remaining references to relationships.target that should be relationships.source
rg -A 3 -B 3 "relationships.*\.target" --type=ts --type=js

# Search for any references to the old target property in relationship contexts
rg -A 3 -B 3 "target.*components\[.*Resource.*\]" --type=ts

# Check for consistent usage of the new source property
rg -A 3 -B 3 "relationships.*\.source" --type=ts --type=js

Length of output: 2624


Regenerate Node SDK schema to update targetsource

The OpenAPI change flipped the relationship property in the API from target to source, but the SDK’s generated types in packages/node-sdk/src/schema.ts still define:

  • target: components["schemas"]["Resource"];

Please regenerate the SDK schema (e.g. rerun openapi-typescript against the updated spec) so that schema.ts uses:

source: components["schemas"]["Resource"];

and verify no residual target references remain.

🤖 Prompt for AI Agents
In e2e/api/schema.ts at line 4821, the SDK schema still uses the outdated
property `target` instead of the updated `source`. To fix this, regenerate the
Node SDK schema by rerunning openapi-typescript against the latest OpenAPI spec
to update the property from `target` to `source`. After regeneration, verify
that all references to `target` are removed and replaced with `source`
throughout the schema.ts file.

};
};
};
Expand Down
Loading
Loading