From a8931971d4b303ea71b5e0b5423f794d23b8cefc Mon Sep 17 00:00:00 2001 From: Rohan Chaturvedi Date: Sun, 8 Dec 2024 01:48:00 +0530 Subject: [PATCH] feat: add teams support for vercel syncs (#401) * feat: add teams support for vercel syncs * fix: misc fixes for backward compatibility * fix: source environment context when syncing secrets --------- Co-authored-by: Nimish Co-authored-by: Nimish <85357445+nimish-ks@users.noreply.github.com> --- backend/api/tasks.py | 6 +- backend/api/utils/syncing/vercel/main.py | 251 ++++++++++++------ backend/backend/graphene/mutations/syncing.py | 9 +- backend/backend/schema.py | 6 +- frontend/apollo/gql.ts | 8 +- frontend/apollo/graphql.ts | 19 +- frontend/apollo/schema.graphql | 10 +- frontend/components/syncing/ServiceInfo.tsx | 3 +- .../syncing/Vercel/CreateVercelSync.tsx | 196 +++++++++----- .../syncing/vercel/createVercelSync.gql | 6 +- .../queries/syncing/vercel/getProject.gql | 10 +- 11 files changed, 352 insertions(+), 172 deletions(-) diff --git a/backend/api/tasks.py b/backend/api/tasks.py index cd08841b..99d3faa4 100644 --- a/backend/api/tasks.py +++ b/backend/api/tasks.py @@ -93,7 +93,7 @@ def trigger_sync_tasks(env_sync): job_id = job.get_id() EnvironmentSyncEvent.objects.create(id=job_id, env_sync=env_sync) - + elif env_sync.service == ServiceConfig.VERCEL["id"]: env_sync.status = EnvironmentSync.IN_PROGRESS env_sync.save() @@ -792,6 +792,7 @@ def perform_vercel_sync(environment_sync): vercel_sync_options = environment_sync.options vercel_project = vercel_sync_options.get("project") + vercel_team = vercel_sync_options.get("team") vercel_environment = vercel_sync_options.get("environment", "production") vercel_secret_type = vercel_sync_options.get("secret_type", "encrypted") @@ -799,6 +800,7 @@ def perform_vercel_sync(environment_sync): secrets, environment_sync.authentication.id, vercel_project["id"], + vercel_team["id"] if vercel_team is not None else None, vercel_environment, vercel_secret_type, ) @@ -853,4 +855,4 @@ def perform_vercel_sync(environment_sync): environment_sync.last_sync = timezone.now() environment_sync.status = EnvironmentSync.FAILED - environment_sync.save() \ No newline at end of file + environment_sync.save() diff --git a/backend/api/utils/syncing/vercel/main.py b/backend/api/utils/syncing/vercel/main.py index cb69f7a9..4c8b602f 100644 --- a/backend/api/utils/syncing/vercel/main.py +++ b/backend/api/utils/syncing/vercel/main.py @@ -4,12 +4,22 @@ from api.utils.syncing.auth import get_credentials -VERCEL_API_BASE_URL = 'https://api.vercel.com' +VERCEL_API_BASE_URL = "https://api.vercel.com" + + +from graphene import ObjectType, List, ID, String + class VercelProjectType(ObjectType): - id = graphene.ID(required=True) - name = graphene.String(required=True) - environment = graphene.List(graphene.String) + id = ID(required=True) + name = String(required=True) + environment = List(String) + + +class VercelTeamProjectsType(ObjectType): + id = String(required=True) + team_name = String(required=True) + projects = List(VercelProjectType) def get_vercel_credentials(credential_id): @@ -21,14 +31,14 @@ def get_vercel_credentials(credential_id): def get_vercel_headers(token): """Prepare headers for Vercel API requests.""" - return {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'} + return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} def test_vercel_creds(credential_id): """Test if the Vercel credentials are valid.""" try: token = get_vercel_credentials(credential_id) - url = f'{VERCEL_API_BASE_URL}/v2/user' + url = f"{VERCEL_API_BASE_URL}/v2/user" response = requests.get(url, headers=get_vercel_headers(token)) return response.status_code == 200 except Exception: @@ -38,53 +48,106 @@ def test_vercel_creds(credential_id): def list_vercel_projects(credential_id): """ List all Vercel projects accessible with the provided credentials. - Returns a list of projects with their IDs, names, and available environments. + Includes personal projects as part of the "personal team" when teams are listed. + Returns a list of dictionaries with team names and their projects. """ try: token = get_vercel_credentials(credential_id) - url = f'{VERCEL_API_BASE_URL}/v9/projects' - response = requests.get(url, headers=get_vercel_headers(token)) - - if response.status_code != 200: - raise Exception(f"Failed to list Vercel projects: {response.text}") - - projects = response.json().get('projects', []) - return [ - { - "id": project["id"], - "name": project["name"], - "environment": ["development", "preview", "production"] - } - for project in projects - ] + + # Fetch teams + teams_url = f"{VERCEL_API_BASE_URL}/v2/teams" + teams_response = requests.get(teams_url, headers=get_vercel_headers(token)) + if teams_response.status_code != 200: + raise Exception(f"Failed to list Vercel teams: {teams_response.text}") + + teams = teams_response.json().get("teams", []) + + result = [] + + # Fetch projects for each team + for team in teams: + team_id = team["id"] + team_name = team["name"] + + # Construct the URL based on whether it's a personal team + team_projects_url = ( + f"{VERCEL_API_BASE_URL}/v9/projects" + if team_id is None + else f"{VERCEL_API_BASE_URL}/v9/projects?teamId={team_id}" + ) + team_projects_response = requests.get( + team_projects_url, headers=get_vercel_headers(token) + ) + if team_projects_response.status_code != 200: + print( + f"Failed to list projects for team {team_name}: {team_projects_response.text}" + ) + continue + + team_projects = team_projects_response.json().get("projects", []) + result.append( + { + "id": team_id, + "team_name": team_name, + "projects": [ + { + "id": project["id"], + "name": project["name"], + "environment": ["development", "preview", "production"], + } + for project in team_projects + ], + } + ) + + return result + except Exception as e: raise Exception(f"Error listing Vercel projects: {str(e)}") -def get_existing_env_vars(token, project_id): - """Retrieve all environment variables for a specific Vercel project.""" - url = f'{VERCEL_API_BASE_URL}/v9/projects/{project_id}/env' - response = requests.get(url, headers=get_vercel_headers(token)) +def get_existing_env_vars(token, project_id, team_id=None, target_environment=None): + """ + Retrieve environment variables for a specific Vercel project and environment. + Args: + token (str): Vercel API token + project_id (str): Project ID + team_id (str, optional): Team ID + target_environment (str, optional): Specific environment to filter by + """ + url = f"{VERCEL_API_BASE_URL}/v9/projects/{project_id}/env" + if team_id is not None: + url += f"?teamId={team_id}" + response = requests.get(url, headers=get_vercel_headers(token)) + if response.status_code != 200: raise Exception(f"Error retrieving environment variables: {response.text}") + + envs = response.json().get("envs", []) + # Filter variables by target environment if specified + if target_environment: + envs = [env for env in envs if target_environment in env["target"]] + return { - env['key']: { - 'id': env['id'], - 'value': env['value'], - 'target': env['target'], - 'comment': env.get('comment'), + env["key"]: { + "id": env["id"], + "value": env["value"], + "target": env["target"], + "comment": env.get("comment"), } - for env in response.json().get('envs', []) + for env in envs } -def delete_env_var(token, project_id, env_var_id): +def delete_env_var(token, project_id, team_id, env_var_id): """Delete a Vercel environment variable using its ID.""" - url = f'{VERCEL_API_BASE_URL}/v9/projects/{project_id}/env/{env_var_id}' + url = f"{VERCEL_API_BASE_URL}/v9/projects/{project_id}/env/{env_var_id}" + if team_id is not None: + url += f"?teamId={team_id}" response = requests.delete(url, headers=get_vercel_headers(token)) - + if response.status_code != 200: raise Exception(f"Error deleting environment variable: {response.text}") @@ -93,76 +156,92 @@ def sync_vercel_secrets( secrets, credential_id, project_id, + team_id, environment="production", secret_type="encrypted", ): """ - Sync secrets to a Vercel project. - + Sync secrets to a specific Vercel project environment. + Args: secrets (list of tuple): List of (key, value, comment) tuples to sync credential_id (str): The ID of the stored credentials project_id (str): The Vercel project ID + team_id (str): The Vercel project team ID environment (str): Target environment (development/preview/production/all) secret_type (str): Type of secret (plain/encrypted/sensitive) - + Returns: tuple: (bool, dict) indicating success/failure and a message """ try: token = get_vercel_credentials(credential_id) - + # Determine target environments target_environments = ( - ['production', 'preview', 'development'] - if environment == 'all' + ["production", "preview", "development"] + if environment == "all" else [environment] ) - - # Get existing environment variables - existing_env_vars = get_existing_env_vars(token, project_id) - - # Prepare payload for bulk creation - payload = [] - for key, value, comment in secrets: - # Check if the environment variable exists and needs updating - if key in existing_env_vars: - existing_var = existing_env_vars[key] - if ( - value != existing_var['value'] - or target_environments != existing_var['target'] - or comment != existing_var.get('comment') - ): - delete_env_var(token, project_id, existing_var['id']) - - env_var = { - 'key': key, - 'value': value, - 'type': secret_type, - 'target': target_environments, - } - if comment: - env_var['comment'] = comment - payload.append(env_var) - - # Delete environment variables not in the source - for key, env_var in existing_env_vars.items(): - if not any(s[0] == key for s in secrets): - delete_env_var(token, project_id, env_var['id']) - - # Bulk create environment variables - if payload: - url = f'{VERCEL_API_BASE_URL}/v10/projects/{project_id}/env?upsert=true' - response = requests.post( - url, headers=get_vercel_headers(token), json=payload + + all_updates_successful = True + messages = [] + + # Process each target environment separately + for target_env in target_environments: + # Get existing environment variables for this specific environment + existing_env_vars = get_existing_env_vars( + token, project_id, team_id, target_environment=target_env ) - - if response.status_code != 201: - raise Exception(f"Error creating environment variables: {response.text}") - - return True, { - "message": f"Successfully synced secrets to Vercel project environments: {', '.join(target_environments)}" - } - + + # Prepare payload for bulk creation + payload = [] + for key, value, comment in secrets: + # Check if the environment variable exists and needs updating + if key in existing_env_vars: + existing_var = existing_env_vars[key] + if ( + value != existing_var["value"] + or comment != existing_var.get("comment") + ): + # Only delete if we're updating this specific variable + delete_env_var(token, project_id, team_id, existing_var["id"]) + + env_var = { + "key": key, + "value": value, + "type": secret_type, + "target": [target_env], # Set target to specific environment + } + if comment: + env_var["comment"] = comment + payload.append(env_var) + + # Delete environment variables not in the source (only for this environment) + for key, env_var in existing_env_vars.items(): + if not any(s[0] == key for s in secrets): + delete_env_var(token, project_id, team_id, env_var["id"]) + + # Bulk create environment variables + if payload: + url = f"{VERCEL_API_BASE_URL}/v10/projects/{project_id}/env?upsert=true" + if team_id is not None: + url += f"&teamId={team_id}" + response = requests.post( + url, headers=get_vercel_headers(token), json=payload + ) + + if response.status_code != 201: + all_updates_successful = False + messages.append( + f"Failed to sync secrets for environment {target_env}: {response.text}" + ) + else: + messages.append( + f"Successfully synced secrets to environment: {target_env}" + ) + + return all_updates_successful, {"message": "; ".join(messages)} + except Exception as e: return False, {"message": f"Failed to sync secrets: {str(e)}"} diff --git a/backend/backend/graphene/mutations/syncing.py b/backend/backend/graphene/mutations/syncing.py index 6714d295..690a3b3a 100644 --- a/backend/backend/graphene/mutations/syncing.py +++ b/backend/backend/graphene/mutations/syncing.py @@ -26,9 +26,6 @@ class RailwayResourceInput(graphene.InputObjectType): id = graphene.ID(required=True) name = graphene.String(required=True) -class VercelResourceInput(graphene.InputObjectType): - id = graphene.ID(required=True) - name = graphene.String(required=True) class InitEnvSync(graphene.Mutation): class Arguments: @@ -546,6 +543,8 @@ class Arguments: env_id = graphene.ID() path = graphene.String() credential_id = graphene.ID() + team_id = graphene.String() + team_name = graphene.String() project_id = graphene.String() project_name = graphene.String() environment = graphene.String() @@ -561,6 +560,8 @@ def mutate( env_id, path, credential_id, + team_id, + team_name, project_id, project_name, environment="production", @@ -578,6 +579,7 @@ def mutate( sync_options = { "project": {"id": project_id, "name": project_name}, + "team": {"id": team_id, "name": team_name}, "environment": environment, "secret_type": secret_type, } @@ -602,6 +604,7 @@ def mutate( return CreateVercelSync(sync=sync) + class DeleteSync(graphene.Mutation): class Arguments: sync_id = graphene.ID() diff --git a/backend/backend/schema.py b/backend/backend/schema.py index 855bd8ee..6bf3ac01 100644 --- a/backend/backend/schema.py +++ b/backend/backend/schema.py @@ -12,7 +12,7 @@ UpdateServiceAccountHandlersMutation, UpdateServiceAccountMutation, ) -from api.utils.syncing.vercel.main import VercelProjectType +from api.utils.syncing.vercel.main import VercelTeamProjectsType from .graphene.queries.syncing import ( resolve_vercel_projects, ) @@ -309,8 +309,8 @@ class Query(graphene.ObjectType): railway_projects = graphene.List(RailwayProjectType, credential_id=graphene.ID()) - vercel_projects = graphene.List(VercelProjectType, credential_id=graphene.ID()) - + vercel_projects = graphene.List(VercelTeamProjectsType, credential_id=graphene.ID()) + test_vercel_creds = graphene.Field(graphene.Boolean, credential_id=graphene.ID()) test_vault_creds = graphene.Field(graphene.Boolean, credential_id=graphene.ID()) diff --git a/frontend/apollo/gql.ts b/frontend/apollo/gql.ts index b72f9bb6..7218aa94 100644 --- a/frontend/apollo/gql.ts +++ b/frontend/apollo/gql.ts @@ -71,7 +71,7 @@ const documents = { "mutation UpdateProviderCreds($credentialId: ID!, $name: String!, $credentials: JSONString!) {\n updateProviderCredentials(\n credentialId: $credentialId\n name: $name\n credentials: $credentials\n ) {\n credential {\n id\n }\n }\n}": types.UpdateProviderCredsDocument, "mutation UpdateSyncAuth($syncId: ID!, $credentialId: ID!) {\n updateSyncAuthentication(syncId: $syncId, credentialId: $credentialId) {\n sync {\n id\n status\n }\n }\n}": types.UpdateSyncAuthDocument, "mutation CreateNewVaultSync($envId: ID!, $path: String!, $engine: String!, $vaultPath: String!, $credentialId: ID!) {\n createVaultSync(\n envId: $envId\n path: $path\n engine: $engine\n vaultPath: $vaultPath\n credentialId: $credentialId\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewVaultSyncDocument, - "mutation CreateNewVercelSync($envId: ID!, $path: String!, $credentialId: ID!, $projectId: String!, $projectName: String!, $environment: String!, $secretType: String!) {\n createVercelSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n projectId: $projectId\n projectName: $projectName\n environment: $environment\n secretType: $secretType\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewVercelSyncDocument, + "mutation CreateNewVercelSync($envId: ID!, $path: String!, $credentialId: ID!, $projectId: String!, $projectName: String!, $teamId: String!, $teamName: String!, $environment: String!, $secretType: String!) {\n createVercelSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n projectId: $projectId\n projectName: $projectName\n teamId: $teamId\n teamName: $teamName\n environment: $environment\n secretType: $secretType\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}": types.CreateNewVercelSyncDocument, "mutation CreateNewUserToken($orgId: ID!, $name: String!, $identityKey: String!, $token: String!, $wrappedKeyShare: String!, $expiry: BigInt) {\n createUserToken(\n orgId: $orgId\n name: $name\n identityKey: $identityKey\n token: $token\n wrappedKeyShare: $wrappedKeyShare\n expiry: $expiry\n ) {\n ok\n }\n}": types.CreateNewUserTokenDocument, "mutation RevokeUserToken($tokenId: ID!) {\n deleteUserToken(tokenId: $tokenId) {\n ok\n }\n}": types.RevokeUserTokenDocument, "query GetAppMembers($appId: ID!) {\n appUsers(appId: $appId) {\n id\n identityKey\n email\n fullName\n avatarUrl\n createdAt\n role {\n id\n name\n description\n permissions\n color\n }\n }\n}": types.GetAppMembersDocument, @@ -116,7 +116,7 @@ const documents = { "query TestNomadAuth($credentialId: ID!) {\n testNomadCreds(credentialId: $credentialId)\n}": types.TestNomadAuthDocument, "query GetRailwayProjects($credentialId: ID!) {\n railwayProjects(credentialId: $credentialId) {\n id\n name\n environments {\n id\n name\n }\n services {\n id\n name\n }\n }\n}": types.GetRailwayProjectsDocument, "query TestVaultAuth($credentialId: ID!) {\n testVaultCreds(credentialId: $credentialId)\n}": types.TestVaultAuthDocument, - "query GetVercelProjects($credentialId: ID!) {\n vercelProjects(credentialId: $credentialId) {\n id\n name\n environment\n }\n}": types.GetVercelProjectsDocument, + "query GetVercelProjects($credentialId: ID!) {\n vercelProjects(credentialId: $credentialId) {\n id\n teamName\n projects {\n id\n name\n environment\n }\n }\n}": types.GetVercelProjectsDocument, "query GetUserTokens($organisationId: ID!) {\n userTokens(organisationId: $organisationId) {\n id\n name\n wrappedKeyShare\n createdAt\n expiresAt\n }\n}": types.GetUserTokensDocument, }; @@ -369,7 +369,7 @@ export function graphql(source: "mutation CreateNewVaultSync($envId: ID!, $path: /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "mutation CreateNewVercelSync($envId: ID!, $path: String!, $credentialId: ID!, $projectId: String!, $projectName: String!, $environment: String!, $secretType: String!) {\n createVercelSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n projectId: $projectId\n projectName: $projectName\n environment: $environment\n secretType: $secretType\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateNewVercelSync($envId: ID!, $path: String!, $credentialId: ID!, $projectId: String!, $projectName: String!, $environment: String!, $secretType: String!) {\n createVercelSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n projectId: $projectId\n projectName: $projectName\n environment: $environment\n secretType: $secretType\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}"]; +export function graphql(source: "mutation CreateNewVercelSync($envId: ID!, $path: String!, $credentialId: ID!, $projectId: String!, $projectName: String!, $teamId: String!, $teamName: String!, $environment: String!, $secretType: String!) {\n createVercelSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n projectId: $projectId\n projectName: $projectName\n teamId: $teamId\n teamName: $teamName\n environment: $environment\n secretType: $secretType\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}"): (typeof documents)["mutation CreateNewVercelSync($envId: ID!, $path: String!, $credentialId: ID!, $projectId: String!, $projectName: String!, $teamId: String!, $teamName: String!, $environment: String!, $secretType: String!) {\n createVercelSync(\n envId: $envId\n path: $path\n credentialId: $credentialId\n projectId: $projectId\n projectName: $projectName\n teamId: $teamId\n teamName: $teamName\n environment: $environment\n secretType: $secretType\n ) {\n sync {\n id\n environment {\n id\n name\n envType\n }\n serviceInfo {\n id\n name\n }\n isActive\n lastSync\n createdAt\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -549,7 +549,7 @@ export function graphql(source: "query TestVaultAuth($credentialId: ID!) {\n te /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query GetVercelProjects($credentialId: ID!) {\n vercelProjects(credentialId: $credentialId) {\n id\n name\n environment\n }\n}"): (typeof documents)["query GetVercelProjects($credentialId: ID!) {\n vercelProjects(credentialId: $credentialId) {\n id\n name\n environment\n }\n}"]; +export function graphql(source: "query GetVercelProjects($credentialId: ID!) {\n vercelProjects(credentialId: $credentialId) {\n id\n teamName\n projects {\n id\n name\n environment\n }\n }\n}"): (typeof documents)["query GetVercelProjects($credentialId: ID!) {\n vercelProjects(credentialId: $credentialId) {\n id\n teamName\n projects {\n id\n name\n environment\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/frontend/apollo/graphql.ts b/frontend/apollo/graphql.ts index 2f7df7da..e899e643 100644 --- a/frontend/apollo/graphql.ts +++ b/frontend/apollo/graphql.ts @@ -893,6 +893,8 @@ export type MutationCreateVercelSyncArgs = { projectId?: InputMaybe; projectName?: InputMaybe; secretType?: InputMaybe; + teamId?: InputMaybe; + teamName?: InputMaybe; }; @@ -1271,7 +1273,7 @@ export type Query = { testVercelCreds?: Maybe; userTokens?: Maybe>>; validateInvite?: Maybe; - vercelProjects?: Maybe>>; + vercelProjects?: Maybe>>; }; @@ -1802,6 +1804,13 @@ export type VercelProjectType = { name: Scalars['String']['output']; }; +export type VercelTeamProjectsType = { + __typename?: 'VercelTeamProjectsType'; + id: Scalars['String']['output']; + projects?: Maybe>>; + teamName: Scalars['String']['output']; +}; + export type CreateRoleMutationVariables = Exact<{ name: Scalars['String']['input']; description: Scalars['String']['input']; @@ -2338,6 +2347,8 @@ export type CreateNewVercelSyncMutationVariables = Exact<{ credentialId: Scalars['ID']['input']; projectId: Scalars['String']['input']; projectName: Scalars['String']['input']; + teamId: Scalars['String']['input']; + teamName: Scalars['String']['input']; environment: Scalars['String']['input']; secretType: Scalars['String']['input']; }>; @@ -2668,7 +2679,7 @@ export type GetVercelProjectsQueryVariables = Exact<{ }>; -export type GetVercelProjectsQuery = { __typename?: 'Query', vercelProjects?: Array<{ __typename?: 'VercelProjectType', id: string, name: string, environment?: Array | null } | null> | null }; +export type GetVercelProjectsQuery = { __typename?: 'Query', vercelProjects?: Array<{ __typename?: 'VercelTeamProjectsType', id: string, teamName: string, projects?: Array<{ __typename?: 'VercelProjectType', id: string, name: string, environment?: Array | null } | null> | null } | null> | null }; export type GetUserTokensQueryVariables = Exact<{ organisationId: Scalars['ID']['input']; @@ -2736,7 +2747,7 @@ export const TriggerEnvSyncDocument = {"kind":"Document","definitions":[{"kind": export const UpdateProviderCredsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateProviderCreds"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentials"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSONString"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateProviderCredentials"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentials"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentials"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"credential"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateSyncAuthDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSyncAuth"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"syncId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateSyncAuthentication"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"syncId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"syncId"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sync"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewVaultSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewVaultSync"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"engine"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"vaultPath"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createVaultSync"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"engine"},"value":{"kind":"Variable","name":{"kind":"Name","value":"engine"}}},{"kind":"Argument","name":{"kind":"Name","value":"vaultPath"},"value":{"kind":"Variable","name":{"kind":"Name","value":"vaultPath"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sync"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; -export const CreateNewVercelSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewVercelSync"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"environment"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"secretType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createVercelSync"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}}},{"kind":"Argument","name":{"kind":"Name","value":"environment"},"value":{"kind":"Variable","name":{"kind":"Name","value":"environment"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretType"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sync"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateNewVercelSyncDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewVercelSync"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"envId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"teamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"teamName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"environment"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"secretType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createVercelSync"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"envId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"envId"}}},{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}},{"kind":"Argument","name":{"kind":"Name","value":"projectName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectName"}}},{"kind":"Argument","name":{"kind":"Name","value":"teamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"teamId"}}},{"kind":"Argument","name":{"kind":"Name","value":"teamName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"teamName"}}},{"kind":"Argument","name":{"kind":"Name","value":"environment"},"value":{"kind":"Variable","name":{"kind":"Name","value":"environment"}}},{"kind":"Argument","name":{"kind":"Name","value":"secretType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"secretType"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sync"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"environment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"envType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serviceInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"lastSync"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateNewUserTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewUserToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createUserToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orgId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orgId"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"identityKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identityKey"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"wrappedKeyShare"},"value":{"kind":"Variable","name":{"kind":"Name","value":"wrappedKeyShare"}}},{"kind":"Argument","name":{"kind":"Name","value":"expiry"},"value":{"kind":"Variable","name":{"kind":"Name","value":"expiry"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; export const RevokeUserTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RevokeUserToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"tokenId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteUserToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"tokenId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"tokenId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ok"}}]}}]}}]} as unknown as DocumentNode; export const GetAppMembersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAppMembers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appUsers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"identityKey"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"fullName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -2781,5 +2792,5 @@ export const GetGitLabResourcesDocument = {"kind":"Document","definitions":[{"ki export const TestNomadAuthDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"TestNomadAuth"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"testNomadCreds"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}]}]}}]} as unknown as DocumentNode; export const GetRailwayProjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetRailwayProjects"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"railwayProjects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"environments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const TestVaultAuthDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"TestVaultAuth"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"testVaultCreds"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}]}]}}]} as unknown as DocumentNode; -export const GetVercelProjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetVercelProjects"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"vercelProjects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"environment"}}]}}]}}]} as unknown as DocumentNode; +export const GetVercelProjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetVercelProjects"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"vercelProjects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"credentialId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"credentialId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"teamName"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"environment"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetUserTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUserTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userTokens"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"organisationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"organisationId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"wrappedKeyShare"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/apollo/schema.graphql b/frontend/apollo/schema.graphql index 6b8a00db..d3693677 100644 --- a/frontend/apollo/schema.graphql +++ b/frontend/apollo/schema.graphql @@ -40,7 +40,7 @@ type Query { gitlabProjects(credentialId: ID): [GitLabProjectType] gitlabGroups(credentialId: ID): [GitLabGroupType] railwayProjects(credentialId: ID): [RailwayProjectType] - vercelProjects(credentialId: ID): [VercelProjectType] + vercelProjects(credentialId: ID): [VercelTeamProjectsType] testVercelCreds(credentialId: ID): Boolean testVaultCreds(credentialId: ID): Boolean testNomadCreds(credentialId: ID): Boolean @@ -636,6 +636,12 @@ type RailwayServiceType { name: String! } +type VercelTeamProjectsType { + id: String! + teamName: String! + projects: [VercelProjectType] +} + type VercelProjectType { id: ID! name: String! @@ -696,7 +702,7 @@ type Mutation { createNomadSync(credentialId: ID, envId: ID, nomadNamespace: String, nomadPath: String, path: String): CreateNomadSync createGitlabCiSync(credentialId: ID, envId: ID, isGroup: Boolean, masked: Boolean, path: String, protected: Boolean, resourceId: String, resourcePath: String): CreateGitLabCISync createRailwaySync(credentialId: ID, envId: ID, path: String, railwayEnvironment: RailwayResourceInput, railwayProject: RailwayResourceInput, railwayService: RailwayResourceInput): CreateRailwaySync - createVercelSync(credentialId: ID, envId: ID, environment: String, path: String, projectId: String, projectName: String, secretType: String): CreateVercelSync + createVercelSync(credentialId: ID, envId: ID, environment: String, path: String, projectId: String, projectName: String, secretType: String, teamId: String, teamName: String): CreateVercelSync createUserToken(expiry: BigInt, identityKey: String!, name: String!, orgId: ID!, token: String!, wrappedKeyShare: String!): CreateUserTokenMutation deleteUserToken(tokenId: ID!): DeleteUserTokenMutation createServiceToken(appId: ID!, environmentKeys: [EnvironmentKeyInput], expiry: BigInt, identityKey: String!, name: String!, token: String!, wrappedKeyShare: String!): CreateServiceTokenMutation diff --git a/frontend/components/syncing/ServiceInfo.tsx b/frontend/components/syncing/ServiceInfo.tsx index 5b4ff126..9095b213 100644 --- a/frontend/components/syncing/ServiceInfo.tsx +++ b/frontend/components/syncing/ServiceInfo.tsx @@ -67,13 +67,14 @@ export const ServiceInfo = (props: { sync: EnvironmentSyncType }) => { ) } else if (sync.serviceInfo?.id?.includes('vercel')) { + const team: { id: string; name: string } | undefined = JSON.parse(sync.options)['team'] const project: VercelProjectType = JSON.parse(sync.options)['project'] const environment = JSON.parse(sync.options)['environment'] const secretType = JSON.parse(sync.options)['secret_type'] return (
- {project.name} ({environment}) + {team?.name && `${team.name} / `} {project.name} ({environment}) {secretType === 'encrypted' && } {secretType === 'sensitive' && }
diff --git a/frontend/components/syncing/Vercel/CreateVercelSync.tsx b/frontend/components/syncing/Vercel/CreateVercelSync.tsx index 221c1b15..a7d80e81 100644 --- a/frontend/components/syncing/Vercel/CreateVercelSync.tsx +++ b/frontend/components/syncing/Vercel/CreateVercelSync.tsx @@ -6,10 +6,11 @@ import CreateNewVercelSync from '@/graphql/mutations/syncing/vercel/createVercel import { useLazyQuery, useMutation, useQuery } from '@apollo/client' import { Fragment, useContext, useEffect, useState } from 'react' import { Button } from '../../common/Button' -import { - EnvironmentType, +import { + EnvironmentType, ProviderCredentialsType, - VercelProjectType + VercelProjectType, + VercelTeamProjectsType, } from '@/apollo/graphql' import { Combobox, RadioGroup, Transition } from '@headlessui/react' import clsx from 'clsx' @@ -26,11 +27,11 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void const { appId, closeModal } = props const { data: appEnvsData } = useQuery(GetAppEnvironments, { - variables: { appId } + variables: { appId }, }) const { data: credentialsData } = useQuery(GetSavedCredentials, { - variables: { orgId: organisation!.id } + variables: { orgId: organisation!.id }, }) const [getVercelProjects, { loading }] = useLazyQuery(GetVercelProjects) @@ -38,15 +39,18 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void const [createVercelSync, { loading: creating }] = useMutation(CreateNewVercelSync) const [credential, setCredential] = useState(null) - const [vercelProjects, setVercelProjects] = useState([]) + const [vercelTeams, setVercelTeams] = useState([]) + const [vercelTeam, setVercelTeam] = useState(null) + const [teamQuery, setTeamQuery] = useState('') + const [vercelProject, setVercelProject] = useState(null) const [projectQuery, setProjectQuery] = useState('') - + const [phaseEnv, setPhaseEnv] = useState(null) const [path, setPath] = useState('/') const [environment, setEnvironment] = useState('production') const [secretType, setSecretType] = useState('encrypted') - + const [credentialsValid, setCredentialsValid] = useState(false) // Preselect first available credential @@ -72,10 +76,10 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void } else if (!credentialsValid) { try { const { data: projectsData } = await getVercelProjects({ - variables: { credentialId: credential.id } + variables: { credentialId: credential.id }, }) if (projectsData?.vercelProjects) { - setVercelProjects(projectsData.vercelProjects) + setVercelTeams(projectsData.vercelProjects) setCredentialsValid(true) } } catch (error: any) { @@ -93,10 +97,12 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void credentialId: credential.id, projectId: vercelProject.id, projectName: vercelProject.name, + teamId: vercelTeam?.id, + teamName: vercelTeam?.teamName, environment, - secretType + secretType, }, - refetchQueries: [{ query: GetAppSyncStatus, variables: { appId } }] + refetchQueries: [{ query: GetAppSyncStatus, variables: { appId } }], }) toast.success('Created new Sync!') @@ -107,18 +113,23 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void } } + const filteredTeams = + teamQuery === '' + ? vercelTeams + : vercelTeams.filter((team) => team.teamName?.toLowerCase().includes(teamQuery.toLowerCase())) - const filteredProjects = projectQuery === '' - ? vercelProjects - : vercelProjects.filter((project) => - project.name?.toLowerCase().includes(projectQuery.toLowerCase()) - ) + const filteredProjects = + projectQuery === '' + ? vercelTeam?.projects + : vercelTeam?.projects?.filter((project) => + project!.name?.toLowerCase().includes(projectQuery.toLowerCase()) + ) const environments = ['production', 'preview', 'development', 'all'] const secretTypes = [ { value: 'plain', label: 'Plain Text' }, { value: 'encrypted', label: 'Encrypted' }, - { value: 'sensitive', label: 'Sensitive' } + { value: 'sensitive', label: 'Sensitive' }, ] return ( @@ -128,9 +139,7 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void Vercel -
- Sync an environment with Vercel. -
+
Sync an environment with Vercel.
@@ -177,11 +186,7 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void checked && 'bg-zinc-700' )} > - {checked ? ( - - ) : ( - - )} + {checked ? : } {env.name} )} @@ -200,29 +205,31 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void
-
- +
+ {({ open }) => ( <>
setProjectQuery(event.target.value)} - displayValue={(project: VercelProjectType) => project?.name!} + onChange={(event) => setTeamQuery(event.target.value)} + displayValue={(team: VercelTeamProjectsType) => team?.teamName!} required />
- +
@@ -237,19 +244,19 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void >
- {filteredProjects.map((project) => ( - + {filteredTeams.map((team) => ( + {({ active }) => ( -
+
- {project.name} -
-
- {project.id} + {team.teamName}
+
{team.id}
)} @@ -261,6 +268,73 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void )}
+ {vercelTeam ? ( +
+ + {({ open }) => ( + <> +
+ + + +
+ setProjectQuery(event.target.value)} + displayValue={(project: VercelProjectType) => project?.name!} + required + /> +
+ + + +
+
+
+ + +
+ {filteredProjects!.map((project) => ( + + {({ active }) => ( +
+
+ {project!.name} +
+
{project!.id}
+
+ )} +
+ ))} +
+
+
+ + )} +
+
+ ) : ( +
+ )}
@@ -273,16 +347,14 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void {environments.map((env) => ( {({ active, checked }) => ( -
- {checked ? ( - - ) : ( - +
+ {checked ? : } {env}
)} @@ -303,16 +375,14 @@ export const CreateVercelSync = (props: { appId: string; closeModal: () => void {secretTypes.map((type) => ( {({ active, checked }) => ( -
- {checked ? ( - - ) : ( - +
+ {checked ? : } {type.label}
)} diff --git a/frontend/graphql/mutations/syncing/vercel/createVercelSync.gql b/frontend/graphql/mutations/syncing/vercel/createVercelSync.gql index 569dbe96..864db7bd 100644 --- a/frontend/graphql/mutations/syncing/vercel/createVercelSync.gql +++ b/frontend/graphql/mutations/syncing/vercel/createVercelSync.gql @@ -4,6 +4,8 @@ mutation CreateNewVercelSync( $credentialId: ID! $projectId: String! $projectName: String! + $teamId: String! + $teamName: String! $environment: String! $secretType: String! ) { @@ -13,6 +15,8 @@ mutation CreateNewVercelSync( credentialId: $credentialId projectId: $projectId projectName: $projectName + teamId: $teamId + teamName: $teamName environment: $environment secretType: $secretType ) { @@ -32,4 +36,4 @@ mutation CreateNewVercelSync( createdAt } } -} \ No newline at end of file +} diff --git a/frontend/graphql/queries/syncing/vercel/getProject.gql b/frontend/graphql/queries/syncing/vercel/getProject.gql index 00abf0a6..9c4c2d5e 100644 --- a/frontend/graphql/queries/syncing/vercel/getProject.gql +++ b/frontend/graphql/queries/syncing/vercel/getProject.gql @@ -1,7 +1,11 @@ query GetVercelProjects($credentialId: ID!) { vercelProjects(credentialId: $credentialId) { id - name - environment + teamName + projects { + id + name + environment + } } -} \ No newline at end of file +}