Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -7,7 +7,7 @@

import { createQueryService } from "@bufbuild/connect-query";
import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf";
import { CheckFederatedGraphRequest, CheckFederatedGraphResponse, CheckSubgraphSchemaRequest, CheckSubgraphSchemaResponse, CreateAPIKeyRequest, CreateAPIKeyResponse, CreateFederatedGraphRequest, CreateFederatedGraphResponse, CreateFederatedGraphTokenRequest, CreateFederatedGraphTokenResponse, CreateFederatedSubgraphRequest, CreateFederatedSubgraphResponse, DeleteAPIKeyRequest, DeleteAPIKeyResponse, DeleteFederatedGraphRequest, DeleteFederatedGraphResponse, DeleteFederatedSubgraphRequest, DeleteFederatedSubgraphResponse, FixSubgraphSchemaRequest, FixSubgraphSchemaResponse, GetAnalyticsViewRequest, GetAnalyticsViewResponse, GetAPIKeysRequest, GetAPIKeysResponse, GetCheckDetailsRequest, GetCheckDetailsResponse, GetChecksByFederatedGraphNameRequest, GetChecksByFederatedGraphNameResponse, GetDashboardAnalyticsViewRequest, GetDashboardAnalyticsViewResponse, GetFederatedGraphByNameRequest, GetFederatedGraphByNameResponse, GetFederatedGraphChangelogRequest, GetFederatedGraphChangelogResponse, GetFederatedGraphSDLByNameRequest, GetFederatedGraphSDLByNameResponse, GetFederatedGraphsRequest, GetFederatedGraphsResponse, GetFederatedSubgraphSDLByNameRequest, GetFederatedSubgraphSDLByNameResponse, GetOrganizationMembersRequest, GetOrganizationMembersResponse, GetSubgraphByNameRequest, GetSubgraphByNameResponse, GetSubgraphsRequest, GetSubgraphsResponse, GetTraceRequest, GetTraceResponse, InviteUserRequest, InviteUserResponse, PublishFederatedSubgraphRequest, PublishFederatedSubgraphResponse, RemoveInvitationRequest, RemoveInvitationResponse, UpdateFederatedGraphRequest, UpdateFederatedGraphResponse, UpdateSubgraphRequest, UpdateSubgraphResponse } from "./platform_pb.js";
import { CheckFederatedGraphRequest, CheckFederatedGraphResponse, CheckSubgraphSchemaRequest, CheckSubgraphSchemaResponse, CreateAPIKeyRequest, CreateAPIKeyResponse, CreateFederatedGraphRequest, CreateFederatedGraphResponse, CreateFederatedGraphTokenRequest, CreateFederatedGraphTokenResponse, CreateFederatedSubgraphRequest, CreateFederatedSubgraphResponse, DeleteAPIKeyRequest, DeleteAPIKeyResponse, DeleteFederatedGraphRequest, DeleteFederatedGraphResponse, DeleteFederatedSubgraphRequest, DeleteFederatedSubgraphResponse, FixSubgraphSchemaRequest, FixSubgraphSchemaResponse, GetAnalyticsViewRequest, GetAnalyticsViewResponse, GetAPIKeysRequest, GetAPIKeysResponse, GetCheckDetailsRequest, GetCheckDetailsResponse, GetChecksByFederatedGraphNameRequest, GetChecksByFederatedGraphNameResponse, GetDashboardAnalyticsViewRequest, GetDashboardAnalyticsViewResponse, GetFederatedGraphByNameRequest, GetFederatedGraphByNameResponse, GetFederatedGraphChangelogRequest, GetFederatedGraphChangelogResponse, GetFederatedGraphSDLByNameRequest, GetFederatedGraphSDLByNameResponse, GetFederatedGraphsRequest, GetFederatedGraphsResponse, GetFederatedSubgraphSDLByNameRequest, GetFederatedSubgraphSDLByNameResponse, GetOrganizationMembersRequest, GetOrganizationMembersResponse, GetSubgraphByNameRequest, GetSubgraphByNameResponse, GetSubgraphsRequest, GetSubgraphsResponse, GetTraceRequest, GetTraceResponse, InviteUserRequest, InviteUserResponse, MigrateFromApolloRequest, MigrateFromApolloResponse, PublishFederatedSubgraphRequest, PublishFederatedSubgraphResponse, RemoveInvitationRequest, RemoveInvitationResponse, UpdateFederatedGraphRequest, UpdateFederatedGraphResponse, UpdateSubgraphRequest, UpdateSubgraphResponse } from "./platform_pb.js";
import { GetConfigRequest, GetConfigResponse } from "../../node/v1/node_pb.js";

export const typeName = "wg.cosmo.platform.v1.PlatformService";
Expand Down Expand Up @@ -523,6 +523,25 @@ export const getLatestValidRouterConfig = createQueryService({
},
}).getLatestValidRouterConfig;

/**
* MigrateFromApollo migrates the graphs from apollo to cosmo
*
* @generated from rpc wg.cosmo.platform.v1.PlatformService.MigrateFromApollo
*/
export const migrateFromApollo = createQueryService({
service: {
methods: {
migrateFromApollo: {
name: "MigrateFromApollo",
kind: MethodKind.Unary,
I: MigrateFromApolloRequest,
O: MigrateFromApolloResponse,
},
},
typeName: "wg.cosmo.platform.v1.PlatformService",
},
}).migrateFromApollo;

/**
* @generated from rpc wg.cosmo.platform.v1.PlatformService.GetAnalyticsView
*/
Expand Down
13 changes: 12 additions & 1 deletion connect/src/wg/cosmo/platform/v1/platform_connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/* eslint-disable */
// @ts-nocheck

import { CheckFederatedGraphRequest, CheckFederatedGraphResponse, CheckSubgraphSchemaRequest, CheckSubgraphSchemaResponse, CreateAPIKeyRequest, CreateAPIKeyResponse, CreateFederatedGraphRequest, CreateFederatedGraphResponse, CreateFederatedGraphTokenRequest, CreateFederatedGraphTokenResponse, CreateFederatedSubgraphRequest, CreateFederatedSubgraphResponse, DeleteAPIKeyRequest, DeleteAPIKeyResponse, DeleteFederatedGraphRequest, DeleteFederatedGraphResponse, DeleteFederatedSubgraphRequest, DeleteFederatedSubgraphResponse, FixSubgraphSchemaRequest, FixSubgraphSchemaResponse, GetAnalyticsViewRequest, GetAnalyticsViewResponse, GetAPIKeysRequest, GetAPIKeysResponse, GetCheckDetailsRequest, GetCheckDetailsResponse, GetChecksByFederatedGraphNameRequest, GetChecksByFederatedGraphNameResponse, GetDashboardAnalyticsViewRequest, GetDashboardAnalyticsViewResponse, GetFederatedGraphByNameRequest, GetFederatedGraphByNameResponse, GetFederatedGraphChangelogRequest, GetFederatedGraphChangelogResponse, GetFederatedGraphSDLByNameRequest, GetFederatedGraphSDLByNameResponse, GetFederatedGraphsRequest, GetFederatedGraphsResponse, GetFederatedSubgraphSDLByNameRequest, GetFederatedSubgraphSDLByNameResponse, GetOrganizationMembersRequest, GetOrganizationMembersResponse, GetSubgraphByNameRequest, GetSubgraphByNameResponse, GetSubgraphsRequest, GetSubgraphsResponse, GetTraceRequest, GetTraceResponse, InviteUserRequest, InviteUserResponse, PublishFederatedSubgraphRequest, PublishFederatedSubgraphResponse, RemoveInvitationRequest, RemoveInvitationResponse, UpdateFederatedGraphRequest, UpdateFederatedGraphResponse, UpdateSubgraphRequest, UpdateSubgraphResponse } from "./platform_pb.js";
import { CheckFederatedGraphRequest, CheckFederatedGraphResponse, CheckSubgraphSchemaRequest, CheckSubgraphSchemaResponse, CreateAPIKeyRequest, CreateAPIKeyResponse, CreateFederatedGraphRequest, CreateFederatedGraphResponse, CreateFederatedGraphTokenRequest, CreateFederatedGraphTokenResponse, CreateFederatedSubgraphRequest, CreateFederatedSubgraphResponse, DeleteAPIKeyRequest, DeleteAPIKeyResponse, DeleteFederatedGraphRequest, DeleteFederatedGraphResponse, DeleteFederatedSubgraphRequest, DeleteFederatedSubgraphResponse, FixSubgraphSchemaRequest, FixSubgraphSchemaResponse, GetAnalyticsViewRequest, GetAnalyticsViewResponse, GetAPIKeysRequest, GetAPIKeysResponse, GetCheckDetailsRequest, GetCheckDetailsResponse, GetChecksByFederatedGraphNameRequest, GetChecksByFederatedGraphNameResponse, GetDashboardAnalyticsViewRequest, GetDashboardAnalyticsViewResponse, GetFederatedGraphByNameRequest, GetFederatedGraphByNameResponse, GetFederatedGraphChangelogRequest, GetFederatedGraphChangelogResponse, GetFederatedGraphSDLByNameRequest, GetFederatedGraphSDLByNameResponse, GetFederatedGraphsRequest, GetFederatedGraphsResponse, GetFederatedSubgraphSDLByNameRequest, GetFederatedSubgraphSDLByNameResponse, GetOrganizationMembersRequest, GetOrganizationMembersResponse, GetSubgraphByNameRequest, GetSubgraphByNameResponse, GetSubgraphsRequest, GetSubgraphsResponse, GetTraceRequest, GetTraceResponse, InviteUserRequest, InviteUserResponse, MigrateFromApolloRequest, MigrateFromApolloResponse, PublishFederatedSubgraphRequest, PublishFederatedSubgraphResponse, RemoveInvitationRequest, RemoveInvitationResponse, UpdateFederatedGraphRequest, UpdateFederatedGraphResponse, UpdateSubgraphRequest, UpdateSubgraphResponse } from "./platform_pb.js";
import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf";
import { GetConfigRequest, GetConfigResponse } from "../../node/v1/node_pb.js";

Expand Down Expand Up @@ -310,6 +310,17 @@ export const PlatformService = {
O: GetConfigResponse,
kind: MethodKind.Unary,
},
/**
* MigrateFromApollo migrates the graphs from apollo to cosmo
*
* @generated from rpc wg.cosmo.platform.v1.PlatformService.MigrateFromApollo
*/
migrateFromApollo: {
name: "MigrateFromApollo",
I: MigrateFromApolloRequest,
O: MigrateFromApolloResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc wg.cosmo.platform.v1.PlatformService.GetAnalyticsView
*/
Expand Down
74 changes: 74 additions & 0 deletions connect/src/wg/cosmo/platform/v1/platform_pb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3940,6 +3940,80 @@ export class RemoveInvitationResponse extends Message<RemoveInvitationResponse>
}
}

/**
* @generated from message wg.cosmo.platform.v1.MigrateFromApolloRequest
*/
export class MigrateFromApolloRequest extends Message<MigrateFromApolloRequest> {
/**
* @generated from field: string apiKey = 1;
*/
apiKey = "";

constructor(data?: PartialMessage<MigrateFromApolloRequest>) {
super();
proto3.util.initPartial(data, this);
}

static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "wg.cosmo.platform.v1.MigrateFromApolloRequest";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "apiKey", kind: "scalar", T: 9 /* ScalarType.STRING */ },
]);

static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): MigrateFromApolloRequest {
return new MigrateFromApolloRequest().fromBinary(bytes, options);
}

static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): MigrateFromApolloRequest {
return new MigrateFromApolloRequest().fromJson(jsonValue, options);
}

static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): MigrateFromApolloRequest {
return new MigrateFromApolloRequest().fromJsonString(jsonString, options);
}

static equals(a: MigrateFromApolloRequest | PlainMessage<MigrateFromApolloRequest> | undefined, b: MigrateFromApolloRequest | PlainMessage<MigrateFromApolloRequest> | undefined): boolean {
return proto3.util.equals(MigrateFromApolloRequest, a, b);
}
}

/**
* @generated from message wg.cosmo.platform.v1.MigrateFromApolloResponse
*/
export class MigrateFromApolloResponse extends Message<MigrateFromApolloResponse> {
/**
* @generated from field: wg.cosmo.platform.v1.Response response = 1;
*/
response?: Response;

constructor(data?: PartialMessage<MigrateFromApolloResponse>) {
super();
proto3.util.initPartial(data, this);
}

static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "wg.cosmo.platform.v1.MigrateFromApolloResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "response", kind: "message", T: Response },
]);

static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): MigrateFromApolloResponse {
return new MigrateFromApolloResponse().fromBinary(bytes, options);
}

static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): MigrateFromApolloResponse {
return new MigrateFromApolloResponse().fromJson(jsonValue, options);
}

static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): MigrateFromApolloResponse {
return new MigrateFromApolloResponse().fromJsonString(jsonString, options);
}

static equals(a: MigrateFromApolloResponse | PlainMessage<MigrateFromApolloResponse> | undefined, b: MigrateFromApolloResponse | PlainMessage<MigrateFromApolloResponse> | undefined): boolean {
return proto3.util.equals(MigrateFromApolloResponse, a, b);
}
}

/**
* @generated from message wg.cosmo.platform.v1.SpanAttributes
*/
Expand Down
79 changes: 79 additions & 0 deletions controlplane/src/core/bufservices/PlatformService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { TraceRepository } from '../repositories/analytics/TraceRepository.js';
import type { RouterOptions } from '../routes.js';
import { ApiKeyGenerator } from '../services/ApiGenerator.js';
import { handleError, isValidLabelMatchers, isValidLabels } from '../util.js';
import MigrateFromApollo from '../services/MigrateFromApollo.js';

export default function (opts: RouterOptions): Partial<ServiceImpl<typeof PlatformService>> {
return {
Expand Down Expand Up @@ -1721,5 +1722,83 @@ export default function (opts: RouterOptions): Partial<ServiceImpl<typeof Platfo
};
});
},

migrateFromApollo: (req, ctx) => {
const logger = opts.logger.child({
service: ctx.service.typeName,
method: ctx.method.name,
});

return handleError<PlainMessage<DeleteAPIKeyResponse>>(logger, async () => {
const authContext = await opts.authenticator.authenticate(ctx.requestHeader);
const orgRepo = new OrganizationRepository(opts.db);
const fedGraphRepo = new FederatedGraphRepository(opts.db, authContext.organizationId);
const subgraphRepo = new SubgraphRepository(opts.db, authContext.organizationId);

const org = await orgRepo.byId(authContext.organizationId);
if (!org) {
return {
response: {
code: EnumStatusCode.ERR_NOT_FOUND,
details: `Organization not found`,
},
};
}

const migrateFromApollo = new MigrateFromApollo({ apiKey: req.apiKey, organizationSlug: org.slug });

const graph = await migrateFromApollo.fetchGraphID();
const graphDetails = await migrateFromApollo.fetchGraphDetails({ graphID: graph.id, variantName: 'main' });

if (await fedGraphRepo.exists(graph.name)) {
return {
response: {
code: EnumStatusCode.ERR_ALREADY_EXISTS,
details: `Federated graph '${graph.name}' already exists.`,
},
};
}

for (const subgraph of graphDetails.subgraphs) {
if (await subgraphRepo.exists(subgraph.name)) {
return {
response: {
code: EnumStatusCode.ERR_ALREADY_EXISTS,
details: `Subgraph '${subgraph.name}' already exists`,
},
};
}
}

const federatedGraph = await fedGraphRepo.migrateGraphFromApollo({
fedGraph: {
name: graph.name,
routingURL: graphDetails.fedGraphRoutingURL,
},
subgraphs: graphDetails.subgraphs,
organizationID: authContext.organizationId,
});

const compositionErrors = await updateComposedSchema({
federatedGraph,
fedGraphRepo,
subgraphRepo,
});

if (compositionErrors.length > 0) {
return {
response: {
code: EnumStatusCode.ERR_SUBGRAPH_COMPOSITION_FAILED,
},
};
}

return {
response: {
code: EnumStatusCode.OK,
},
};
});
},
};
}
37 changes: 37 additions & 0 deletions controlplane/src/core/repositories/FederatedGraphRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
GraphApiKeyDTO,
Label,
ListFilterOptions,
MigrationSubgraph,
SchemaChangeType,
} from '../../types/index.js';
import { updateComposedSchema } from '../composition/updateComposedSchema.js';
Expand Down Expand Up @@ -517,4 +518,40 @@ export class FederatedGraphRepository {
token: key.token,
};
}

public migrateGraphFromApollo({
Comment thread
StarpTech marked this conversation as resolved.
Outdated
fedGraph,
subgraphs,
organizationID,
}: {
fedGraph: {
name: string;
routingURL: string;
};
subgraphs: MigrationSubgraph[];
organizationID: string;
}): Promise<FederatedGraphDTO> {
return this.db.transaction<FederatedGraphDTO>(async (db) => {
const fedGraphRepo = new FederatedGraphRepository(db, organizationID);
const subgraphRepo = new SubgraphRepository(db, organizationID);

const federatedGraph = await fedGraphRepo.create({
name: fedGraph.name,
labelMatchers: ['env=main'],
routingUrl: fedGraph.routingURL,
});

for (const subgraph of subgraphs) {
await subgraphRepo.create({
name: subgraph.name,
labels: [{ key: 'env', value: 'main' }],
routingUrl: subgraph.routingURL,
});

await subgraphRepo.updateSchema(subgraph.name, subgraph.schema);
}

return federatedGraph;
});
}
}
114 changes: 114 additions & 0 deletions controlplane/src/core/services/MigrateFromApollo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { MigrationSubgraph } from 'src/types/index.js';

export default class MigrateFromApollo {
Comment thread
StarpTech marked this conversation as resolved.
Outdated
apiKey = '';
organizationSlug = '';
constructor({ apiKey, organizationSlug }: { apiKey: string; organizationSlug: string }) {
this.apiKey = apiKey;
this.organizationSlug = organizationSlug;
}

public async fetchGraphID(): Promise<{ id: string; name: string }> {
const headers = new Headers();
headers.append('X-API-KEY', this.apiKey);
headers.append('apollographql-client-name', this.organizationSlug);
headers.append('apollographql-client-version', '1.0.0');
headers.append('Content-Type', 'application/json');

const graphql = JSON.stringify({
operationName: 'ListVisibleGraphs',
query: `
query ListVisibleGraphs {
me {
id
name
}
}
`,
variables: {},
});

const response = await fetch('https://graphql.api.apollographql.com/api/graphql', {
method: 'POST',
headers,
body: graphql,
});
if (response.status !== 200) {
throw new Error('Could not fetch the graph from apollo.');
}
const body = await response.json();
const data = body.data;
return {
id: data.me.id,
name: data.me.name,
};
}

// fetches the schemas of the subgraphs and the routing url of the federated graph
public async fetchGraphDetails({ graphID, variantName }: { graphID: string; variantName: string }): Promise<{
fedGraphRoutingURL: string;
subgraphs: MigrationSubgraph[];
}> {
const headers = new Headers();
headers.append('X-API-KEY', this.apiKey);
headers.append('apollographql-client-name', this.organizationSlug);
headers.append('apollographql-client-version', '1.0.0');
headers.append('Content-Type', 'application/json');

const graphql = JSON.stringify({
query: `
query GetGraph($graphId: ID!) {
graph(id: $graphId) {
id
title
variants {
id
url
name
subgraphs {
name
url
activePartialSchema {
sdl
}
}
}
}
}
`,
variables: {
graphId: graphID,
},
});

const response = await fetch('https://graphql.api.apollographql.com/api/graphql', {
method: 'POST',
headers,
body: graphql,
});
if (response.status !== 200) {
throw new Error('Could not fetch the subgraphs from apollo.');
}
const body = await response.json();
const data = body.data;
const variants: any[] = data.graph.variants;

const variant = variants.find((v: { name: string }) => v.name === variantName);

if (!variant) {
throw new Error('Could not find the requested variant of the graph.');
}
const subgraphs: any[] = variant.subgraphs;

return {
fedGraphRoutingURL: variant.url,
subgraphs: subgraphs.map((subgraph) => {
return {
name: subgraph.name,
routingURL: subgraph.url,
schema: subgraph.activePartialSchema.sdl,
} as MigrationSubgraph;
}),
};
}
}
Loading