From 8f056a1c961c545f94624d081953a422d577f388 Mon Sep 17 00:00:00 2001 From: JivusAyrus Date: Wed, 23 Aug 2023 19:13:54 +0530 Subject: [PATCH 1/4] feat: add resend invitation and remove member/invitation functionality --- .../platform-PlatformService_connectquery.ts | 21 +- .../wg/cosmo/platform/v1/platform_connect.ts | 13 +- .../src/wg/cosmo/platform/v1/platform_pb.ts | 74 + .../migrations/0038_lame_abomination.sql | 12 + .../migrations/meta/0038_snapshot.json | 1218 +++++++++++++++++ controlplane/migrations/meta/_journal.json | 7 + .../src/core/bufservices/PlatformService.ts | 128 +- controlplane/src/core/build-server.ts | 12 + controlplane/src/core/controllers/auth.ts | 2 +- .../repositories/OrganizationRepository.ts | 75 +- .../src/core/repositories/UserRepository.ts | 6 +- controlplane/src/core/routes.ts | 2 + controlplane/src/core/test-util.ts | 2 +- controlplane/src/db/schema.ts | 23 +- controlplane/test/api-keys.test.ts | 28 +- controlplane/test/apollo-federation.test.ts | 26 +- controlplane/test/authenthication.test.ts | 3 +- .../test/check-federated-graph.test.ts | 28 +- .../test/check-subgraph-schema.test.ts | 78 +- .../test/compose-federationv1-graphs.test.ts | 28 +- .../test/compose-federationv2-graphs.test.ts | 28 +- controlplane/test/composition-errors.test.ts | 28 +- .../test/delete-federated-graph.test.ts | 28 +- controlplane/test/delete-subgraph.test.ts | 26 +- controlplane/test/federated-graph.test.ts | 126 +- controlplane/test/label.test.ts | 76 +- controlplane/test/router-config.test.ts | 51 +- controlplane/test/subgraph.test.ts | 71 +- proto/wg/cosmo/platform/v1/platform.proto | 10 + studio/src/components/subgraphs-table.tsx | 7 +- studio/src/components/ui/use-toast.ts | 5 + .../src/pages/[organizationSlug]/members.tsx | 141 +- 32 files changed, 2172 insertions(+), 211 deletions(-) create mode 100644 controlplane/migrations/0038_lame_abomination.sql create mode 100644 controlplane/migrations/meta/0038_snapshot.json diff --git a/connect/src/wg/cosmo/platform/v1/platform-PlatformService_connectquery.ts b/connect/src/wg/cosmo/platform/v1/platform-PlatformService_connectquery.ts index 926daf42b5..d879ac611c 100644 --- a/connect/src/wg/cosmo/platform/v1/platform-PlatformService_connectquery.ts +++ b/connect/src/wg/cosmo/platform/v1/platform-PlatformService_connectquery.ts @@ -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, 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, 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"; @@ -485,6 +485,25 @@ export const deleteAPIKey = createQueryService({ }, }).deleteAPIKey; +/** + * RemoveOrganizationMember removes the user from the organization + * + * @generated from rpc wg.cosmo.platform.v1.PlatformService.RemoveInvitation + */ +export const removeInvitation = createQueryService({ + service: { + methods: { + removeInvitation: { + name: "RemoveInvitation", + kind: MethodKind.Unary, + I: RemoveInvitationRequest, + O: RemoveInvitationResponse, + }, + }, + typeName: "wg.cosmo.platform.v1.PlatformService", + }, +}).removeInvitation; + /** * GetLatestValidRouterConfig returns the router config for the federated graph * diff --git a/connect/src/wg/cosmo/platform/v1/platform_connect.ts b/connect/src/wg/cosmo/platform/v1/platform_connect.ts index 5659637bff..84aa47b8b7 100644 --- a/connect/src/wg/cosmo/platform/v1/platform_connect.ts +++ b/connect/src/wg/cosmo/platform/v1/platform_connect.ts @@ -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, 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, 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"; @@ -288,6 +288,17 @@ export const PlatformService = { O: DeleteAPIKeyResponse, kind: MethodKind.Unary, }, + /** + * RemoveOrganizationMember removes the user from the organization + * + * @generated from rpc wg.cosmo.platform.v1.PlatformService.RemoveInvitation + */ + removeInvitation: { + name: "RemoveInvitation", + I: RemoveInvitationRequest, + O: RemoveInvitationResponse, + kind: MethodKind.Unary, + }, /** * GetLatestValidRouterConfig returns the router config for the federated graph * diff --git a/connect/src/wg/cosmo/platform/v1/platform_pb.ts b/connect/src/wg/cosmo/platform/v1/platform_pb.ts index 1ce648d23e..6f498df90c 100644 --- a/connect/src/wg/cosmo/platform/v1/platform_pb.ts +++ b/connect/src/wg/cosmo/platform/v1/platform_pb.ts @@ -3866,6 +3866,80 @@ export class DeleteAPIKeyResponse extends Message { } } +/** + * @generated from message wg.cosmo.platform.v1.RemoveInvitationRequest + */ +export class RemoveInvitationRequest extends Message { + /** + * @generated from field: string email = 1; + */ + email = ""; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "wg.cosmo.platform.v1.RemoveInvitationRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "email", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): RemoveInvitationRequest { + return new RemoveInvitationRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): RemoveInvitationRequest { + return new RemoveInvitationRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): RemoveInvitationRequest { + return new RemoveInvitationRequest().fromJsonString(jsonString, options); + } + + static equals(a: RemoveInvitationRequest | PlainMessage | undefined, b: RemoveInvitationRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(RemoveInvitationRequest, a, b); + } +} + +/** + * @generated from message wg.cosmo.platform.v1.RemoveInvitationResponse + */ +export class RemoveInvitationResponse extends Message { + /** + * @generated from field: wg.cosmo.platform.v1.Response response = 1; + */ + response?: Response; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "wg.cosmo.platform.v1.RemoveInvitationResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "response", kind: "message", T: Response }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): RemoveInvitationResponse { + return new RemoveInvitationResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): RemoveInvitationResponse { + return new RemoveInvitationResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): RemoveInvitationResponse { + return new RemoveInvitationResponse().fromJsonString(jsonString, options); + } + + static equals(a: RemoveInvitationResponse | PlainMessage | undefined, b: RemoveInvitationResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(RemoveInvitationResponse, a, b); + } +} + /** * @generated from message wg.cosmo.platform.v1.SpanAttributes */ diff --git a/controlplane/migrations/0038_lame_abomination.sql b/controlplane/migrations/0038_lame_abomination.sql new file mode 100644 index 0000000000..31ce8ea250 --- /dev/null +++ b/controlplane/migrations/0038_lame_abomination.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS "organization_member_roles" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "organization_member_id" uuid NOT NULL, + "role" "member_role" NOT NULL +); +--> statement-breakpoint +DROP TABLE "member_roles";--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "organization_member_roles" ADD CONSTRAINT "organization_member_roles_organization_member_id_organization_members_id_fk" FOREIGN KEY ("organization_member_id") REFERENCES "organization_members"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/controlplane/migrations/meta/0038_snapshot.json b/controlplane/migrations/meta/0038_snapshot.json new file mode 100644 index 0000000000..94af6faa01 --- /dev/null +++ b/controlplane/migrations/meta/0038_snapshot.json @@ -0,0 +1,1218 @@ +{ + "version": "5", + "dialect": "pg", + "id": "2e51adc7-dc51-4f48-9803-d357f00ca94f", + "prevId": "fe627a51-d14c-49e3-9eae-3622e2dd4182", + "tables": { + "api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "apikey_name_idx": { + "name": "apikey_name_idx", + "columns": [ + "name", + "organization_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "api_keys_user_id_users_id_fk": { + "name": "api_keys_user_id_users_id_fk", + "tableFrom": "api_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "api_keys_organization_id_organizations_id_fk": { + "name": "api_keys_organization_id_organizations_id_fk", + "tableFrom": "api_keys", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + } + }, + "federated_graphs": { + "name": "federated_graphs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "routing_url": { + "name": "routing_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "composed_schema_version_id": { + "name": "composed_schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "federated_graphs_target_id_targets_id_fk": { + "name": "federated_graphs_target_id_targets_id_fk", + "tableFrom": "federated_graphs", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "federated_graphs_composed_schema_version_id_schema_versions_id_fk": { + "name": "federated_graphs_composed_schema_version_id_schema_versions_id_fk", + "tableFrom": "federated_graphs", + "tableTo": "schema_versions", + "columnsFrom": [ + "composed_schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "graph_api_tokens": { + "name": "graph_api_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "graph_api_tokens_organization_id_organizations_id_fk": { + "name": "graph_api_tokens_organization_id_organizations_id_fk", + "tableFrom": "graph_api_tokens", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "graph_api_tokens_federated_graph_id_federated_graphs_id_fk": { + "name": "graph_api_tokens_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "graph_api_tokens", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "graph_api_tokens_token_unique": { + "name": "graph_api_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + } + }, + "organization_member_roles": { + "name": "organization_member_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_member_id": { + "name": "organization_member_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "member_role", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_member_roles_organization_member_id_organization_members_id_fk": { + "name": "organization_member_roles_organization_member_id_organization_members_id_fk", + "tableFrom": "organization_member_roles", + "tableTo": "organization_members", + "columnsFrom": [ + "organization_member_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code": { + "name": "invite_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organizations_slug_unique": { + "name": "organizations_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + } + }, + "organization_members": { + "name": "organization_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "accepted_invite": { + "name": "accepted_invite", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_member_idx": { + "name": "organization_member_idx", + "columns": [ + "id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "organization_members_user_id_users_id_fk": { + "name": "organization_members_user_id_users_id_fk", + "tableFrom": "organization_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_members_organization_id_organizations_id_fk": { + "name": "organization_members_organization_id_organizations_id_fk", + "tableFrom": "organization_members", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "schema_check_change_action": { + "name": "schema_check_change_action", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schema_check_id": { + "name": "schema_check_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "change_type": { + "name": "change_type", + "type": "schema_change_type", + "primaryKey": false, + "notNull": false + }, + "change_message": { + "name": "change_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_breaking": { + "name": "is_breaking", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "schema_check_change_action_schema_check_id_schema_checks_id_fk": { + "name": "schema_check_change_action_schema_check_id_schema_checks_id_fk", + "tableFrom": "schema_check_change_action", + "tableTo": "schema_checks", + "columnsFrom": [ + "schema_check_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "schema_check_composition": { + "name": "schema_check_composition", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schema_check_id": { + "name": "schema_check_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "composition_errors": { + "name": "composition_errors", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composed_schema_sdl": { + "name": "composed_schema_sdl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "schema_check_composition_schema_check_id_schema_checks_id_fk": { + "name": "schema_check_composition_schema_check_id_schema_checks_id_fk", + "tableFrom": "schema_check_composition", + "tableTo": "schema_checks", + "columnsFrom": [ + "schema_check_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "schema_check_composition_target_id_targets_id_fk": { + "name": "schema_check_composition_target_id_targets_id_fk", + "tableFrom": "schema_check_composition", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "schema_checks": { + "name": "schema_checks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "is_composable": { + "name": "is_composable", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "has_breaking_changes": { + "name": "has_breaking_changes", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "proposed_subgraph_schema_sdl": { + "name": "proposed_subgraph_schema_sdl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "schema_checks_target_id_targets_id_fk": { + "name": "schema_checks_target_id_targets_id_fk", + "tableFrom": "schema_checks", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "schema_versions": { + "name": "schema_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "schema_sdl": { + "name": "schema_sdl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_composable": { + "name": "is_composable", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "composition_errors": { + "name": "composition_errors", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "routerConfig": { + "name": "routerConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "schema_versions_target_id_targets_id_fk": { + "name": "schema_versions_target_id_targets_id_fk", + "tableFrom": "schema_versions", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "schema_version_change_action": { + "name": "schema_version_change_action", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "schema_version_id": { + "name": "schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "change_type": { + "name": "change_type", + "type": "schema_change_type", + "primaryKey": false, + "notNull": true + }, + "change_message": { + "name": "change_message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "schema_version_change_action_schema_version_id_schema_versions_id_fk": { + "name": "schema_version_change_action_schema_version_id_schema_versions_id_fk", + "tableFrom": "schema_version_change_action", + "tableTo": "schema_versions", + "columnsFrom": [ + "schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sessions_user_id_unique": { + "name": "sessions_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + } + }, + "subgraphs": { + "name": "subgraphs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "routing_url": { + "name": "routing_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema_version_id": { + "name": "schema_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "subgraphs_schema_version_id_schema_versions_id_fk": { + "name": "subgraphs_schema_version_id_schema_versions_id_fk", + "tableFrom": "subgraphs", + "tableTo": "schema_versions", + "columnsFrom": [ + "schema_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "subgraphs_target_id_targets_id_fk": { + "name": "subgraphs_target_id_targets_id_fk", + "tableFrom": "subgraphs", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "federated_subgraphs": { + "name": "federated_subgraphs", + "schema": "", + "columns": { + "federated_graph_id": { + "name": "federated_graph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subgraph_id": { + "name": "subgraph_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "federated_subgraphs_federated_graph_id_federated_graphs_id_fk": { + "name": "federated_subgraphs_federated_graph_id_federated_graphs_id_fk", + "tableFrom": "federated_subgraphs", + "tableTo": "federated_graphs", + "columnsFrom": [ + "federated_graph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "federated_subgraphs_subgraph_id_subgraphs_id_fk": { + "name": "federated_subgraphs_subgraph_id_subgraphs_id_fk", + "tableFrom": "federated_subgraphs", + "tableTo": "subgraphs", + "columnsFrom": [ + "subgraph_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "federated_subgraphs_federated_graph_id_subgraph_id": { + "name": "federated_subgraphs_federated_graph_id_subgraph_id", + "columns": [ + "federated_graph_id", + "subgraph_id" + ] + } + }, + "uniqueConstraints": {} + }, + "target_label_matchers": { + "name": "target_label_matchers", + "schema": "", + "columns": { + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label_matcher": { + "name": "label_matcher", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "target_label_matchers_target_id_targets_id_fk": { + "name": "target_label_matchers_target_id_targets_id_fk", + "tableFrom": "target_label_matchers", + "tableTo": "targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "targets": { + "name": "targets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "target_type", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "labels": { + "name": "labels", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "organization_name_idx": { + "name": "organization_name_idx", + "columns": [ + "organization_id", + "name" + ], + "isUnique": true + } + }, + "foreignKeys": { + "targets_organization_id_organizations_id_fk": { + "name": "targets_organization_id_organizations_id_fk", + "tableFrom": "targets", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + } + } + }, + "enums": { + "member_role": { + "name": "member_role", + "values": { + "admin": "admin", + "member": "member" + } + }, + "schema_change_type": { + "name": "schema_change_type", + "values": { + "FIELD_ARGUMENT_DESCRIPTION_CHANGED": "FIELD_ARGUMENT_DESCRIPTION_CHANGED", + "FIELD_ARGUMENT_DEFAULT_CHANGED": "FIELD_ARGUMENT_DEFAULT_CHANGED", + "FIELD_ARGUMENT_TYPE_CHANGED": "FIELD_ARGUMENT_TYPE_CHANGED", + "DIRECTIVE_REMOVED": "DIRECTIVE_REMOVED", + "DIRECTIVE_ADDED": "DIRECTIVE_ADDED", + "DIRECTIVE_DESCRIPTION_CHANGED": "DIRECTIVE_DESCRIPTION_CHANGED", + "DIRECTIVE_LOCATION_ADDED": "DIRECTIVE_LOCATION_ADDED", + "DIRECTIVE_LOCATION_REMOVED": "DIRECTIVE_LOCATION_REMOVED", + "DIRECTIVE_ARGUMENT_ADDED": "DIRECTIVE_ARGUMENT_ADDED", + "DIRECTIVE_ARGUMENT_REMOVED": "DIRECTIVE_ARGUMENT_REMOVED", + "DIRECTIVE_ARGUMENT_DESCRIPTION_CHANGED": "DIRECTIVE_ARGUMENT_DESCRIPTION_CHANGED", + "DIRECTIVE_ARGUMENT_DEFAULT_VALUE_CHANGED": "DIRECTIVE_ARGUMENT_DEFAULT_VALUE_CHANGED", + "DIRECTIVE_ARGUMENT_TYPE_CHANGED": "DIRECTIVE_ARGUMENT_TYPE_CHANGED", + "ENUM_VALUE_REMOVED": "ENUM_VALUE_REMOVED", + "ENUM_VALUE_ADDED": "ENUM_VALUE_ADDED", + "ENUM_VALUE_DESCRIPTION_CHANGED": "ENUM_VALUE_DESCRIPTION_CHANGED", + "ENUM_VALUE_DEPRECATION_REASON_CHANGED": "ENUM_VALUE_DEPRECATION_REASON_CHANGED", + "ENUM_VALUE_DEPRECATION_REASON_ADDED": "ENUM_VALUE_DEPRECATION_REASON_ADDED", + "ENUM_VALUE_DEPRECATION_REASON_REMOVED": "ENUM_VALUE_DEPRECATION_REASON_REMOVED", + "FIELD_REMOVED": "FIELD_REMOVED", + "FIELD_ADDED": "FIELD_ADDED", + "FIELD_DESCRIPTION_CHANGED": "FIELD_DESCRIPTION_CHANGED", + "FIELD_DESCRIPTION_ADDED": "FIELD_DESCRIPTION_ADDED", + "FIELD_DESCRIPTION_REMOVED": "FIELD_DESCRIPTION_REMOVED", + "FIELD_DEPRECATION_ADDED": "FIELD_DEPRECATION_ADDED", + "FIELD_DEPRECATION_REMOVED": "FIELD_DEPRECATION_REMOVED", + "FIELD_DEPRECATION_REASON_CHANGED": "FIELD_DEPRECATION_REASON_CHANGED", + "FIELD_DEPRECATION_REASON_ADDED": "FIELD_DEPRECATION_REASON_ADDED", + "FIELD_DEPRECATION_REASON_REMOVED": "FIELD_DEPRECATION_REASON_REMOVED", + "FIELD_TYPE_CHANGED": "FIELD_TYPE_CHANGED", + "FIELD_ARGUMENT_ADDED": "FIELD_ARGUMENT_ADDED", + "FIELD_ARGUMENT_REMOVED": "FIELD_ARGUMENT_REMOVED", + "INPUT_FIELD_REMOVED": "INPUT_FIELD_REMOVED", + "INPUT_FIELD_ADDED": "INPUT_FIELD_ADDED", + "INPUT_FIELD_DESCRIPTION_ADDED": "INPUT_FIELD_DESCRIPTION_ADDED", + "INPUT_FIELD_DESCRIPTION_REMOVED": "INPUT_FIELD_DESCRIPTION_REMOVED", + "INPUT_FIELD_DESCRIPTION_CHANGED": "INPUT_FIELD_DESCRIPTION_CHANGED", + "INPUT_FIELD_DEFAULT_VALUE_CHANGED": "INPUT_FIELD_DEFAULT_VALUE_CHANGED", + "INPUT_FIELD_TYPE_CHANGED": "INPUT_FIELD_TYPE_CHANGED", + "OBJECT_TYPE_INTERFACE_ADDED": "OBJECT_TYPE_INTERFACE_ADDED", + "OBJECT_TYPE_INTERFACE_REMOVED": "OBJECT_TYPE_INTERFACE_REMOVED", + "SCHEMA_QUERY_TYPE_CHANGED": "SCHEMA_QUERY_TYPE_CHANGED", + "SCHEMA_MUTATION_TYPE_CHANGED": "SCHEMA_MUTATION_TYPE_CHANGED", + "SCHEMA_SUBSCRIPTION_TYPE_CHANGED": "SCHEMA_SUBSCRIPTION_TYPE_CHANGED", + "TYPE_REMOVED": "TYPE_REMOVED", + "TYPE_ADDED": "TYPE_ADDED", + "TYPE_KIND_CHANGED": "TYPE_KIND_CHANGED", + "TYPE_DESCRIPTION_CHANGED": "TYPE_DESCRIPTION_CHANGED", + "TYPE_DESCRIPTION_REMOVED": "TYPE_DESCRIPTION_REMOVED", + "TYPE_DESCRIPTION_ADDED": "TYPE_DESCRIPTION_ADDED", + "UNION_MEMBER_REMOVED": "UNION_MEMBER_REMOVED", + "UNION_MEMBER_ADDED": "UNION_MEMBER_ADDED" + } + }, + "target_type": { + "name": "target_type", + "values": { + "federated": "federated", + "subgraph": "subgraph", + "graph": "graph" + } + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/controlplane/migrations/meta/_journal.json b/controlplane/migrations/meta/_journal.json index f1ad788238..d929dc4b4f 100644 --- a/controlplane/migrations/meta/_journal.json +++ b/controlplane/migrations/meta/_journal.json @@ -267,6 +267,13 @@ "when": 1692728912058, "tag": "0037_dapper_christian_walker", "breakpoints": true + }, + { + "idx": 38, + "version": "5", + "when": 1692780607175, + "tag": "0038_lame_abomination", + "breakpoints": true } ] } \ No newline at end of file diff --git a/controlplane/src/core/bufservices/PlatformService.ts b/controlplane/src/core/bufservices/PlatformService.ts index 55c4bef033..962286ca76 100644 --- a/controlplane/src/core/bufservices/PlatformService.ts +++ b/controlplane/src/core/bufservices/PlatformService.ts @@ -33,6 +33,7 @@ import { GetTraceResponse, InviteUserResponse, PublishFederatedSubgraphResponse, + RemoveInvitationResponse, RequestSeriesItem, UpdateFederatedGraphResponse, UpdateSubgraphResponse, @@ -53,9 +54,9 @@ import { AnalyticsDashboardViewRepository } from '../repositories/analytics/Anal import { AnalyticsRequestViewRepository } from '../repositories/analytics/AnalyticsRequestViewRepository.js'; import { TraceRepository } from '../repositories/analytics/TraceRepository.js'; import type { RouterOptions } from '../routes.js'; +import { ApiKeyGenerator } from '../services/ApiGenerator.js'; import Keycloak from '../services/Keycloak.js'; import { handleError, isValidLabelMatchers, isValidLabels } from '../util.js'; -import { ApiKeyGenerator } from '../services/ApiGenerator.js'; export default function (opts: RouterOptions): Partial> { return { @@ -1365,20 +1366,29 @@ export default function (opts: RouterOptions): Partial { + const logger = opts.logger.child({ + service: ctx.service.typeName, + method: ctx.method.name, + }); + + return handleError>(logger, async () => { + const authContext = await opts.authenticator.authenticate(ctx.requestHeader); + const orgRepo = new OrganizationRepository(opts.db); + const userRepo = new UserRepository(opts.db); + + const user = await userRepo.byEmail(req.email); + if (!user) { + return { + response: { + code: EnumStatusCode.ERR_NOT_FOUND, + details: `User ${req.email} not found`, + }, + }; + } + + const org = await orgRepo.byId(authContext.organizationId); + if (!org) { + return { + response: { + code: EnumStatusCode.ERR_NOT_FOUND, + details: `Organization not found`, + }, + }; + } + + const orgMember = await orgRepo.getOrganizationMember({ + organizationID: authContext.organizationId, + userID: user.id, + }); + if (!orgMember) { + return { + response: { + code: EnumStatusCode.ERR_NOT_FOUND, + details: `User ${req.email} is not a part of this organization.`, + }, + }; + } + + await opts.keycloakClient.authenticateClient(); + + const organizationGroup = await opts.keycloakClient.client.groups.find({ + max: 1, + search: org.slug, + realm: opts.keycloak.realm, + }); + + if (organizationGroup.length === 0) { + throw new Error(`Organization group '${org.slug}' not found`); + } + + await opts.keycloakClient.client.users.delFromGroup({ + id: user.id, + groupId: organizationGroup[0].id!, + realm: opts.keycloak.realm, + }); + + await orgRepo.removeOrganizationMember({ organizationID: authContext.organizationId, userID: user.id }); + + // deleting the user from keycloak + await opts.keycloakClient.client.users.del({ + id: user.id, + realm: opts.keycloak.realm, + }); + // deleting the user from the db + await userRepo.deleteUser({ id: user.id }); + + return { + response: { + code: EnumStatusCode.OK, + }, + }; + }); + }, }; } diff --git a/controlplane/src/core/build-server.ts b/controlplane/src/core/build-server.ts index 8e68b24ad1..9c18f5370a 100644 --- a/controlplane/src/core/build-server.ts +++ b/controlplane/src/core/build-server.ts @@ -18,6 +18,7 @@ import { Authentication } from './services/Authentication.js'; import { OrganizationRepository } from './repositories/OrganizationRepository.js'; import GraphApiTokenAuthenticator from './services/GraphApiTokenAuthenticator.js'; import AuthUtils from './auth-utils.js'; +import Keycloak from './services/Keycloak.js'; export interface BuildConfig { logger: PinoLoggerOptions; @@ -123,6 +124,16 @@ export default async function build(opts: BuildConfig) { const organizationRepository = new OrganizationRepository(fastify.db); const authenticator = new Authentication(webAuth, apiKeyAuth, graphKeyAuth, organizationRepository); + const keycloakClient = new Keycloak({ + apiUrl: opts.keycloak.apiUrl, + // Important: If you want to do admin operations, you need to use the master realm + // Otherwise use the realm where your users are stored + realm: 'master', + clientId: opts.keycloak.clientId, + adminUser: opts.keycloak.adminUser, + adminPassword: opts.keycloak.adminPassword, + }); + /** * Controllers registration */ @@ -154,6 +165,7 @@ export default async function build(opts: BuildConfig) { keycloak: opts.keycloak, chClient: fastify.ch, authenticator, + keycloakClient, }), logLevel: opts.logger.level as pino.LevelWithSilent, // Avoid compression for small requests diff --git a/controlplane/src/core/controllers/auth.ts b/controlplane/src/core/controllers/auth.ts index f306e6b94e..0d3fe9e565 100644 --- a/controlplane/src/core/controllers/auth.ts +++ b/controlplane/src/core/controllers/auth.ts @@ -47,7 +47,7 @@ const plugin: FastifyPluginCallback = function Auth(fasti id: userSession.userId, email: userInfoData.email, organizations: orgs, - roles: await opts.organizationRepository.getMemberRoles({ + roles: await opts.organizationRepository.getOrganizationMemberRoles({ userID: userSession.userId, // just passing the first org because we are limiting the user to onyly be a part of a single organization. organizationID: orgs[0].id, diff --git a/controlplane/src/core/repositories/OrganizationRepository.ts b/controlplane/src/core/repositories/OrganizationRepository.ts index 3d7edf5c99..489e2f98f2 100644 --- a/controlplane/src/core/repositories/OrganizationRepository.ts +++ b/controlplane/src/core/repositories/OrganizationRepository.ts @@ -1,8 +1,8 @@ +import { ExpiresAt } from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb'; import { and, asc, eq } from 'drizzle-orm'; import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; -import { ExpiresAt, User } from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb'; import * as schema from '../../db/schema.js'; -import { apiKeys, memberRoles, organizations, organizationsMembers, users } from '../../db/schema.js'; +import { apiKeys, organizationMemberRoles, organizations, organizationsMembers, users } from '../../db/schema.js'; import { APIKeyDTO, OrganizationDTO, OrganizationMemberDTO } from '../../types/index.js'; /** @@ -86,6 +86,40 @@ export class OrganizationRepository { })); } + public async getOrganizationMember(input: { + organizationID: string; + userID: string; + }): Promise { + const orgMember = await this.db + .select({ + id: users.id, + email: users.email, + acceptedInvite: organizationsMembers.acceptedInvite, + memberID: organizationsMembers.id, + }) + .from(organizationsMembers) + .innerJoin(users, eq(users.id, organizationsMembers.userId)) + .where(and(eq(organizationsMembers.organizationId, input.organizationID), eq(users.id, input.userID))) + .orderBy(asc(organizationsMembers.createdAt)) + .execute(); + + if (orgMember.length === 0) { + return null; + } + + const userRoles = await this.getOrganizationMemberRoles({ + organizationID: input.organizationID, + userID: input.userID, + }); + + return { + id: orgMember[0].id, + email: orgMember[0].email, + roles: userRoles, + acceptedInvite: orgMember[0].acceptedInvite, + } as OrganizationMemberDTO; + } + public async getMembers(input: { organizationID: string }): Promise { const orgMembers = await this.db .select({ @@ -100,22 +134,22 @@ export class OrganizationRepository { .orderBy(asc(organizationsMembers.createdAt)) .execute(); - const members: User[] = []; + const members: OrganizationMemberDTO[] = []; for (const member of orgMembers) { const roles = await this.db .select({ - role: memberRoles.role, + role: organizationMemberRoles.role, }) - .from(memberRoles) - .where(eq(memberRoles.organizationMemberId, member.memberID)) + .from(organizationMemberRoles) + .where(eq(organizationMemberRoles.organizationMemberId, member.memberID)) .execute(); members.push({ id: member.id, email: member.email, roles: roles.map((role) => role.role), acceptedInvite: member.acceptedInvite, - } as User); + } as OrganizationMemberDTO); } return members; } @@ -157,7 +191,7 @@ export class OrganizationRepository { return insertedMember[0]; } - public async addMemberRoles(input: { memberID: string; roles: ('admin' | 'member')[] }) { + public async addOrganizationMemberRoles(input: { memberID: string; roles: ('admin' | 'member')[] }) { const values: { organizationMemberId: string; role: 'admin' | 'member'; @@ -170,16 +204,31 @@ export class OrganizationRepository { }); } - await this.db.insert(memberRoles).values(values).execute(); + await this.db.insert(organizationMemberRoles).values(values).execute(); + } + + public async removeOrganizationMember(input: { userID: string; organizationID: string }) { + await this.db + .delete(organizationsMembers) + .where( + and( + eq(organizationsMembers.organizationId, input.organizationID), + eq(organizationsMembers.userId, input.userID), + ), + ) + .execute(); } - public async getMemberRoles(input: { userID: string; organizationID: string }): Promise<('admin' | 'member')[]> { + public async getOrganizationMemberRoles(input: { + userID: string; + organizationID: string; + }): Promise<('admin' | 'member')[]> { const userRoles = await this.db .select({ - role: memberRoles.role, + role: organizationMemberRoles.role, }) - .from(memberRoles) - .innerJoin(organizationsMembers, eq(organizationsMembers.id, memberRoles.organizationMemberId)) + .from(organizationMemberRoles) + .innerJoin(organizationsMembers, eq(organizationsMembers.id, organizationMemberRoles.organizationMemberId)) .where( and( eq(organizationsMembers.userId, input.userID), diff --git a/controlplane/src/core/repositories/UserRepository.ts b/controlplane/src/core/repositories/UserRepository.ts index 95fa23faf5..6587212599 100644 --- a/controlplane/src/core/repositories/UserRepository.ts +++ b/controlplane/src/core/repositories/UserRepository.ts @@ -38,6 +38,10 @@ export class UserRepository { .execute(); } + public async deleteUser(input: { id: string }) { + await this.db.delete(users).where(eq(users.id, input.id)).execute(); + } + public async inviteUser(input: { email: string; keycloakUserID: string; @@ -61,7 +65,7 @@ export class UserRepository { acceptedInvite: false, }); - await orgRepo.addMemberRoles({ memberID: insertedMember.id, roles: ['member'] }); + await orgRepo.addOrganizationMemberRoles({ memberID: insertedMember.id, roles: ['member'] }); }); } } diff --git a/controlplane/src/core/routes.ts b/controlplane/src/core/routes.ts index 0fd0bf7fc7..7301e7b20f 100644 --- a/controlplane/src/core/routes.ts +++ b/controlplane/src/core/routes.ts @@ -10,6 +10,7 @@ import NodeServiceImpl from './bufservices/NodeService.js'; import { ClickHouseClient } from './clickhouse/index.js'; import { Authenticator } from './services/Authentication.js'; import { BuildConfig } from './build-server.js'; +import Keycloak from './services/Keycloak.js'; export interface RouterOptions { db: PostgresJsDatabase; @@ -18,6 +19,7 @@ export interface RouterOptions { keycloak: BuildConfig['keycloak']; chClient?: ClickHouseClient; logger: pino.Logger; + keycloakClient: Keycloak; } const handlerOptions: Partial = { maxTimeoutMs: 5000, diff --git a/controlplane/src/core/test-util.ts b/controlplane/src/core/test-util.ts index 89361f9a5f..cd10fb5b9a 100644 --- a/controlplane/src/core/test-util.ts +++ b/controlplane/src/core/test-util.ts @@ -66,7 +66,7 @@ export async function seedTest(databaseConnectionUrl: string, userTestData: User acceptedInvite: true, }); - await orgRepo.addMemberRoles({ + await orgRepo.addOrganizationMemberRoles({ memberID: orgMember.id, roles: ['admin'], }); diff --git a/controlplane/src/db/schema.ts b/controlplane/src/db/schema.ts index 5f5e60b28a..a8b09ed3f2 100644 --- a/controlplane/src/db/schema.ts +++ b/controlplane/src/db/schema.ts @@ -1,16 +1,5 @@ -import { relations, sql } from 'drizzle-orm'; -import { - boolean, - index, - jsonb, - pgEnum, - pgTable, - primaryKey, - text, - timestamp, - uniqueIndex, - uuid, -} from 'drizzle-orm/pg-core'; +import { relations } from 'drizzle-orm'; +import { boolean, jsonb, pgEnum, pgTable, primaryKey, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core'; export const federatedGraphs = pgTable('federated_graphs', { id: uuid('id').primaryKey().defaultRandom(), @@ -419,7 +408,7 @@ export const organizationRelations = relations(organizations, ({ many }) => ({ export const memberRoleEnum = pgEnum('member_role', ['admin', 'member']); -export const memberRoles = pgTable('member_roles', { +export const organizationMemberRoles = pgTable('organization_member_roles', { id: uuid('id').notNull().primaryKey().defaultRandom(), organizationMemberId: uuid('organization_member_id') .notNull() @@ -430,12 +419,12 @@ export const memberRoles = pgTable('member_roles', { }); export const organizationMembersRelations = relations(organizationsMembers, ({ many }) => ({ - memberRoles: many(memberRoles), + memberRoles: many(organizationMemberRoles), })); -export const memberRolesRelations = relations(memberRoles, ({ one }) => ({ +export const organizationMemberRolesRelations = relations(organizationMemberRoles, ({ one }) => ({ member: one(organizationsMembers, { - fields: [memberRoles.organizationMemberId], + fields: [organizationMemberRoles.organizationMemberId], references: [organizationsMembers.id], }), })); diff --git a/controlplane/test/api-keys.test.ts b/controlplane/test/api-keys.test.ts index 869aa5d0d0..f43b4b85e2 100644 --- a/controlplane/test/api-keys.test.ts +++ b/controlplane/test/api-keys.test.ts @@ -11,6 +11,7 @@ import { uid } from 'uid'; import database from '../src/core/plugins/database'; import routes from '../src/core/routes'; import { afterAllSetup, beforeAllSetup, createTestAuthenticator, seedTest } from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -38,6 +39,20 @@ describe('API Keys', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -45,12 +60,13 @@ describe('API Keys', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -84,7 +100,7 @@ describe('API Keys', (ctx) => { expect(response.response?.code).toBe(EnumStatusCode.ERR_ALREADY_EXISTS); // test when api key name is wrong - response = await client.createAPIKey({ name: 'a b', expires: ExpiresAt.NEVER, userID: userTestData.userId }); + response = await client.createAPIKey({ name: 'ab', expires: ExpiresAt.NEVER, userID: userTestData.userId }); expect(response.response?.code).toBe(EnumStatusCode.ERR); let deleteResponse = await client.deleteAPIKey({ name: 'test' }); diff --git a/controlplane/test/apollo-federation.test.ts b/controlplane/test/apollo-federation.test.ts index 11d80267d5..f4530de57a 100644 --- a/controlplane/test/apollo-federation.test.ts +++ b/controlplane/test/apollo-federation.test.ts @@ -19,6 +19,7 @@ import { genUniqueLabel, seedTest, } from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -46,6 +47,20 @@ describe('Apollo Federated Graph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -53,12 +68,13 @@ describe('Apollo Federated Graph', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/authenthication.test.ts b/controlplane/test/authenthication.test.ts index f577034ea0..a34b50ff1b 100644 --- a/controlplane/test/authenthication.test.ts +++ b/controlplane/test/authenthication.test.ts @@ -6,7 +6,7 @@ import { PlatformService } from '@wundergraph/cosmo-connect/dist/platform/v1/pla import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common_pb'; import { NodeService } from '@wundergraph/cosmo-connect/dist/node/v1/node_connect'; import build from '../src/core/build-server'; -import { afterAllSetup, beforeAllSetup, genID, genUniqueLabel } from 'src/core/test-util'; +import { afterAllSetup, beforeAllSetup, genID, genUniqueLabel } from '../src/core/test-util'; let dbname = ''; @@ -31,6 +31,7 @@ describe('Authentication', (ctx) => { clientId: 'test', secret: 'secret', openIdApiBaseUrl: 'http://localhost:8080', + openIdBaseUrl: 'http://localhost:8080', redirectUri: 'http://localhost:3000', webBaseUrl: 'http://localhost:3000', webErrorPath: '/error', diff --git a/controlplane/test/check-federated-graph.test.ts b/controlplane/test/check-federated-graph.test.ts index 780eff0924..365464c34c 100644 --- a/controlplane/test/check-federated-graph.test.ts +++ b/controlplane/test/check-federated-graph.test.ts @@ -10,7 +10,8 @@ import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common_pb'; import { afterAll, beforeAll, describe, expect, test } from 'vitest'; import database from '../src/core/plugins/database'; import routes from '../src/core/routes'; -import { afterAllSetup, beforeAllSetup, createTestAuthenticator, genID, seedTest } from 'src/core/test-util'; +import { afterAllSetup, beforeAllSetup, createTestAuthenticator, genID, seedTest } from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -38,6 +39,20 @@ describe('CheckFederatedGraph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -45,12 +60,13 @@ describe('CheckFederatedGraph', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/check-subgraph-schema.test.ts b/controlplane/test/check-subgraph-schema.test.ts index dac64f03ac..c27431089b 100644 --- a/controlplane/test/check-subgraph-schema.test.ts +++ b/controlplane/test/check-subgraph-schema.test.ts @@ -17,7 +17,8 @@ import { genID, genUniqueLabel, seedTest, -} from 'src/core/test-util'; +} from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -45,6 +46,20 @@ describe('CheckSubgraphSchema', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -52,12 +67,13 @@ describe('CheckSubgraphSchema', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -129,6 +145,20 @@ describe('CheckSubgraphSchema', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -136,12 +166,13 @@ describe('CheckSubgraphSchema', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -211,6 +242,20 @@ describe('CheckSubgraphSchema', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -218,12 +263,13 @@ describe('CheckSubgraphSchema', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/compose-federationv1-graphs.test.ts b/controlplane/test/compose-federationv1-graphs.test.ts index 8906e19abd..31fc2bc93e 100644 --- a/controlplane/test/compose-federationv1-graphs.test.ts +++ b/controlplane/test/compose-federationv1-graphs.test.ts @@ -19,7 +19,8 @@ import { genID, genUniqueLabel, seedTest, -} from 'src/core/test-util'; +} from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -47,6 +48,20 @@ describe('ComposeFederationV1Graphs', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -54,12 +69,13 @@ describe('ComposeFederationV1Graphs', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/compose-federationv2-graphs.test.ts b/controlplane/test/compose-federationv2-graphs.test.ts index 5294f2d114..7b57159832 100644 --- a/controlplane/test/compose-federationv2-graphs.test.ts +++ b/controlplane/test/compose-federationv2-graphs.test.ts @@ -19,7 +19,8 @@ import { genID, genUniqueLabel, seedTest, -} from 'src/core/test-util'; +} from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -47,6 +48,20 @@ describe('ComposeFederationV2Graphs', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -54,12 +69,13 @@ describe('ComposeFederationV2Graphs', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/composition-errors.test.ts b/controlplane/test/composition-errors.test.ts index 49470009f7..6f69e0108a 100644 --- a/controlplane/test/composition-errors.test.ts +++ b/controlplane/test/composition-errors.test.ts @@ -20,7 +20,8 @@ import { genID, genUniqueLabel, seedTest, -} from 'src/core/test-util'; +} from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -48,6 +49,20 @@ describe('CompositionErrors', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -55,12 +70,13 @@ describe('CompositionErrors', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/delete-federated-graph.test.ts b/controlplane/test/delete-federated-graph.test.ts index cfebf47827..2d19b60d59 100644 --- a/controlplane/test/delete-federated-graph.test.ts +++ b/controlplane/test/delete-federated-graph.test.ts @@ -16,7 +16,8 @@ import { genID, genUniqueLabel, seedTest, -} from 'src/core/test-util'; +} from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -44,6 +45,20 @@ describe('DeleteFederatedGraph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -51,12 +66,13 @@ describe('DeleteFederatedGraph', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/delete-subgraph.test.ts b/controlplane/test/delete-subgraph.test.ts index 450bbe8bfe..223d8283d2 100644 --- a/controlplane/test/delete-subgraph.test.ts +++ b/controlplane/test/delete-subgraph.test.ts @@ -17,6 +17,7 @@ import { genUniqueLabel, seedTest, } from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -44,6 +45,20 @@ describe('DeleteSubgraph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -51,12 +66,13 @@ describe('DeleteSubgraph', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/federated-graph.test.ts b/controlplane/test/federated-graph.test.ts index 5bb30747b9..dea8bef95e 100644 --- a/controlplane/test/federated-graph.test.ts +++ b/controlplane/test/federated-graph.test.ts @@ -17,6 +17,7 @@ import { genUniqueLabel, seedTest, } from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -44,6 +45,20 @@ describe('Federated Graph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -51,12 +66,13 @@ describe('Federated Graph', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -126,6 +142,20 @@ describe('Federated Graph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -133,12 +163,13 @@ describe('Federated Graph', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -208,6 +239,20 @@ describe('Federated Graph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -215,12 +260,13 @@ describe('Federated Graph', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -290,6 +336,20 @@ describe('Federated Graph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -297,12 +357,13 @@ describe('Federated Graph', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -401,6 +462,20 @@ describe('Federated Graph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -408,12 +483,13 @@ describe('Federated Graph', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/label.test.ts b/controlplane/test/label.test.ts index fd86148826..6a3b8334fd 100644 --- a/controlplane/test/label.test.ts +++ b/controlplane/test/label.test.ts @@ -20,6 +20,7 @@ import { genUniqueLabel, seedTest, } from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -47,6 +48,20 @@ describe('Labels', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -54,12 +69,13 @@ describe('Labels', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -143,6 +159,20 @@ describe('Labels', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -150,12 +180,13 @@ describe('Labels', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -264,6 +295,20 @@ describe('Labels', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -271,12 +316,13 @@ describe('Labels', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/router-config.test.ts b/controlplane/test/router-config.test.ts index 31c2caf8cb..a4275142db 100644 --- a/controlplane/test/router-config.test.ts +++ b/controlplane/test/router-config.test.ts @@ -20,6 +20,7 @@ import { genUniqueLabel, seedTest, } from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -47,6 +48,20 @@ describe('Router Config', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -54,12 +69,13 @@ describe('Router Config', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -263,6 +279,20 @@ describe('Router Config', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -270,12 +300,13 @@ describe('Router Config', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); diff --git a/controlplane/test/subgraph.test.ts b/controlplane/test/subgraph.test.ts index baa295e57b..2b0d2f6d4b 100644 --- a/controlplane/test/subgraph.test.ts +++ b/controlplane/test/subgraph.test.ts @@ -18,6 +18,7 @@ import { genUniqueLabel, seedTest, } from '../src/core/test-util'; +import Keycloak from '../src/core/services/Keycloak'; let dbname = ''; @@ -45,6 +46,20 @@ describe('Subgraph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + await server.register(fastifyConnectPlugin, { routes: routes({ db: server.db, @@ -52,12 +67,13 @@ describe('Subgraph', (ctx) => { authenticator, jwtSecret: 'secret', keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', + apiUrl, + realm, + clientId, + adminUser, + adminPassword, }, + keycloakClient, }), }); @@ -109,21 +125,36 @@ describe('Subgraph', (ctx) => { const { authenticator, userTestData } = createTestAuthenticator(); - await server.register(fastifyConnectPlugin, { - routes: routes({ - db: server.db, - logger: pino(), - authenticator, - jwtSecret: 'secret', - keycloak: { - realm: 'test', - adminUser: 'admin', - adminPassword: 'changeme', - apiUrl: 'http://localhost:8080', - clientId: 'studio', - }, - }), - }); + const realm = 'test'; + const apiUrl = 'http://localhost:8080'; + const clientId = 'studio'; + const adminUser = 'admin'; + const adminPassword = 'changeme'; + + const keycloakClient = new Keycloak({ + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }); + + await server.register(fastifyConnectPlugin, { + routes: routes({ + db: server.db, + logger: pino(), + authenticator, + jwtSecret: 'secret', + keycloak: { + apiUrl, + realm, + clientId, + adminUser, + adminPassword, + }, + keycloakClient, + }), + }); const addr = await server.listen({ port: 0, diff --git a/proto/wg/cosmo/platform/v1/platform.proto b/proto/wg/cosmo/platform/v1/platform.proto index 9836f14630..3f8d5e1ec0 100644 --- a/proto/wg/cosmo/platform/v1/platform.proto +++ b/proto/wg/cosmo/platform/v1/platform.proto @@ -502,6 +502,14 @@ message DeleteAPIKeyResponse { Response response = 1; } +message RemoveInvitationRequest { + string email = 1; +} + +message RemoveInvitationResponse { + Response response = 1; +} + message SpanAttributes { string httpStatusCode = 1; string componentName = 2; @@ -591,6 +599,8 @@ service PlatformService { rpc CreateAPIKey(CreateAPIKeyRequest) returns (CreateAPIKeyResponse){} // DeleteAPIKey deletes an API key for the organization rpc DeleteAPIKey(DeleteAPIKeyRequest) returns (DeleteAPIKeyResponse){} + // RemoveOrganizationMember removes the user from the organization + rpc RemoveInvitation(RemoveInvitationRequest) returns (RemoveInvitationResponse){} // GetLatestValidRouterConfig returns the router config for the federated graph rpc GetLatestValidRouterConfig(wg.cosmo.node.v1.GetConfigRequest) returns (wg.cosmo.node.v1.GetConfigResponse) {} diff --git a/studio/src/components/subgraphs-table.tsx b/studio/src/components/subgraphs-table.tsx index 4202118a1b..ba591f3271 100644 --- a/studio/src/components/subgraphs-table.tsx +++ b/studio/src/components/subgraphs-table.tsx @@ -1,23 +1,22 @@ import { docsBaseURL } from "@/lib/constatnts"; import { CommandLineIcon } from "@heroicons/react/24/outline"; +import { formatDistanceToNow } from "date-fns"; import Link from "next/link"; import { Subgraph, FederatedGraph, } from "@wundergraph/cosmo-connect/dist/platform/v1/platform_pb"; import { EmptyState } from "./empty-state"; +import { Badge } from "./ui/badge"; import { CLISteps } from "./ui/cli"; import { Table, TableBody, - TableCaption, TableCell, TableHead, TableHeader, - TableRow, + TableRow } from "./ui/table"; -import { Badge } from "./ui/badge"; -import { formatDistanceToNow } from "date-fns"; export const Empty = ({ graph }: { graph?: FederatedGraph }) => { let label = "team=A"; diff --git a/studio/src/components/ui/use-toast.ts b/studio/src/components/ui/use-toast.ts index edc829679d..85146bcce5 100644 --- a/studio/src/components/ui/use-toast.ts +++ b/studio/src/components/ui/use-toast.ts @@ -182,6 +182,11 @@ function useToast() { return { ...state, toast, + update: (props: ToasterToast) => + dispatch({ + type: "UPDATE_TOAST", + toast: { ...props }, + }), dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), }; } diff --git a/studio/src/pages/[organizationSlug]/members.tsx b/studio/src/pages/[organizationSlug]/members.tsx index 1b49da20fc..f5aa8d4c81 100644 --- a/studio/src/pages/[organizationSlug]/members.tsx +++ b/studio/src/pages/[organizationSlug]/members.tsx @@ -1,19 +1,30 @@ +import { UserContext } from "@/components/app-provider"; import { EmptyState } from "@/components/empty-state"; import { getDashboardLayout } from "@/components/layout/dashboard-layout"; import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { Loader } from "@/components/ui/loader"; import { useToast } from "@/components/ui/use-toast"; import { SubmitHandler, useZodForm } from "@/hooks/use-form"; import { NextPageWithLayout } from "@/lib/page"; +import { cn } from "@/lib/utils"; import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; import { useMutation, useQuery } from "@tanstack/react-query"; -import { sentenceCase } from "change-case"; import { EnumStatusCode } from "@wundergraph/cosmo-connect/dist/common_pb"; import { getOrganizationMembers, inviteUser, + removeInvitation, } from "@wundergraph/cosmo-connect/dist/platform/v1/platform-PlatformService_connectquery"; +import { sentenceCase } from "change-case"; +import { useContext } from "react"; +import { HiOutlineDotsVertical } from "react-icons/hi"; import { z } from "zod"; const emailInputSchema = z.object({ @@ -35,16 +46,10 @@ const InviteForm = ({ refresh }: { refresh: () => void }) => { const { mutate, isLoading } = useMutation(inviteUser.useMutation()); - const { toast, dismiss } = useToast(); + const { toast } = useToast(); const sendToast = (description: string) => { - const { id } = toast({ description }); - - const t = setTimeout(() => { - dismiss(id); - }, 3000); - - return () => clearTimeout(t); + const { id } = toast({ description, duration: 3000 }); }; const onSubmit: SubmitHandler = (data) => { @@ -94,24 +99,112 @@ const MemberCard = ({ email, role, acceptedInvite, + isAdmin, + isCurrentUser, + refresh, }: { email: string; role: string; acceptedInvite: boolean; + isAdmin: boolean; + isCurrentUser: boolean; + refresh: () => void; }) => { + const { mutate: resendInvitation } = useMutation(inviteUser.useMutation()); + const { mutate: revokeInvitation } = useMutation( + removeInvitation.useMutation() + ); + + const { toast, update } = useToast(); + return (
{email}
- {acceptedInvite ? ( - {sentenceCase(role)} - ) : ( - - Pending... - - )} +
+ {acceptedInvite ? ( + {sentenceCase(role)} + ) : ( + + Pending + + )} +
+ +
+ {isAdmin && !isCurrentUser && ( + + +
+ +
+
+ + {!acceptedInvite && ( + { + const { id } = toast({ + description: "Inviting member...", + }); + resendInvitation( + { email }, + { + onSuccess: (d) => { + update({ + description: + d.response?.details || + "Invited member successfully.", + duration: 2000, + id: id, + }); + }, + onError: (error) => { + toast({ + description: + "Could not invite the member. Please try again.", + duration: 3000, + }); + }, + } + ); + }} + > + Resend invitation + + )} + { + revokeInvitation( + { email }, + { + onSuccess: (d) => { + toast({ + description: + d.response?.details || + "Removed member successfully.", + duration: 3000, + }); + refresh(); + }, + onError: (error) => { + toast({ + description: + "Could not remove the member. Please try again.", + duration: 3000, + }); + }, + } + ); + }} + > + {acceptedInvite ? "Remove member" : "Remove invitation"} + + +
+ )} +
); @@ -122,13 +215,15 @@ const MembersPage: NextPageWithLayout = () => { getOrganizationMembers.useQuery() ); + const user = useContext(UserContext); + if (isLoading) return ; - if (error || data.response?.code !== EnumStatusCode.OK) + if (error || data.response?.code !== EnumStatusCode.OK || !user) return ( } - title="Could not retrieve federated graphs" + title="Could not retrieve the members of this organization." description={ data?.response?.details || error?.message || "Please try again" } @@ -138,9 +233,14 @@ const MembersPage: NextPageWithLayout = () => { if (!data?.members) return null; + const currentUser = data.members.find( + (member) => member.email === user.email + ); + const isAdmin = currentUser?.roles.includes("admin"); + return (
- refetch()} /> + {isAdmin && refetch()} />}
{data.members?.map((member) => { return ( @@ -149,6 +249,9 @@ const MembersPage: NextPageWithLayout = () => { email={member.email} role={member.roles[0]} acceptedInvite={member.acceptedInvite} + isAdmin={isAdmin || false} + isCurrentUser={member.email === user.email} + refresh={() => refetch()} /> ); })} From 7d9f834b942aa95528416246bb54db4cb4eb0773 Mon Sep 17 00:00:00 2001 From: JivusAyrus Date: Wed, 23 Aug 2023 19:49:00 +0530 Subject: [PATCH 2/4] fix: tests --- controlplane/test/api-keys.test.ts | 2 ++ controlplane/test/apollo-federation.test.ts | 2 ++ controlplane/test/authenthication.test.ts | 4 +--- controlplane/test/check-federated-graph.test.ts | 2 ++ controlplane/test/check-subgraph-schema.test.ts | 6 ++++++ controlplane/test/compose-federationv1-graphs.test.ts | 2 ++ controlplane/test/compose-federationv2-graphs.test.ts | 2 ++ controlplane/test/composition-errors.test.ts | 2 ++ controlplane/test/delete-federated-graph.test.ts | 2 ++ controlplane/test/delete-subgraph.test.ts | 2 ++ controlplane/test/federated-graph.test.ts | 10 ++++++++++ controlplane/test/label.test.ts | 6 ++++++ controlplane/test/router-config.test.ts | 4 ++++ controlplane/test/subgraph.test.ts | 4 ++++ 14 files changed, 47 insertions(+), 3 deletions(-) diff --git a/controlplane/test/api-keys.test.ts b/controlplane/test/api-keys.test.ts index f43b4b85e2..d8e297673b 100644 --- a/controlplane/test/api-keys.test.ts +++ b/controlplane/test/api-keys.test.ts @@ -41,6 +41,7 @@ describe('API Keys', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -65,6 +66,7 @@ describe('API Keys', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/apollo-federation.test.ts b/controlplane/test/apollo-federation.test.ts index f4530de57a..7008c6f5dd 100644 --- a/controlplane/test/apollo-federation.test.ts +++ b/controlplane/test/apollo-federation.test.ts @@ -49,6 +49,7 @@ describe('Apollo Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -73,6 +74,7 @@ describe('Apollo Federated Graph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/authenthication.test.ts b/controlplane/test/authenthication.test.ts index a34b50ff1b..06fbfdc183 100644 --- a/controlplane/test/authenthication.test.ts +++ b/controlplane/test/authenthication.test.ts @@ -28,10 +28,7 @@ describe('Authentication', (ctx) => { }, allowedOrigins: [], auth: { - clientId: 'test', secret: 'secret', - openIdApiBaseUrl: 'http://localhost:8080', - openIdBaseUrl: 'http://localhost:8080', redirectUri: 'http://localhost:3000', webBaseUrl: 'http://localhost:3000', webErrorPath: '/error', @@ -44,6 +41,7 @@ describe('Authentication', (ctx) => { adminUser: 'admin', adminPassword: 'changeme', apiUrl: 'http://localhost:8080', + frontendUrl: 'http://localhost:8080', clientId: 'studio', }, }); diff --git a/controlplane/test/check-federated-graph.test.ts b/controlplane/test/check-federated-graph.test.ts index 365464c34c..d069c7c027 100644 --- a/controlplane/test/check-federated-graph.test.ts +++ b/controlplane/test/check-federated-graph.test.ts @@ -41,6 +41,7 @@ describe('CheckFederatedGraph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -65,6 +66,7 @@ describe('CheckFederatedGraph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/check-subgraph-schema.test.ts b/controlplane/test/check-subgraph-schema.test.ts index c27431089b..5da969514c 100644 --- a/controlplane/test/check-subgraph-schema.test.ts +++ b/controlplane/test/check-subgraph-schema.test.ts @@ -48,6 +48,7 @@ describe('CheckSubgraphSchema', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -72,6 +73,7 @@ describe('CheckSubgraphSchema', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), @@ -147,6 +149,7 @@ describe('CheckSubgraphSchema', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -171,6 +174,7 @@ describe('CheckSubgraphSchema', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), @@ -244,6 +248,7 @@ describe('CheckSubgraphSchema', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -268,6 +273,7 @@ describe('CheckSubgraphSchema', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/compose-federationv1-graphs.test.ts b/controlplane/test/compose-federationv1-graphs.test.ts index 31fc2bc93e..4973e6163e 100644 --- a/controlplane/test/compose-federationv1-graphs.test.ts +++ b/controlplane/test/compose-federationv1-graphs.test.ts @@ -50,6 +50,7 @@ describe('ComposeFederationV1Graphs', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -74,6 +75,7 @@ describe('ComposeFederationV1Graphs', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/compose-federationv2-graphs.test.ts b/controlplane/test/compose-federationv2-graphs.test.ts index 7b57159832..e5f765aa3f 100644 --- a/controlplane/test/compose-federationv2-graphs.test.ts +++ b/controlplane/test/compose-federationv2-graphs.test.ts @@ -50,6 +50,7 @@ describe('ComposeFederationV2Graphs', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -74,6 +75,7 @@ describe('ComposeFederationV2Graphs', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/composition-errors.test.ts b/controlplane/test/composition-errors.test.ts index 6f69e0108a..134160fe4b 100644 --- a/controlplane/test/composition-errors.test.ts +++ b/controlplane/test/composition-errors.test.ts @@ -51,6 +51,7 @@ describe('CompositionErrors', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -75,6 +76,7 @@ describe('CompositionErrors', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/delete-federated-graph.test.ts b/controlplane/test/delete-federated-graph.test.ts index 2d19b60d59..1e1f7caae7 100644 --- a/controlplane/test/delete-federated-graph.test.ts +++ b/controlplane/test/delete-federated-graph.test.ts @@ -47,6 +47,7 @@ describe('DeleteFederatedGraph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -71,6 +72,7 @@ describe('DeleteFederatedGraph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/delete-subgraph.test.ts b/controlplane/test/delete-subgraph.test.ts index 223d8283d2..fe3e0e75b4 100644 --- a/controlplane/test/delete-subgraph.test.ts +++ b/controlplane/test/delete-subgraph.test.ts @@ -47,6 +47,7 @@ describe('DeleteSubgraph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -71,6 +72,7 @@ describe('DeleteSubgraph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/federated-graph.test.ts b/controlplane/test/federated-graph.test.ts index dea8bef95e..62672461ea 100644 --- a/controlplane/test/federated-graph.test.ts +++ b/controlplane/test/federated-graph.test.ts @@ -47,6 +47,7 @@ describe('Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -71,6 +72,7 @@ describe('Federated Graph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), @@ -144,6 +146,7 @@ describe('Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -168,6 +171,7 @@ describe('Federated Graph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), @@ -241,6 +245,7 @@ describe('Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -265,6 +270,7 @@ describe('Federated Graph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), @@ -338,6 +344,7 @@ describe('Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -362,6 +369,7 @@ describe('Federated Graph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), @@ -464,6 +472,7 @@ describe('Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -488,6 +497,7 @@ describe('Federated Graph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/label.test.ts b/controlplane/test/label.test.ts index 6a3b8334fd..e8ee9f1f79 100644 --- a/controlplane/test/label.test.ts +++ b/controlplane/test/label.test.ts @@ -50,6 +50,7 @@ describe('Labels', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -74,6 +75,7 @@ describe('Labels', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), @@ -161,6 +163,7 @@ describe('Labels', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -185,6 +188,7 @@ describe('Labels', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), @@ -297,6 +301,7 @@ describe('Labels', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -321,6 +326,7 @@ describe('Labels', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/router-config.test.ts b/controlplane/test/router-config.test.ts index a4275142db..62a6538699 100644 --- a/controlplane/test/router-config.test.ts +++ b/controlplane/test/router-config.test.ts @@ -50,6 +50,7 @@ describe('Router Config', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -74,6 +75,7 @@ describe('Router Config', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), @@ -281,6 +283,7 @@ describe('Router Config', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -305,6 +308,7 @@ describe('Router Config', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), diff --git a/controlplane/test/subgraph.test.ts b/controlplane/test/subgraph.test.ts index 2b0d2f6d4b..0e5f7316b0 100644 --- a/controlplane/test/subgraph.test.ts +++ b/controlplane/test/subgraph.test.ts @@ -48,6 +48,7 @@ describe('Subgraph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -72,6 +73,7 @@ describe('Subgraph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), @@ -127,6 +129,7 @@ describe('Subgraph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; + const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -151,6 +154,7 @@ describe('Subgraph', (ctx) => { clientId, adminUser, adminPassword, + frontendUrl, }, keycloakClient, }), From d888f1f61539459205ffa66020b772aa721f3d3b Mon Sep 17 00:00:00 2001 From: JivusAyrus Date: Thu, 24 Aug 2023 13:58:25 +0530 Subject: [PATCH 3/4] fix: pr suggestion --- .../src/core/bufservices/PlatformService.ts | 20 ++++---- controlplane/src/core/build-server.ts | 2 +- controlplane/src/core/routes.ts | 7 ++- controlplane/test/api-keys.test.ts | 10 +--- controlplane/test/apollo-federation.test.ts | 10 +--- .../test/check-federated-graph.test.ts | 10 +--- .../test/check-subgraph-schema.test.ts | 30 ++--------- .../test/compose-federationv1-graphs.test.ts | 10 +--- .../test/compose-federationv2-graphs.test.ts | 10 +--- controlplane/test/composition-errors.test.ts | 10 +--- .../test/delete-federated-graph.test.ts | 10 +--- controlplane/test/delete-subgraph.test.ts | 10 +--- controlplane/test/federated-graph.test.ts | 50 ++----------------- controlplane/test/label.test.ts | 30 ++--------- controlplane/test/router-config.test.ts | 20 +------- controlplane/test/subgraph.test.ts | 20 +------- 16 files changed, 37 insertions(+), 222 deletions(-) diff --git a/controlplane/src/core/bufservices/PlatformService.ts b/controlplane/src/core/bufservices/PlatformService.ts index 962286ca76..2988837660 100644 --- a/controlplane/src/core/bufservices/PlatformService.ts +++ b/controlplane/src/core/bufservices/PlatformService.ts @@ -1379,7 +1379,7 @@ export default function (opts: RouterOptions): Partial; jwtSecret: string; authenticator: Authenticator; - keycloak: BuildConfig['keycloak']; + keycloakRealm: string; chClient?: ClickHouseClient; logger: pino.Logger; keycloakClient: Keycloak; diff --git a/controlplane/test/api-keys.test.ts b/controlplane/test/api-keys.test.ts index d8e297673b..5bf4cfca0e 100644 --- a/controlplane/test/api-keys.test.ts +++ b/controlplane/test/api-keys.test.ts @@ -41,7 +41,6 @@ describe('API Keys', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -60,14 +59,7 @@ describe('API Keys', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/apollo-federation.test.ts b/controlplane/test/apollo-federation.test.ts index 7008c6f5dd..d9899adf0a 100644 --- a/controlplane/test/apollo-federation.test.ts +++ b/controlplane/test/apollo-federation.test.ts @@ -49,7 +49,6 @@ describe('Apollo Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -68,14 +67,7 @@ describe('Apollo Federated Graph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/check-federated-graph.test.ts b/controlplane/test/check-federated-graph.test.ts index d069c7c027..a0e6a1cc9e 100644 --- a/controlplane/test/check-federated-graph.test.ts +++ b/controlplane/test/check-federated-graph.test.ts @@ -41,7 +41,6 @@ describe('CheckFederatedGraph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -60,14 +59,7 @@ describe('CheckFederatedGraph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/check-subgraph-schema.test.ts b/controlplane/test/check-subgraph-schema.test.ts index 5da969514c..ed59344bd8 100644 --- a/controlplane/test/check-subgraph-schema.test.ts +++ b/controlplane/test/check-subgraph-schema.test.ts @@ -48,7 +48,6 @@ describe('CheckSubgraphSchema', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -67,14 +66,7 @@ describe('CheckSubgraphSchema', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); @@ -149,7 +141,6 @@ describe('CheckSubgraphSchema', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -168,14 +159,7 @@ describe('CheckSubgraphSchema', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); @@ -248,7 +232,6 @@ describe('CheckSubgraphSchema', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -267,14 +250,7 @@ describe('CheckSubgraphSchema', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/compose-federationv1-graphs.test.ts b/controlplane/test/compose-federationv1-graphs.test.ts index 4973e6163e..12708f2766 100644 --- a/controlplane/test/compose-federationv1-graphs.test.ts +++ b/controlplane/test/compose-federationv1-graphs.test.ts @@ -50,7 +50,6 @@ describe('ComposeFederationV1Graphs', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -69,14 +68,7 @@ describe('ComposeFederationV1Graphs', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/compose-federationv2-graphs.test.ts b/controlplane/test/compose-federationv2-graphs.test.ts index e5f765aa3f..50b7fb32c0 100644 --- a/controlplane/test/compose-federationv2-graphs.test.ts +++ b/controlplane/test/compose-federationv2-graphs.test.ts @@ -50,7 +50,6 @@ describe('ComposeFederationV2Graphs', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -69,14 +68,7 @@ describe('ComposeFederationV2Graphs', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/composition-errors.test.ts b/controlplane/test/composition-errors.test.ts index 134160fe4b..165909e46c 100644 --- a/controlplane/test/composition-errors.test.ts +++ b/controlplane/test/composition-errors.test.ts @@ -51,7 +51,6 @@ describe('CompositionErrors', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -70,14 +69,7 @@ describe('CompositionErrors', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/delete-federated-graph.test.ts b/controlplane/test/delete-federated-graph.test.ts index 1e1f7caae7..fa8ede92a0 100644 --- a/controlplane/test/delete-federated-graph.test.ts +++ b/controlplane/test/delete-federated-graph.test.ts @@ -47,7 +47,6 @@ describe('DeleteFederatedGraph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -66,14 +65,7 @@ describe('DeleteFederatedGraph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/delete-subgraph.test.ts b/controlplane/test/delete-subgraph.test.ts index fe3e0e75b4..311f557c63 100644 --- a/controlplane/test/delete-subgraph.test.ts +++ b/controlplane/test/delete-subgraph.test.ts @@ -47,7 +47,6 @@ describe('DeleteSubgraph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -66,14 +65,7 @@ describe('DeleteSubgraph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/federated-graph.test.ts b/controlplane/test/federated-graph.test.ts index 62672461ea..7c1c3e8a25 100644 --- a/controlplane/test/federated-graph.test.ts +++ b/controlplane/test/federated-graph.test.ts @@ -47,7 +47,6 @@ describe('Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -66,14 +65,7 @@ describe('Federated Graph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); @@ -146,7 +138,6 @@ describe('Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -165,14 +156,7 @@ describe('Federated Graph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); @@ -245,7 +229,6 @@ describe('Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -264,14 +247,7 @@ describe('Federated Graph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); @@ -344,7 +320,6 @@ describe('Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -363,14 +338,7 @@ describe('Federated Graph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); @@ -472,7 +440,6 @@ describe('Federated Graph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -491,14 +458,7 @@ describe('Federated Graph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/label.test.ts b/controlplane/test/label.test.ts index e8ee9f1f79..c495bc85c7 100644 --- a/controlplane/test/label.test.ts +++ b/controlplane/test/label.test.ts @@ -50,7 +50,6 @@ describe('Labels', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -69,14 +68,7 @@ describe('Labels', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); @@ -163,7 +155,6 @@ describe('Labels', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -182,14 +173,7 @@ describe('Labels', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); @@ -301,7 +285,6 @@ describe('Labels', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -320,14 +303,7 @@ describe('Labels', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/router-config.test.ts b/controlplane/test/router-config.test.ts index 62a6538699..eda3887994 100644 --- a/controlplane/test/router-config.test.ts +++ b/controlplane/test/router-config.test.ts @@ -50,7 +50,6 @@ describe('Router Config', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -69,14 +68,7 @@ describe('Router Config', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); @@ -283,7 +275,6 @@ describe('Router Config', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -302,14 +293,7 @@ describe('Router Config', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); diff --git a/controlplane/test/subgraph.test.ts b/controlplane/test/subgraph.test.ts index 0e5f7316b0..41bd797032 100644 --- a/controlplane/test/subgraph.test.ts +++ b/controlplane/test/subgraph.test.ts @@ -48,7 +48,6 @@ describe('Subgraph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -67,14 +66,7 @@ describe('Subgraph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); @@ -129,7 +121,6 @@ describe('Subgraph', (ctx) => { const realm = 'test'; const apiUrl = 'http://localhost:8080'; - const frontendUrl = 'http://localhost:8080'; const clientId = 'studio'; const adminUser = 'admin'; const adminPassword = 'changeme'; @@ -148,14 +139,7 @@ describe('Subgraph', (ctx) => { logger: pino(), authenticator, jwtSecret: 'secret', - keycloak: { - apiUrl, - realm, - clientId, - adminUser, - adminPassword, - frontendUrl, - }, + keycloakRealm: realm, keycloakClient, }), }); From 57774244a05077a3eedeb13fd3baa281305607c4 Mon Sep 17 00:00:00 2001 From: JivusAyrus Date: Thu, 24 Aug 2023 14:33:35 +0530 Subject: [PATCH 4/4] fix: add data migration --- ...0038_lame_abomination.sql => 0038_tidy_lightspeed.sql} | 8 ++++++++ controlplane/migrations/meta/0038_snapshot.json | 2 +- controlplane/migrations/meta/_journal.json | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) rename controlplane/migrations/{0038_lame_abomination.sql => 0038_tidy_lightspeed.sql} (76%) diff --git a/controlplane/migrations/0038_lame_abomination.sql b/controlplane/migrations/0038_tidy_lightspeed.sql similarity index 76% rename from controlplane/migrations/0038_lame_abomination.sql rename to controlplane/migrations/0038_tidy_lightspeed.sql index 31ce8ea250..7aa9c3b277 100644 --- a/controlplane/migrations/0038_lame_abomination.sql +++ b/controlplane/migrations/0038_tidy_lightspeed.sql @@ -3,6 +3,14 @@ CREATE TABLE IF NOT EXISTS "organization_member_roles" ( "organization_member_id" uuid NOT NULL, "role" "member_role" NOT NULL ); + +BEGIN TRANSACTION; +INSERT INTO "organization_member_roles" ("id", "organization_member_id", "role") +SELECT "id", "organization_member_id", "role" +FROM "member_roles"; + +COMMIT; + --> statement-breakpoint DROP TABLE "member_roles";--> statement-breakpoint DO $$ BEGIN diff --git a/controlplane/migrations/meta/0038_snapshot.json b/controlplane/migrations/meta/0038_snapshot.json index 94af6faa01..078927b558 100644 --- a/controlplane/migrations/meta/0038_snapshot.json +++ b/controlplane/migrations/meta/0038_snapshot.json @@ -1,7 +1,7 @@ { "version": "5", "dialect": "pg", - "id": "2e51adc7-dc51-4f48-9803-d357f00ca94f", + "id": "2b9ca50f-d51c-4224-9c2a-6305c149865f", "prevId": "fe627a51-d14c-49e3-9eae-3622e2dd4182", "tables": { "api_keys": { diff --git a/controlplane/migrations/meta/_journal.json b/controlplane/migrations/meta/_journal.json index d929dc4b4f..8d0b50ae87 100644 --- a/controlplane/migrations/meta/_journal.json +++ b/controlplane/migrations/meta/_journal.json @@ -271,8 +271,8 @@ { "idx": 38, "version": "5", - "when": 1692780607175, - "tag": "0038_lame_abomination", + "when": 1692867741484, + "tag": "0038_tidy_lightspeed", "breakpoints": true } ]