export function migrateFromApollo()

in controlplane/src/core/bufservices/federated-graph/migrateFromApollo.ts [22:283]


export function migrateFromApollo(
  opts: RouterOptions,
  req: MigrateFromApolloRequest,
  ctx: HandlerContext,
): Promise<PlainMessage<MigrateFromApolloResponse>> {
  let logger = getLogger(ctx, opts.logger);

  return handleError<PlainMessage<MigrateFromApolloResponse>>(ctx, logger, async () => {
    const authContext = await opts.authenticator.authenticate(ctx.requestHeader);
    logger = enrichLogger(ctx, logger, authContext);

    const userRepo = new UserRepository(logger, opts.db);
    const orgRepo = new OrganizationRepository(logger, opts.db, opts.billingDefaultPlanId);
    const fedGraphRepo = new FederatedGraphRepository(logger, opts.db, authContext.organizationId);
    const subgraphRepo = new SubgraphRepository(logger, opts.db, authContext.organizationId);
    const orgWebhooks = new OrganizationWebhookService(
      opts.db,
      authContext.organizationId,
      opts.logger,
      opts.billingDefaultPlanId,
    );
    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 doesnt have the permissions to perform this operation`,
        },
        token: '',
      };
    }

    opts.platformWebhooks.send(PlatformEventName.APOLLO_MIGRATE_INIT, {
      actor_id: authContext.userId,
    });

    const namespace = await namespaceRepo.byName(req.namespace);
    if (!namespace) {
      return {
        response: {
          code: EnumStatusCode.ERR_NOT_FOUND,
          details: `Could not find namespace ${req.namespace}`,
        },
        token: '',
      };
    }

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

    const user = await userRepo.byId(authContext.userId || '');

    const apolloMigrator = new ApolloMigrator({
      apiKey: req.apiKey,
      organizationSlug: org.slug,
      variantName: req.variantName,
      logger,
      userEmail: user?.email || '',
      userId: user?.id || '',
    });

    const graph = await apolloMigrator.fetchGraphID();
    if (!graph.success) {
      return {
        response: {
          code: EnumStatusCode.ERR,
          details: `Could not fetch the graph from Apollo. Please ensure that the API Key is valid.`,
        },
        token: '',
      };
    }

    const graphDetails = await apolloMigrator.fetchGraphDetails({ graphID: graph.id });

    if (!graphDetails.success) {
      return {
        response: {
          code: EnumStatusCode.ERR,
          details: graphDetails.errorMessage,
        },
        token: '',
      };
    }

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

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

    await opts.db.transaction(async (tx) => {
      const fedGraphRepo = new FederatedGraphRepository(logger, tx, authContext.organizationId);

      const federatedGraph = await apolloMigrator.migrateGraphFromApollo({
        fedGraph: {
          name: graph.name,
          routingURL: graphDetails.fedGraphRoutingURL || '',
        },
        subgraphs: graphDetails.subgraphs,
        organizationID: authContext.organizationId,
        db: tx,
        creatorUserId: authContext.userId,
        namespace: req.namespace,
        namespaceId: namespace.id,
      });

      await fedGraphRepo.composeAndDeployGraphs({
        federatedGraphs: [federatedGraph],
        actorId: authContext.userId,
        blobStorage: opts.blobStorage,
        admissionConfig: {
          cdnBaseUrl: opts.cdnBaseUrl,
          webhookJWTSecret: opts.admissionWebhookJWTSecret,
        },
        chClient: opts.chClient!,
      });
    });

    const migratedGraph = await fedGraphRepo.byName(graph.name, req.namespace);
    if (!migratedGraph) {
      return {
        response: {
          code: EnumStatusCode.ERR,
          details: 'Could not complete the migration. Please try again.',
        },
        token: '',
      };
    }

    await auditLogRepo.addAuditLog({
      organizationId: authContext.organizationId,
      auditAction: 'federated_graph.created',
      action: 'created',
      actorId: authContext.userId,
      auditableType: 'federated_graph',
      auditableDisplayName: migratedGraph.name,
      actorDisplayName: authContext.userDisplayName,
      apiKeyName: authContext.apiKeyName,
      actorType: authContext.auth === 'api_key' ? 'api_key' : 'user',
      targetNamespaceId: migratedGraph.namespaceId,
      targetNamespaceDisplayName: migratedGraph.namespace,
    });

    const subgraphs = await subgraphRepo.byGraphLabelMatchers({
      labelMatchers: migratedGraph.labelMatchers,
      namespaceId: migratedGraph.namespaceId,
    });
    for (const subgraph of subgraphs) {
      await auditLogRepo.addAuditLog({
        organizationId: authContext.organizationId,
        auditAction: 'subgraph.created',
        action: 'created',
        actorId: authContext.userId,
        auditableType: 'subgraph',
        auditableDisplayName: subgraph.name,
        actorDisplayName: authContext.userDisplayName,
        apiKeyName: authContext.apiKeyName,
        actorType: authContext.auth === 'api_key' ? 'api_key' : 'user',
        targetNamespaceId: subgraph.namespaceId,
        targetNamespaceDisplayName: subgraph.namespace,
      });
    }

    orgWebhooks.send(
      {
        eventName: OrganizationEventName.FEDERATED_GRAPH_SCHEMA_UPDATED,
        payload: {
          federated_graph: {
            id: migratedGraph.id,
            name: migratedGraph.name,
            namespace: migratedGraph.namespace,
          },
          organization: {
            id: authContext.organizationId,
            slug: authContext.organizationSlug,
          },
          errors: false,
          actor_id: authContext.userId,
        },
      },
      authContext.userId,
    );

    const tokenValue = await signJwtHS256<GraphApiKeyJwtPayload>({
      secret: opts.jwtSecret,
      token: {
        iss: authContext.userId,
        federated_graph_id: migratedGraph.id,
        aud: audiences.cosmoGraphKey, // to distinguish from other tokens
        organization_id: authContext.organizationId,
      },
    });

    const token = await fedGraphRepo.createToken({
      token: tokenValue,
      federatedGraphId: migratedGraph.id,
      tokenName: migratedGraph.name,
      organizationId: authContext.organizationId,
      createdBy: authContext.userId,
    });

    await auditLogRepo.addAuditLog({
      organizationId: authContext.organizationId,
      auditAction: 'graph_token.created',
      action: 'created',
      actorId: authContext.userId,
      targetId: migratedGraph.id,
      targetDisplayName: migratedGraph.name,
      targetType: 'federated_graph',
      actorDisplayName: authContext.userDisplayName,
      apiKeyName: authContext.apiKeyName,
      actorType: authContext.auth === 'api_key' ? 'api_key' : 'user',
      auditableDisplayName: token.name,
      auditableType: 'graph_token',
      targetNamespaceId: migratedGraph.namespaceId,
      targetNamespaceDisplayName: migratedGraph.namespace,
    });

    opts.platformWebhooks.send(PlatformEventName.APOLLO_MIGRATE_SUCCESS, {
      federated_graph: {
        id: migratedGraph.id,
        name: migratedGraph.name,
      },
      actor_id: authContext.userId,
    });

    return {
      response: {
        code: EnumStatusCode.OK,
      },
      token: token.token,
    };
  });
}