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 @@ -89,27 +89,25 @@ export function deleteFederatedSubgraph(
});

let proposalMatchMessage: string | undefined;
let matchedEntity:
| {
proposalId: string;
proposalSubgraphId: string;
}
| undefined;
const matchedProposalEntities: {
proposalId: string;
proposalSubgraphId: string;
}[] = [];
if (namespace.enableProposals) {
const federatedGraphs = await fedGraphRepo.bySubgraphLabels({
labels: subgraph.labels,
namespaceId: namespace.id,
});
const proposalConfig = await proposalRepo.getProposalConfig({ namespaceId: namespace.id });
if (proposalConfig) {
const match = await proposalRepo.matchSchemaWithProposal({
const matches = await proposalRepo.matchSchemaWithProposals({
subgraphName: subgraph.name,
namespaceId: namespace.id,
schemaSDL: '',
routerCompatibilityVersion: getFederatedGraphRouterCompatibilityVersion(federatedGraphs),
isDeleted: true,
});
if (!match) {
if (matches.length === 0) {
if (proposalConfig.publishSeverityLevel === 'warn') {
proposalMatchMessage = `The subgraph ${subgraph.name} is not proposed to be deleted in any of the approved proposals.`;
} else {
Expand All @@ -125,7 +123,7 @@ export function deleteFederatedSubgraph(
};
}
}
matchedEntity = match;
matchedProposalEntities.push(...matches);
}
}

Expand Down Expand Up @@ -188,9 +186,45 @@ export function deleteFederatedSubgraph(
federatedGraphs: affectedFederatedGraphs,
});

// Re-fetch the federated graphs to get the updated composedSchemaVersionId
const refreshedGraphs = await Promise.all(affectedFederatedGraphs.map((g) => fedGraphRepo.byId(g.id)));
for (let i = 0; i < affectedFederatedGraphs.length; i++) {
const refreshedGraph = refreshedGraphs[i];
if (refreshedGraph) {
affectedFederatedGraphs[i] = refreshedGraph;
}
}

return { affectedFederatedGraphs, compositionErrors, deploymentErrors, compositionWarnings };
});

// if this subgraph is part of a proposal, mark the proposal subgraph as published
// and if all proposal subgraphs are published, collect proposal details for the webhook
const proposalDetailsList: {
id: string;
name: string;
namespace: string;
federatedGraphId: string;
}[] = [];

for (const matchedEntity of matchedProposalEntities) {
const { allSubgraphsPublished } = await proposalRepo.markProposalSubgraphAsPublished({
proposalSubgraphId: matchedEntity.proposalSubgraphId,
proposalId: matchedEntity.proposalId,
});
if (allSubgraphsPublished) {
const proposal = await proposalRepo.ById(matchedEntity.proposalId);
if (proposal) {
proposalDetailsList.push({
id: proposal.proposal.id,
name: proposal.proposal.name,
namespace: req.namespace,
federatedGraphId: proposal.proposal.federatedGraphId,
});
}
}
}

for (const affectedFederatedGraph of affectedFederatedGraphs) {
const hasErrors =
compositionErrors.some((error) => error.federatedGraphName === affectedFederatedGraph.name) ||
Expand All @@ -203,6 +237,7 @@ export function deleteFederatedSubgraph(
id: affectedFederatedGraph.id,
name: affectedFederatedGraph.name,
namespace: affectedFederatedGraph.namespace,
composedSchemaVersionId: affectedFederatedGraph.composedSchemaVersionId,
},
organization: {
id: authContext.organizationId,
Expand All @@ -216,44 +251,34 @@ export function deleteFederatedSubgraph(
);
}

// if this subgraph is part of a proposal, mark the proposal subgraph as published
// and if all proposal subgraphs are published, update the proposal state to PUBLISHED
if (matchedEntity) {
const { allSubgraphsPublished } = await proposalRepo.markProposalSubgraphAsPublished({
proposalSubgraphId: matchedEntity.proposalSubgraphId,
proposalId: matchedEntity.proposalId,
});
if (allSubgraphsPublished) {
const proposal = await proposalRepo.ById(matchedEntity.proposalId);
if (proposal) {
const federatedGraph = await fedGraphRepo.byId(proposal.proposal.federatedGraphId);
if (federatedGraph) {
orgWebhooks.send(
{
eventName: OrganizationEventName.PROPOSAL_STATE_UPDATED,
payload: {
federated_graph: {
id: federatedGraph.id,
name: federatedGraph.name,
namespace: federatedGraph.namespace,
},
organization: {
id: authContext.organizationId,
slug: authContext.organizationSlug,
},
proposal: {
id: proposal.proposal.id,
name: proposal.proposal.name,
namespace: req.namespace,
state: 'PUBLISHED',
},
actor_id: authContext.userId,
},
// Send PROPOSAL_STATE_UPDATED webhook for each published proposal
for (const proposalDetails of proposalDetailsList) {
const federatedGraph = affectedFederatedGraphs.find((g) => g.id === proposalDetails.federatedGraphId);
if (federatedGraph) {
orgWebhooks.send(
{
eventName: OrganizationEventName.PROPOSAL_STATE_UPDATED,
payload: {
federated_graph: {
id: federatedGraph.id,
name: federatedGraph.name,
namespace: federatedGraph.namespace,
},
authContext.userId,
);
}
}
organization: {
id: authContext.organizationId,
slug: authContext.organizationSlug,
},
proposal: {
id: proposalDetails.id,
name: proposalDetails.name,
namespace: proposalDetails.namespace,
state: 'PUBLISHED',
},
actor_id: authContext.userId,
},
},
authContext.userId,
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,25 +125,23 @@ export function publishFederatedSubgraph(
}

let proposalMatchMessage: string | undefined;
let matchedEntity:
| {
proposalId: string;
proposalSubgraphId: string;
}
| undefined;
const matchedProposalEntities: {
proposalId: string;
proposalSubgraphId: string;
}[] = [];

// if the subgraph is a feature subgraph, we don't need to check for proposal matches for now.
if (namespace.enableProposals && !subgraph?.isFeatureSubgraph) {
const proposalConfig = await proposalRepo.getProposalConfig({ namespaceId: namespace.id });
if (proposalConfig) {
const match = await proposalRepo.matchSchemaWithProposal({
const matches = await proposalRepo.matchSchemaWithProposals({
subgraphName: req.name,
namespaceId: namespace.id,
schemaSDL: subgraphSchemaSDL,
routerCompatibilityVersion,
isDeleted: false,
});
if (!match) {
if (matches.length === 0) {
const message = `The subgraph ${req.name}'s schema does not match to this subgraph's schema in any approved proposal.`;
if (proposalConfig.publishSeverityLevel === 'warn') {
proposalMatchMessage = message;
Expand All @@ -160,7 +158,7 @@ export function publishFederatedSubgraph(
};
}
}
matchedEntity = match;
matchedProposalEntities.push(...matches);
}
}

Expand Down Expand Up @@ -577,6 +575,33 @@ export function publishFederatedSubgraph(
newCompositionOptions(req.disableResolvabilityValidation),
);

// if this subgraph is part of a proposal, mark the proposal subgraph as published
// and if all proposal subgraphs are published, collect proposal details for the webhook
const proposalDetailsList: {
id: string;
name: string;
namespace: string;
federatedGraphId: string;
}[] = [];

for (const matchedEntity of matchedProposalEntities) {
const { allSubgraphsPublished } = await proposalRepo.markProposalSubgraphAsPublished({
proposalSubgraphId: matchedEntity.proposalSubgraphId,
proposalId: matchedEntity.proposalId,
});
if (allSubgraphsPublished) {
const proposal = await proposalRepo.ById(matchedEntity.proposalId);
if (proposal) {
proposalDetailsList.push({
id: proposal.proposal.id,
name: proposal.proposal.name,
namespace: req.namespace,
federatedGraphId: proposal.proposal.federatedGraphId,
});
}
}
}

for (const graph of updatedFederatedGraphs) {
const hasErrors =
compositionErrors.some((error) => error.federatedGraphName === graph.name) ||
Expand All @@ -589,6 +614,7 @@ export function publishFederatedSubgraph(
id: graph.id,
name: graph.name,
namespace: graph.namespace,
composedSchemaVersionId: graph.composedSchemaVersionId,
},
organization: {
id: authContext.organizationId,
Expand All @@ -602,44 +628,34 @@ export function publishFederatedSubgraph(
);
}

// if this subgraph is part of a proposal, mark the proposal subgraph as published
// and if all proposal subgraphs are published, update the proposal state to PUBLISHED
if (matchedEntity) {
const { allSubgraphsPublished } = await proposalRepo.markProposalSubgraphAsPublished({
proposalSubgraphId: matchedEntity.proposalSubgraphId,
proposalId: matchedEntity.proposalId,
});
if (allSubgraphsPublished) {
const proposal = await proposalRepo.ById(matchedEntity.proposalId);
if (proposal) {
const federatedGraph = await fedGraphRepo.byId(proposal.proposal.federatedGraphId);
if (federatedGraph) {
orgWebhooks.send(
{
eventName: OrganizationEventName.PROPOSAL_STATE_UPDATED,
payload: {
federated_graph: {
id: federatedGraph.id,
name: federatedGraph.name,
namespace: federatedGraph.namespace,
},
organization: {
id: authContext.organizationId,
slug: authContext.organizationSlug,
},
proposal: {
id: proposal.proposal.id,
name: proposal.proposal.name,
namespace: req.namespace,
state: 'PUBLISHED',
},
actor_id: authContext.userId,
},
// Send PROPOSAL_STATE_UPDATED webhook for each published proposal
for (const proposalDetails of proposalDetailsList) {
const federatedGraph = updatedFederatedGraphs.find((g) => g.id === proposalDetails.federatedGraphId);
if (federatedGraph) {
orgWebhooks.send(
{
eventName: OrganizationEventName.PROPOSAL_STATE_UPDATED,
payload: {
federated_graph: {
id: federatedGraph.id,
name: federatedGraph.name,
namespace: federatedGraph.namespace,
},
authContext.userId,
);
}
}
organization: {
id: authContext.organizationId,
slug: authContext.organizationSlug,
},
proposal: {
id: proposalDetails.id,
name: proposalDetails.name,
namespace: proposalDetails.namespace,
state: 'PUBLISHED',
},
actor_id: authContext.userId,
},
},
authContext.userId,
);
}
}

Expand Down
17 changes: 10 additions & 7 deletions controlplane/src/core/repositories/ProposalRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ export class ProposalRepository {
return proposalSubgraphs;
}

public async matchSchemaWithProposal({
public async matchSchemaWithProposals({
subgraphName,
namespaceId,
schemaCheckId,
Expand All @@ -494,12 +494,14 @@ export class ProposalRepository {
schemaSDL: string;
routerCompatibilityVersion: string;
isDeleted: boolean;
}): Promise<{ proposalId: string; proposalSubgraphId: string } | undefined> {
}): Promise<{ proposalId: string; proposalSubgraphId: string }[]> {
const proposalSubgraphs = await this.getApprovedProposalSubgraphsBySubgraph({
subgraphName,
namespaceId,
});

const matches: { proposalId: string; proposalSubgraphId: string }[] = [];

for (const proposalSubgraph of proposalSubgraphs) {
if (proposalSubgraph.isDeleted && isDeleted) {
if (schemaCheckId) {
Expand All @@ -517,10 +519,11 @@ export class ProposalRepository {
},
});
}
return {
matches.push({
proposalId: proposalSubgraph.proposalId,
proposalSubgraphId: proposalSubgraph.id,
};
});
continue;
}

if (!proposalSubgraph.proposedSchemaSDL) {
Expand Down Expand Up @@ -549,13 +552,13 @@ export class ProposalRepository {
}

if (schemaChanges.changes.length === 0) {
return {
matches.push({
proposalId: proposalSubgraph.proposalId,
proposalSubgraphId: proposalSubgraph.id,
};
});
}
}
return undefined;
return matches;
}

public async getLatestCheckForProposal(
Expand Down
Loading
Loading