in controlplane/src/core/bufservices/subgraph/createFederatedSubgraph.ts [23:254]
export function createFederatedSubgraph(
opts: RouterOptions,
req: CreateFederatedSubgraphRequest,
ctx: HandlerContext,
): Promise<PlainMessage<CreateFederatedSubgraphResponse>> {
let logger = getLogger(ctx, opts.logger);
return handleError<PlainMessage<CreateFederatedSubgraphResponse>>(ctx, logger, async () => {
const authContext = await opts.authenticator.authenticate(ctx.requestHeader);
logger = enrichLogger(ctx, logger, authContext);
const subgraphRepo = new SubgraphRepository(logger, opts.db, authContext.organizationId);
const auditLogRepo = new AuditLogRepository(opts.db);
const namespaceRepo = new NamespaceRepository(opts.db, authContext.organizationId);
req.namespace = req.namespace || DefaultNamespace;
if (!authContext.hasWriteAccess) {
return {
response: {
code: EnumStatusCode.ERR,
details: `The user does not have the permissions to perform this operation`,
},
compositionErrors: [],
admissionErrors: [],
};
}
if (!isValidLabels(req.labels)) {
return {
response: {
code: EnumStatusCode.ERR_INVALID_LABELS,
details: `One or more labels were found to be invalid`,
},
compositionErrors: [],
admissionErrors: [],
};
}
/* Routing URL is now optional; if empty or undefined, set an empty string
* The routing URL must be defined unless the subgraph is an Event-Driven Graph
* */
const routingUrl = req.routingUrl || '';
if (req.isEventDrivenGraph) {
if (req.routingUrl !== undefined) {
return {
response: {
code: EnumStatusCode.ERR,
details: `An Event-Driven Graph must not define a routing URL`,
},
compositionErrors: [],
admissionErrors: [],
};
}
if (req.subscriptionUrl !== undefined) {
return {
response: {
code: EnumStatusCode.ERR,
details: `An Event-Driven Graph must not define a subscription URL`,
},
compositionErrors: [],
admissionErrors: [],
};
}
if (req.subscriptionProtocol !== undefined) {
return {
response: {
code: EnumStatusCode.ERR,
details: `An Event-Driven Graph must not define a subscription protocol`,
},
compositionErrors: [],
admissionErrors: [],
};
}
if (req.websocketSubprotocol !== undefined) {
return {
response: {
code: EnumStatusCode.ERR,
details: `An Event-Driven Graph must not define a websocket subprotocol`,
},
compositionErrors: [],
admissionErrors: [],
};
}
} else {
if (!routingUrl) {
return {
response: {
code: EnumStatusCode.ERR,
details: `A non-Event-Driven Graph must define a routing URL`,
},
compositionErrors: [],
admissionErrors: [],
};
}
if (!isValidUrl(routingUrl)) {
return {
response: {
code: EnumStatusCode.ERR,
details: `Routing URL "${routingUrl}" is not a valid URL`,
},
compositionErrors: [],
admissionErrors: [],
};
}
if (req.subscriptionUrl && !isValidUrl(req.subscriptionUrl)) {
return {
response: {
code: EnumStatusCode.ERR,
details: `Subscription URL "${req.subscriptionUrl}" is not a valid URL`,
},
compositionErrors: [],
admissionErrors: [],
};
}
}
const namespace = await namespaceRepo.byName(req.namespace);
if (!namespace) {
return {
response: {
code: EnumStatusCode.ERR_NOT_FOUND,
details: `Could not find namespace ${req.namespace}`,
},
graphs: [],
};
}
const existingSubgraph = await subgraphRepo.byName(req.name, req.namespace);
if (existingSubgraph) {
return {
response: {
code: EnumStatusCode.ERR_ALREADY_EXISTS,
details:
`A ${existingSubgraph.isFeatureSubgraph ? 'feature ' : ''}subgraph with the name` +
` "${req.name}" already exists in the namespace "${req.namespace}".`,
},
compositionErrors: [],
admissionErrors: [],
};
}
if (!isValidGraphName(req.name)) {
return {
response: {
code: EnumStatusCode.ERR_INVALID_NAME,
details: `The name of the subgraph is invalid. Name should start and end with an alphanumeric character. Only '.', '_', '@', '/', and '-' are allowed as separators in between and must be between 1 and 100 characters in length.`,
},
compositionErrors: [],
deploymentErrors: [],
compositionWarnings: [],
};
}
let baseSubgraphID = '';
if (req.isFeatureSubgraph) {
if (!req.baseSubgraphName) {
return {
response: {
code: EnumStatusCode.ERR,
details: `A feature subgraph requires a base subgraph.`,
},
compositionErrors: [],
admissionErrors: [],
};
}
const baseSubgraph = await subgraphRepo.byName(req.baseSubgraphName, req.namespace);
if (!baseSubgraph) {
return {
response: {
code: EnumStatusCode.ERR,
details: `Base subgraph "${req.baseSubgraphName}" does not exist in the namespace "${req.namespace}".`,
},
compositionErrors: [],
admissionErrors: [],
};
}
baseSubgraphID = baseSubgraph.id;
}
const subgraph = await subgraphRepo.create({
name: req.name,
namespace: req.namespace,
namespaceId: namespace.id,
createdBy: authContext.userId,
labels: req.labels,
routingUrl,
isEventDrivenGraph: req.isEventDrivenGraph || false,
readme: req.readme,
subscriptionUrl: req.subscriptionUrl,
subscriptionProtocol:
req.subscriptionProtocol === undefined ? undefined : formatSubscriptionProtocol(req.subscriptionProtocol),
websocketSubprotocol:
req.websocketSubprotocol === undefined ? undefined : formatWebsocketSubprotocol(req.websocketSubprotocol),
featureSubgraphOptions: req.isFeatureSubgraph
? {
isFeatureSubgraph: req.isFeatureSubgraph || false,
baseSubgraphID,
}
: undefined,
});
if (!subgraph) {
return {
response: {
code: EnumStatusCode.ERR,
details: `The ${req.isFeatureSubgraph ? 'feature' : ''} subgraph "${req.name}" could not be created.`,
},
};
}
await auditLogRepo.addAuditLog({
organizationId: authContext.organizationId,
auditAction: req.isFeatureSubgraph ? 'feature_subgraph.created' : 'subgraph.created',
action: 'created',
actorId: authContext.userId,
auditableType: req.isFeatureSubgraph ? 'feature_subgraph' : 'subgraph',
auditableDisplayName: subgraph.name,
actorDisplayName: authContext.userDisplayName,
apiKeyName: authContext.apiKeyName,
actorType: authContext.auth === 'api_key' ? 'api_key' : 'user',
targetNamespaceId: subgraph.namespaceId,
targetNamespaceDisplayName: subgraph.namespace,
});
return {
response: {
code: EnumStatusCode.OK,
},
};
});
}