diff --git a/packages/eas-cli/graphql-codegen.yml b/packages/eas-cli/graphql-codegen.yml index 373266f4cb..32741105eb 100644 --- a/packages/eas-cli/graphql-codegen.yml +++ b/packages/eas-cli/graphql-codegen.yml @@ -5,8 +5,6 @@ documents: - 'src/credentials/ios/api/graphql/**/!(*.d).{ts,tsx}' - 'src/credentials/android/api/graphql/**/!(*.d).{ts,tsx}' - 'src/commands/**/*.ts' - - 'src/branch/**/*.ts' - - 'src/channel/**/*.ts' generates: src/graphql/generated.ts: plugins: @@ -16,7 +14,7 @@ generates: dedupeOperationSuffix: true hooks: afterOneFileWrite: - - 'node ./scripts/annotate-graphql-codegen.js' + - ./annotate-graphql-codegen.sh ./graphql.schema.json: plugins: - 'introspection' diff --git a/packages/eas-cli/graphql.schema.json b/packages/eas-cli/graphql.schema.json index 3e04fcb88f..9d8f9dfaa2 100644 --- a/packages/eas-cli/graphql.schema.json +++ b/packages/eas-cli/graphql.schema.json @@ -4726,6 +4726,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "googleServiceAccountKeyForFcmV1", + "description": null, + "args": [], + "type": { + "kind": "OBJECT", + "name": "GoogleServiceAccountKey", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "googleServiceAccountKeyForSubmissions", "description": null, @@ -4829,6 +4841,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "googleServiceAccountKeyForFcmV1Id", + "description": null, + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "googleServiceAccountKeyForSubmissionsId", "description": null, @@ -4916,6 +4940,87 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "createFcmV1Credential", + "description": "Create a GoogleServiceAccountKeyEntity to store credential and\nconnect it with an edge from AndroidAppCredential", + "args": [ + { + "name": "accountId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "androidAppCredentialsId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "appFullName", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "credential", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "AndroidAppCredentials", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "deleteAndroidAppCredentials", "description": "Delete a set of credentials for an Android app", @@ -4998,6 +5103,55 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "setGoogleServiceAccountKeyForFcmV1", + "description": "Set the Google Service Account Key to be used for Firebase Cloud Messaging V1", + "args": [ + { + "name": "googleServiceAccountKeyId", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "AndroidAppCredentials", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "setGoogleServiceAccountKeyForSubmissions", "description": "Set the Google Service Account Key to be used for submitting an Android app", diff --git a/packages/eas-cli/src/credentials/android/actions/AssignGoogleServiceAccountKeyForFcmV1.ts b/packages/eas-cli/src/credentials/android/actions/AssignGoogleServiceAccountKeyForFcmV1.ts new file mode 100644 index 0000000000..95ca349863 --- /dev/null +++ b/packages/eas-cli/src/credentials/android/actions/AssignGoogleServiceAccountKeyForFcmV1.ts @@ -0,0 +1,33 @@ +import { + CommonAndroidAppCredentialsFragment, + GoogleServiceAccountKeyFragment, +} from '../../../graphql/generated'; +import Log from '../../../log'; +import { CredentialsContext } from '../../context'; +import { AppLookupParams } from '../api/GraphqlClient'; + +export class AssignGoogleServiceAccountKeyForFcmV1 { + constructor(private app: AppLookupParams) {} + + public async runAsync( + ctx: CredentialsContext, + googleServiceAccountKey: GoogleServiceAccountKeyFragment + ): Promise { + const appCredentials = + await ctx.android.createOrGetExistingAndroidAppCredentialsWithBuildCredentialsAsync( + ctx.graphqlClient, + this.app + ); + const updatedAppCredentials = await ctx.android.updateAndroidAppCredentialsAsync( + ctx.graphqlClient, + appCredentials, + { + googleServiceAccountKeyForFcmV1Id: googleServiceAccountKey.id, + } + ); + Log.succeed( + `Google Service Account Key assigned to ${this.app.androidApplicationIdentifier} for FCM V1` + ); + return updatedAppCredentials; + } +} diff --git a/packages/eas-cli/src/credentials/android/actions/CreateGoogleServiceAccountKeyForFcmV1.ts b/packages/eas-cli/src/credentials/android/actions/CreateGoogleServiceAccountKeyForFcmV1.ts new file mode 100644 index 0000000000..85ff81bd59 --- /dev/null +++ b/packages/eas-cli/src/credentials/android/actions/CreateGoogleServiceAccountKeyForFcmV1.ts @@ -0,0 +1,76 @@ +import chalk from 'chalk'; +import fs from 'fs-extra'; + +import { AccountFragment, GoogleServiceAccountKeyFragment } from '../../../graphql/generated'; +import Log, { learnMore } from '../../../log'; +import { promptAsync } from '../../../prompts'; +import { CredentialsContext } from '../../context'; +import { GoogleServiceAccountKey } from '../credentials'; +import { + detectGoogleServiceAccountKeyPathAsync, + readAndValidateServiceAccountKey, +} from '../utils/googleServiceAccountKey'; + +export class CreateGoogleServiceAccountKeyForFcmV1 { + constructor(private account: AccountFragment) {} + + public async runAsync(ctx: CredentialsContext): Promise { + if (ctx.nonInteractive) { + throw new Error(`New FCM V1 Service Account Key cannot be created in non-interactive mode.`); + } + const jsonKeyObject = await this.provideAsync(ctx); + const gsaKeyFragment = await ctx.android.createGoogleServiceAccountKeyAsync( + ctx.graphqlClient, + this.account, + jsonKeyObject + ); + Log.succeed('Uploaded FCM V1 Service Account Key.'); + return gsaKeyFragment; + } + + private async provideAsync(ctx: CredentialsContext): Promise { + try { + const keyJsonPath = await this.provideKeyJsonPathAsync(ctx); + return readAndValidateServiceAccountKey(keyJsonPath); + } catch (e) { + Log.error(e); + return await this.provideAsync(ctx); + } + } + + private async provideKeyJsonPathAsync(ctx: CredentialsContext): Promise { + const detectedPath = await detectGoogleServiceAccountKeyPathAsync(ctx.projectDir); + if (detectedPath) { + return detectedPath; + } + + Log.log( + `${chalk.bold( + 'A Google Service Account JSON key is required to send Android notifications via FCM V1' + )}.\n` + + `If you're not sure what this is or how to create one, ${learnMore( + 'https://expo.fyi/creating-google-service-account-for-fcm-v1', + { learnMoreMessage: 'learn more' } + )}` + ); + const { filePath } = await promptAsync({ + name: 'filePath', + message: 'Path to Google Service Account file:', + initial: 'api-0000000000000000000-111111-aaaaaabbbbbb.json', + type: 'text', + // eslint-disable-next-line async-protect/async-suffix + validate: async (filePath: string) => { + try { + const stats = await fs.stat(filePath); + if (stats.isFile()) { + return true; + } + return 'Input is not a file.'; + } catch { + return 'File does not exist.'; + } + }, + }); + return filePath; + } +} diff --git a/packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKeyForFcmV1.ts b/packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKeyForFcmV1.ts new file mode 100644 index 0000000000..d8fabe1656 --- /dev/null +++ b/packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKeyForFcmV1.ts @@ -0,0 +1,87 @@ +import nullthrows from 'nullthrows'; + +import { AssignGoogleServiceAccountKeyForFcmV1 } from './AssignGoogleServiceAccountKeyForFcmV1'; +import { CreateGoogleServiceAccountKeyForFcmV1 } from './CreateGoogleServiceAccountKeyForFcmV1'; +import { UseExistingGoogleServiceAccountKey } from './UseExistingGoogleServiceAccountKey'; +import { + CommonAndroidAppCredentialsFragment, + GoogleServiceAccountKeyFragment, +} from '../../../graphql/generated'; +import Log from '../../../log'; +import { promptAsync } from '../../../prompts'; +import { CredentialsContext } from '../../context'; +import { MissingCredentialsNonInteractiveError } from '../../errors'; +import { AppLookupParams } from '../api/GraphqlClient'; + +export class SetUpGoogleServiceAccountKeyForFcmV1 { + constructor(private app: AppLookupParams) {} + + public async runAsync(ctx: CredentialsContext): Promise { + const isKeySetup = await this.isGoogleServiceAccountKeySetupAsync(ctx); + if (isKeySetup) { + Log.succeed('Google Service Account Key for FCM V1 already set up.'); + return nullthrows( + await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync( + ctx.graphqlClient, + this.app + ), + 'androidAppCredentials cannot be null if google service account key is already set up' + ); + } + if (ctx.nonInteractive) { + throw new MissingCredentialsNonInteractiveError( + 'Google Service Account Keys cannot be set up in --non-interactive mode.' + ); + } + + const keysForAccount = await ctx.android.getGoogleServiceAccountKeysForAccountAsync( + ctx.graphqlClient, + this.app.account + ); + let googleServiceAccountKey = null; + if (keysForAccount.length === 0) { + googleServiceAccountKey = await new CreateGoogleServiceAccountKeyForFcmV1( + this.app.account + ).runAsync(ctx); + } else { + googleServiceAccountKey = await this.createOrUseExistingKeyAsync(ctx); + } + return await new AssignGoogleServiceAccountKeyForFcmV1(this.app).runAsync( + ctx, + googleServiceAccountKey + ); + } + + private async isGoogleServiceAccountKeySetupAsync(ctx: CredentialsContext): Promise { + const appCredentials = await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync( + ctx.graphqlClient, + this.app + ); + return !!appCredentials?.googleServiceAccountKeyForFcmV1; + } + + private async createOrUseExistingKeyAsync( + ctx: CredentialsContext + ): Promise { + const { action } = await promptAsync({ + type: 'select', + name: 'action', + message: 'Select the Google Service Account Key to use for FCM V1:', + choices: [ + { + title: '[Choose an existing key]', + value: 'CHOOSE_EXISTING', + }, + { title: '[Upload a new service account key]', value: 'GENERATE' }, + ], + }); + + if (action === 'GENERATE') { + return await new CreateGoogleServiceAccountKeyForFcmV1(this.app.account).runAsync(ctx); + } + return ( + (await new UseExistingGoogleServiceAccountKey(this.app.account).runAsync(ctx)) ?? + (await this.createOrUseExistingKeyAsync(ctx)) + ); + } +} diff --git a/packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKey.ts b/packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKeyForSubmissions.ts similarity index 98% rename from packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKey.ts rename to packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKeyForSubmissions.ts index 41c747b9f4..05df712f7d 100644 --- a/packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKey.ts +++ b/packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKeyForSubmissions.ts @@ -13,7 +13,7 @@ import { CredentialsContext } from '../../context'; import { MissingCredentialsNonInteractiveError } from '../../errors'; import { AppLookupParams } from '../api/GraphqlClient'; -export class SetUpGoogleServiceAccountKey { +export class SetUpGoogleServiceAccountKeyForSubmissions { constructor(private app: AppLookupParams) {} public async runAsync(ctx: CredentialsContext): Promise { diff --git a/packages/eas-cli/src/credentials/android/actions/__tests__/SetUpGoogleServiceAccountKey-test.ts b/packages/eas-cli/src/credentials/android/actions/__tests__/SetUpGoogleServiceAccountKey-test.ts index c0fb3201f4..498487a63e 100644 --- a/packages/eas-cli/src/credentials/android/actions/__tests__/SetUpGoogleServiceAccountKey-test.ts +++ b/packages/eas-cli/src/credentials/android/actions/__tests__/SetUpGoogleServiceAccountKey-test.ts @@ -11,7 +11,7 @@ import { testAppQueryByIdResponse } from '../../../__tests__/fixtures-constants' import { createCtxMock } from '../../../__tests__/fixtures-context'; import { MissingCredentialsNonInteractiveError } from '../../../errors'; import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils'; -import { SetUpGoogleServiceAccountKey } from '../SetUpGoogleServiceAccountKey'; +import { SetUpGoogleServiceAccountKeyForSubmissions } from '../SetUpGoogleServiceAccountKeyForSubmissions'; jest.mock('../../../../prompts'); jest.mock('fs'); @@ -24,7 +24,7 @@ beforeEach(() => { vol.reset(); }); -describe(SetUpGoogleServiceAccountKey, () => { +describe(SetUpGoogleServiceAccountKeyForSubmissions, () => { beforeEach(() => { jest.mocked(AppQuery.byIdAsync).mockResolvedValue(testAppQueryByIdResponse); }); @@ -39,7 +39,9 @@ describe(SetUpGoogleServiceAccountKey, () => { }, }); const appLookupParams = await getAppLookupParamsFromContextAsync(ctx); - const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams); + const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKeyForSubmissions( + appLookupParams + ); await setupGoogleServiceAccountKeyAction.runAsync(ctx); expect(ctx.android.createGoogleServiceAccountKeyAsync).not.toHaveBeenCalled(); @@ -62,7 +64,9 @@ describe(SetUpGoogleServiceAccountKey, () => { }, }); const appLookupParams = await getAppLookupParamsFromContextAsync(ctx); - const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams); + const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKeyForSubmissions( + appLookupParams + ); await setupGoogleServiceAccountKeyAction.runAsync(ctx); expect(ctx.android.createGoogleServiceAccountKeyAsync).toHaveBeenCalledTimes(1); @@ -73,7 +77,9 @@ describe(SetUpGoogleServiceAccountKey, () => { nonInteractive: true, }); const appLookupParams = await getAppLookupParamsFromContextAsync(ctx); - const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams); + const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKeyForSubmissions( + appLookupParams + ); await expect(setupGoogleServiceAccountKeyAction.runAsync(ctx)).rejects.toThrowError( MissingCredentialsNonInteractiveError ); diff --git a/packages/eas-cli/src/credentials/android/api/GraphqlClient.ts b/packages/eas-cli/src/credentials/android/api/GraphqlClient.ts index 83615bb2fc..f6627ac324 100644 --- a/packages/eas-cli/src/credentials/android/api/GraphqlClient.ts +++ b/packages/eas-cli/src/credentials/android/api/GraphqlClient.ts @@ -106,9 +106,11 @@ export async function updateAndroidAppCredentialsAsync( { androidFcmId, googleServiceAccountKeyForSubmissionsId, + googleServiceAccountKeyForFcmV1Id, }: { androidFcmId?: string; googleServiceAccountKeyForSubmissionsId?: string; + googleServiceAccountKeyForFcmV1Id?: string; } ): Promise { let updatedAppCredentials = appCredentials; @@ -127,6 +129,14 @@ export async function updateAndroidAppCredentialsAsync( googleServiceAccountKeyForSubmissionsId ); } + if (googleServiceAccountKeyForFcmV1Id) { + updatedAppCredentials = + await AndroidAppCredentialsMutation.setGoogleServiceAccountKeyForFcmV1Async( + graphqlClient, + appCredentials.id, + googleServiceAccountKeyForFcmV1Id + ); + } return updatedAppCredentials; } diff --git a/packages/eas-cli/src/credentials/android/api/graphql/mutations/AndroidAppCredentialsMutation.ts b/packages/eas-cli/src/credentials/android/api/graphql/mutations/AndroidAppCredentialsMutation.ts index e69d8e66c9..d7c22a88ff 100644 --- a/packages/eas-cli/src/credentials/android/api/graphql/mutations/AndroidAppCredentialsMutation.ts +++ b/packages/eas-cli/src/credentials/android/api/graphql/mutations/AndroidAppCredentialsMutation.ts @@ -8,6 +8,7 @@ import { CommonAndroidAppCredentialsFragment, CreateAndroidAppCredentialsMutation, SetFcmMutation, + SetGoogleServiceAccountKeyForFcmV1Mutation, SetGoogleServiceAccountKeyForSubmissionsMutation, } from '../../../../../graphql/generated'; import { CommonAndroidAppCredentialsFragmentNode } from '../../../../../graphql/types/credentials/AndroidAppCredentials'; @@ -124,4 +125,42 @@ export const AndroidAppCredentialsMutation = { ); return data.androidAppCredentials.setGoogleServiceAccountKeyForSubmissions; }, + async setGoogleServiceAccountKeyForFcmV1Async( + graphqlClient: ExpoGraphqlClient, + androidAppCredentialsId: string, + googleServiceAccountKeyId: string + ): Promise { + const data = await withErrorHandlingAsync( + graphqlClient + .mutation( + gql` + mutation SetGoogleServiceAccountKeyForFcmV1Mutation( + $androidAppCredentialsId: ID! + $googleServiceAccountKeyId: ID! + ) { + androidAppCredentials { + setGoogleServiceAccountKeyForFcmV1( + id: $androidAppCredentialsId + googleServiceAccountKeyId: $googleServiceAccountKeyId + ) { + id + ...CommonAndroidAppCredentialsFragment + } + } + } + ${print(CommonAndroidAppCredentialsFragmentNode)} + `, + { + androidAppCredentialsId, + googleServiceAccountKeyId, + } + ) + .toPromise() + ); + assert( + data.androidAppCredentials.setGoogleServiceAccountKeyForFcmV1, + 'GraphQL: `setGoogleServiceAccountKeyForFcmV1` not defined in server response' + ); + return data.androidAppCredentials.setGoogleServiceAccountKeyForFcmV1; + }, }; diff --git a/packages/eas-cli/src/credentials/android/utils/googleServiceAccountKey.ts b/packages/eas-cli/src/credentials/android/utils/googleServiceAccountKey.ts index c659c95e64..8ba0803287 100644 --- a/packages/eas-cli/src/credentials/android/utils/googleServiceAccountKey.ts +++ b/packages/eas-cli/src/credentials/android/utils/googleServiceAccountKey.ts @@ -96,7 +96,7 @@ export async function detectGoogleServiceAccountKeyPathAsync( ): Promise { const foundFilePaths = await glob('**/*.json', { cwd: projectDir, - ignore: ['app.json', 'package*.json', 'tsconfig.json', 'node_modules'], + ignore: ['app.json', 'package*.json', 'tsconfig.json', 'node_modules', 'google-services.json'], }); const googleServiceFiles = foundFilePaths diff --git a/packages/eas-cli/src/credentials/android/utils/printCredentials.ts b/packages/eas-cli/src/credentials/android/utils/printCredentials.ts index dae3b1d586..894bbdeb4d 100644 --- a/packages/eas-cli/src/credentials/android/utils/printCredentials.ts +++ b/packages/eas-cli/src/credentials/android/utils/printCredentials.ts @@ -29,7 +29,7 @@ export function displayEmptyAndroidCredentials(appLookupParams: AppLookupParams) function displayAndroidFcmCredentials(appCredentials: CommonAndroidAppCredentialsFragment): void { const maybeFcm = appCredentials.androidFcm; Log.log( - formatFields([{ label: 'Push Notifications (FCM)', value: '' }], { + formatFields([{ label: 'Push Notifications (FCM Legacy)', value: '' }], { labelFormat: chalk.cyan.bold, }) ); @@ -60,9 +60,43 @@ function displayGoogleServiceAccountKeyForSubmissions( ): void { const maybeGsaKey = appCredentials.googleServiceAccountKeyForSubmissions; Log.log( - formatFields([{ label: 'Google Service Account Key For Submissions', value: '' }], { - labelFormat: chalk.cyan.bold, - }) + formatFields( + [{ label: 'Submissions: Google Service Account Key for Play Store Submissions', value: '' }], + { + labelFormat: chalk.cyan.bold, + } + ) + ); + if (!maybeGsaKey) { + Log.log(formatFields([{ label: '', value: 'None assigned yet' }])); + Log.newLine(); + return; + } + const { projectIdentifier, privateKeyIdentifier, clientEmail, clientIdentifier, updatedAt } = + maybeGsaKey; + + const fields = [ + { label: 'Project ID', value: projectIdentifier }, + { label: 'Client Email', value: clientEmail }, + { label: 'Client ID', value: clientIdentifier }, + { label: 'Private Key ID', value: privateKeyIdentifier }, + { label: 'Updated', value: `${fromNow(new Date(updatedAt))} ago` }, + ]; + Log.log(formatFields(fields, { labelFormat: chalk.cyan.bold })); + Log.newLine(); +} + +function displayGoogleServiceAccountKeyForFcmV1( + appCredentials: CommonAndroidAppCredentialsFragment +): void { + const maybeGsaKey = appCredentials.googleServiceAccountKeyForFcmV1; + Log.log( + formatFields( + [{ label: 'Push Notifications (FCM V1): Google Service Account Key For FCM V1', value: '' }], + { + labelFormat: chalk.cyan.bold, + } + ) ); if (!maybeGsaKey) { Log.log(formatFields([{ label: '', value: 'None assigned yet' }])); @@ -87,6 +121,7 @@ function displayEASAndroidAppCredentials( appCredentials: CommonAndroidAppCredentialsFragment ): void { displayAndroidFcmCredentials(appCredentials); + displayGoogleServiceAccountKeyForFcmV1(appCredentials); displayGoogleServiceAccountKeyForSubmissions(appCredentials); const sortedBuildCredentialsList = sortBuildCredentials( appCredentials.androidAppBuildCredentialsList diff --git a/packages/eas-cli/src/credentials/manager/Actions.ts b/packages/eas-cli/src/credentials/manager/Actions.ts index 6d9b3fc57c..dfea7a0ffa 100644 --- a/packages/eas-cli/src/credentials/manager/Actions.ts +++ b/packages/eas-cli/src/credentials/manager/Actions.ts @@ -13,7 +13,9 @@ export enum Scope { export enum AndroidActionType { ManageBuildCredentials, ManageFcm, - ManageGoogleServiceAccountKey, + ManageGoogleServiceAccountKeyForSubmissions, + ManageGoogleServiceAccount, + ManageGoogleServiceAccountKeyForFcmV1, ManageCredentialsJson, GoBackToCaller, GoBackToHighLevelActions, @@ -24,9 +26,13 @@ export enum AndroidActionType { CreateFcm, RemoveFcm, CreateGsaKey, - UseExistingGsaKey, + UseExistingGsaKeyForSubmissions, RemoveGsaKey, - SetUpGsaKey, + SetUpGsaKeyForSubmissions, + CreateGsaKeyForFcmV1, + UseExistingGsaKeyForFcmV1, + RemoveGsaKeyForFcmV1, + SetUpGsaKeyForFcmV1, UpdateCredentialsJson, SetUpBuildCredentialsFromCredentialsJson, } diff --git a/packages/eas-cli/src/credentials/manager/AndroidActions.ts b/packages/eas-cli/src/credentials/manager/AndroidActions.ts index 644ab67cb0..ab760d3c50 100644 --- a/packages/eas-cli/src/credentials/manager/AndroidActions.ts +++ b/packages/eas-cli/src/credentials/manager/AndroidActions.ts @@ -7,13 +7,13 @@ export const highLevelActions: ActionInfo[] = [ scope: Scope.Manager, }, { - value: AndroidActionType.ManageFcm, - title: 'Push Notifications: Manage your FCM API Key', + value: AndroidActionType.ManageGoogleServiceAccount, + title: 'Google Service Account', scope: Scope.Manager, }, { - value: AndroidActionType.ManageGoogleServiceAccountKey, - title: 'Google Service Account: Manage your Service Account Key', + value: AndroidActionType.ManageFcm, + title: 'Push Notifications (Legacy): Manage your FCM (Legacy) API Key', scope: Scope.Manager, }, { @@ -92,25 +92,61 @@ export const fcmActions: ActionInfo[] = [ }, ]; -export const gsaKeyActions: ActionInfo[] = [ +export const gsaKeyActionsForFcmV1: ActionInfo[] = [ { - value: AndroidActionType.SetUpGsaKey, - title: 'Set up a Google Service Account Key', + value: AndroidActionType.SetUpGsaKeyForFcmV1, + title: 'Set up a Google Service Account Key for Push Notifications (FCM V1)', scope: Scope.Project, }, { - value: AndroidActionType.CreateGsaKey, - title: 'Upload a Google Service Account Key', + value: AndroidActionType.UseExistingGsaKeyForFcmV1, + title: 'Select an existing Google Service Account Key for Push Notifications (FCM V1)', + scope: Scope.Project, + }, + { + value: AndroidActionType.GoBackToHighLevelActions, + title: 'Go back', + scope: Scope.Manager, + }, +]; + +export const gsaKeyActionsForSubmissions: ActionInfo[] = [ + { + value: AndroidActionType.SetUpGsaKeyForSubmissions, + title: 'Set up a Google Service Account Key for Play Store Submissions', + scope: Scope.Project, + }, + { + value: AndroidActionType.UseExistingGsaKeyForSubmissions, + title: 'Select an existing Google Service Account Key for Play Store Submissions', scope: Scope.Project, }, { - value: AndroidActionType.UseExistingGsaKey, - title: 'Use an existing Google Service Account Key', + value: AndroidActionType.GoBackToHighLevelActions, + title: 'Go back', + scope: Scope.Manager, + }, +]; + +export const gsaActions: ActionInfo[] = [ + { + value: AndroidActionType.ManageGoogleServiceAccountKeyForSubmissions, + title: 'Manage your Google Service Account Key for Play Store Submissions', + scope: Scope.Manager, + }, + { + value: AndroidActionType.ManageGoogleServiceAccountKeyForFcmV1, + title: 'Manage your Google Service Account Key for Push Notifications (FCM V1)', + scope: Scope.Manager, + }, + { + value: AndroidActionType.CreateGsaKey, + title: 'Upload a Google Service Account Key to the Keystore', scope: Scope.Project, }, { value: AndroidActionType.RemoveGsaKey, - title: 'Delete a Google Service Account Key', + title: 'Delete a Google Service Account Key from the Keystore', scope: Scope.Project, }, { diff --git a/packages/eas-cli/src/credentials/manager/ManageAndroid.ts b/packages/eas-cli/src/credentials/manager/ManageAndroid.ts index f1d71267d1..635aa7bc7d 100644 --- a/packages/eas-cli/src/credentials/manager/ManageAndroid.ts +++ b/packages/eas-cli/src/credentials/manager/ManageAndroid.ts @@ -7,7 +7,9 @@ import { buildCredentialsActions, credentialsJsonActions, fcmActions, - gsaKeyActions, + gsaActions, + gsaKeyActionsForFcmV1, + gsaKeyActionsForSubmissions, highLevelActions, } from './AndroidActions'; import { CreateAndroidBuildCredentials } from './CreateAndroidBuildCredentials'; @@ -20,6 +22,7 @@ import { GradleBuildContext, resolveGradleBuildContextAsync } from '../../projec import { promptAsync } from '../../prompts'; import { AssignFcm } from '../android/actions/AssignFcm'; import { AssignGoogleServiceAccountKey } from '../android/actions/AssignGoogleServiceAccountKey'; +import { AssignGoogleServiceAccountKeyForFcmV1 } from '../android/actions/AssignGoogleServiceAccountKeyForFcmV1'; import { canCopyLegacyCredentialsAsync, getAppLookupParamsFromContextAsync, @@ -32,7 +35,8 @@ import { RemoveFcm } from '../android/actions/RemoveFcm'; import { SelectAndRemoveGoogleServiceAccountKey } from '../android/actions/RemoveGoogleServiceAccountKey'; import { RemoveKeystore } from '../android/actions/RemoveKeystore'; import { SetUpBuildCredentialsFromCredentialsJson } from '../android/actions/SetUpBuildCredentialsFromCredentialsJson'; -import { SetUpGoogleServiceAccountKey } from '../android/actions/SetUpGoogleServiceAccountKey'; +import { SetUpGoogleServiceAccountKeyForFcmV1 } from '../android/actions/SetUpGoogleServiceAccountKeyForFcmV1'; +import { SetUpGoogleServiceAccountKeyForSubmissions } from '../android/actions/SetUpGoogleServiceAccountKeyForSubmissions'; import { UpdateCredentialsJson } from '../android/actions/UpdateCredentialsJson'; import { UseExistingGoogleServiceAccountKey } from '../android/actions/UseExistingGoogleServiceAccountKey'; import { @@ -120,8 +124,16 @@ export class ManageAndroid { } else if (chosenAction === AndroidActionType.ManageFcm) { currentActions = fcmActions; continue; - } else if (chosenAction === AndroidActionType.ManageGoogleServiceAccountKey) { - currentActions = gsaKeyActions; + } else if (chosenAction === AndroidActionType.ManageGoogleServiceAccount) { + currentActions = gsaActions; + continue; + } else if ( + chosenAction === AndroidActionType.ManageGoogleServiceAccountKeyForSubmissions + ) { + currentActions = gsaKeyActionsForSubmissions; + continue; + } else if (chosenAction === AndroidActionType.ManageGoogleServiceAccountKeyForFcmV1) { + currentActions = gsaKeyActionsForFcmV1; continue; } else if (chosenAction === AndroidActionType.ManageCredentialsJson) { currentActions = credentialsJsonActions; @@ -193,20 +205,28 @@ export class ManageAndroid { await new AssignFcm(appLookupParams).runAsync(ctx, fcm); } else if (action === AndroidActionType.RemoveFcm) { await new RemoveFcm(appLookupParams).runAsync(ctx); - } else if (action === AndroidActionType.CreateGsaKey) { - const gsaKey = await new CreateGoogleServiceAccountKey(appLookupParams.account).runAsync(ctx); - await new AssignGoogleServiceAccountKey(appLookupParams).runAsync(ctx, gsaKey); - } else if (action === AndroidActionType.UseExistingGsaKey) { + } else if (action === AndroidActionType.SetUpGsaKeyForSubmissions) { + await new SetUpGoogleServiceAccountKeyForSubmissions(appLookupParams).runAsync(ctx); + } else if (action === AndroidActionType.UseExistingGsaKeyForSubmissions) { const gsaKey = await new UseExistingGoogleServiceAccountKey(appLookupParams.account).runAsync( ctx ); if (gsaKey) { await new AssignGoogleServiceAccountKey(appLookupParams).runAsync(ctx, gsaKey); } + } else if (action === AndroidActionType.SetUpGsaKeyForFcmV1) { + await new SetUpGoogleServiceAccountKeyForFcmV1(appLookupParams).runAsync(ctx); + } else if (action === AndroidActionType.UseExistingGsaKeyForFcmV1) { + const gsaKey = await new UseExistingGoogleServiceAccountKey(appLookupParams.account).runAsync( + ctx + ); + if (gsaKey) { + await new AssignGoogleServiceAccountKeyForFcmV1(appLookupParams).runAsync(ctx, gsaKey); + } + } else if (action === AndroidActionType.CreateGsaKey) { + await new CreateGoogleServiceAccountKey(appLookupParams.account).runAsync(ctx); } else if (action === AndroidActionType.RemoveGsaKey) { await new SelectAndRemoveGoogleServiceAccountKey(appLookupParams.account).runAsync(ctx); - } else if (action === AndroidActionType.SetUpGsaKey) { - await new SetUpGoogleServiceAccountKey(appLookupParams).runAsync(ctx); } else if (action === AndroidActionType.UpdateCredentialsJson) { const buildCredentials = await new SelectExistingAndroidBuildCredentials( appLookupParams diff --git a/packages/eas-cli/src/graphql/generated.ts b/packages/eas-cli/src/graphql/generated.ts index e7e6e9a6e6..b8b3728e14 100644 --- a/packages/eas-cli/src/graphql/generated.ts +++ b/packages/eas-cli/src/graphql/generated.ts @@ -759,6 +759,7 @@ export type AndroidAppCredentials = { androidFcm?: Maybe; app: App; applicationIdentifier?: Maybe; + googleServiceAccountKeyForFcmV1?: Maybe; googleServiceAccountKeyForSubmissions?: Maybe; id: Scalars['ID']['output']; isLegacy: Scalars['Boolean']['output']; @@ -771,6 +772,7 @@ export type AndroidAppCredentialsFilter = { export type AndroidAppCredentialsInput = { fcmId?: InputMaybe; + googleServiceAccountKeyForFcmV1Id?: InputMaybe; googleServiceAccountKeyForSubmissionsId?: InputMaybe; }; @@ -778,10 +780,17 @@ export type AndroidAppCredentialsMutation = { __typename?: 'AndroidAppCredentialsMutation'; /** Create a set of credentials for an Android app */ createAndroidAppCredentials: AndroidAppCredentials; + /** + * Create a GoogleServiceAccountKeyEntity to store credential and + * connect it with an edge from AndroidAppCredential + */ + createFcmV1Credential: AndroidAppCredentials; /** Delete a set of credentials for an Android app */ deleteAndroidAppCredentials: DeleteAndroidAppCredentialsResult; /** Set the FCM push key to be used in an Android app */ setFcm: AndroidAppCredentials; + /** Set the Google Service Account Key to be used for Firebase Cloud Messaging V1 */ + setGoogleServiceAccountKeyForFcmV1: AndroidAppCredentials; /** Set the Google Service Account Key to be used for submitting an Android app */ setGoogleServiceAccountKeyForSubmissions: AndroidAppCredentials; }; @@ -794,6 +803,14 @@ export type AndroidAppCredentialsMutationCreateAndroidAppCredentialsArgs = { }; +export type AndroidAppCredentialsMutationCreateFcmV1CredentialArgs = { + accountId: Scalars['ID']['input']; + androidAppCredentialsId: Scalars['String']['input']; + appFullName: Scalars['String']['input']; + credential: Scalars['String']['input']; +}; + + export type AndroidAppCredentialsMutationDeleteAndroidAppCredentialsArgs = { id: Scalars['ID']['input']; }; @@ -805,6 +822,12 @@ export type AndroidAppCredentialsMutationSetFcmArgs = { }; +export type AndroidAppCredentialsMutationSetGoogleServiceAccountKeyForFcmV1Args = { + googleServiceAccountKeyId: Scalars['ID']['input']; + id: Scalars['ID']['input']; +}; + + export type AndroidAppCredentialsMutationSetGoogleServiceAccountKeyForSubmissionsArgs = { googleServiceAccountKeyId: Scalars['ID']['input']; id: Scalars['ID']['input']; @@ -6110,7 +6133,7 @@ export type CreateAndroidAppCredentialsMutationVariables = Exact<{ }>; -export type CreateAndroidAppCredentialsMutation = { __typename?: 'RootMutation', androidAppCredentials: { __typename?: 'AndroidAppCredentialsMutation', createAndroidAppCredentials: { __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> } } }; +export type CreateAndroidAppCredentialsMutation = { __typename?: 'RootMutation', androidAppCredentials: { __typename?: 'AndroidAppCredentialsMutation', createAndroidAppCredentials: { __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForFcmV1?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> } } }; export type SetFcmMutationVariables = Exact<{ androidAppCredentialsId: Scalars['ID']['input']; @@ -6118,7 +6141,7 @@ export type SetFcmMutationVariables = Exact<{ }>; -export type SetFcmMutation = { __typename?: 'RootMutation', androidAppCredentials: { __typename?: 'AndroidAppCredentialsMutation', setFcm: { __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> } } }; +export type SetFcmMutation = { __typename?: 'RootMutation', androidAppCredentials: { __typename?: 'AndroidAppCredentialsMutation', setFcm: { __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForFcmV1?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> } } }; export type SetGoogleServiceAccountKeyForSubmissionsMutationVariables = Exact<{ androidAppCredentialsId: Scalars['ID']['input']; @@ -6126,7 +6149,15 @@ export type SetGoogleServiceAccountKeyForSubmissionsMutationVariables = Exact<{ }>; -export type SetGoogleServiceAccountKeyForSubmissionsMutation = { __typename?: 'RootMutation', androidAppCredentials: { __typename?: 'AndroidAppCredentialsMutation', setGoogleServiceAccountKeyForSubmissions: { __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> } } }; +export type SetGoogleServiceAccountKeyForSubmissionsMutation = { __typename?: 'RootMutation', androidAppCredentials: { __typename?: 'AndroidAppCredentialsMutation', setGoogleServiceAccountKeyForSubmissions: { __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForFcmV1?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> } } }; + +export type SetGoogleServiceAccountKeyForFcmV1MutationVariables = Exact<{ + androidAppCredentialsId: Scalars['ID']['input']; + googleServiceAccountKeyId: Scalars['ID']['input']; +}>; + + +export type SetGoogleServiceAccountKeyForFcmV1Mutation = { __typename?: 'RootMutation', androidAppCredentials: { __typename?: 'AndroidAppCredentialsMutation', setGoogleServiceAccountKeyForFcmV1: { __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForFcmV1?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> } } }; export type CreateAndroidFcmMutationVariables = Exact<{ androidFcmInput: AndroidFcmInput; @@ -6180,7 +6211,7 @@ export type CommonAndroidAppCredentialsWithBuildCredentialsByApplicationIdentifi }>; -export type CommonAndroidAppCredentialsWithBuildCredentialsByApplicationIdentifierQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byFullName: { __typename?: 'App', id: string, androidAppCredentials: Array<{ __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> }> } } }; +export type CommonAndroidAppCredentialsWithBuildCredentialsByApplicationIdentifierQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byFullName: { __typename?: 'App', id: string, androidAppCredentials: Array<{ __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForFcmV1?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> }> } } }; export type GoogleServiceAccountKeyByAccountQueryVariables = Exact<{ accountName: Scalars['String']['input']; @@ -6869,7 +6900,7 @@ export type WebhookFragment = { __typename?: 'Webhook', id: string, event: Webho export type AndroidAppBuildCredentialsFragment = { __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }; -export type CommonAndroidAppCredentialsFragment = { __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> }; +export type CommonAndroidAppCredentialsFragment = { __typename?: 'AndroidAppCredentials', id: string, applicationIdentifier?: string | null, isLegacy: boolean, app: { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } }, androidFcm?: { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } } | null, googleServiceAccountKeyForFcmV1?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, googleServiceAccountKeyForSubmissions?: { __typename?: 'GoogleServiceAccountKey', id: string, projectIdentifier: string, privateKeyIdentifier: string, clientEmail: string, clientIdentifier: string, createdAt: any, updatedAt: any } | null, androidAppBuildCredentialsList: Array<{ __typename?: 'AndroidAppBuildCredentials', id: string, isDefault: boolean, isLegacy: boolean, name: string, androidKeystore?: { __typename?: 'AndroidKeystore', id: string, type: AndroidKeystoreType, keystore: string, keystorePassword: string, keyAlias: string, keyPassword?: string | null, md5CertificateFingerprint?: string | null, sha1CertificateFingerprint?: string | null, sha256CertificateFingerprint?: string | null, createdAt: any, updatedAt: any } | null }> }; export type AndroidFcmFragment = { __typename?: 'AndroidFcm', id: string, credential: any, version: AndroidFcmVersion, createdAt: any, updatedAt: any, snippet: { __typename?: 'FcmSnippetLegacy', firstFourCharacters: string, lastFourCharacters: string } | { __typename?: 'FcmSnippetV1', projectId: string, keyId: string, serviceAccountEmail: string, clientId?: string | null } }; diff --git a/packages/eas-cli/src/graphql/types/credentials/AndroidAppCredentials.ts b/packages/eas-cli/src/graphql/types/credentials/AndroidAppCredentials.ts index ab1f8eb8db..bef4d2ff6d 100644 --- a/packages/eas-cli/src/graphql/types/credentials/AndroidAppCredentials.ts +++ b/packages/eas-cli/src/graphql/types/credentials/AndroidAppCredentials.ts @@ -18,6 +18,10 @@ export const CommonAndroidAppCredentialsFragmentNode = gql` id ...AndroidFcmFragment } + googleServiceAccountKeyForFcmV1 { + id + ...GoogleServiceAccountKeyFragment + } googleServiceAccountKeyForSubmissions { id ...GoogleServiceAccountKeyFragment diff --git a/packages/eas-cli/src/submit/android/ServiceAccountSource.ts b/packages/eas-cli/src/submit/android/ServiceAccountSource.ts index 74f1608b25..e226e2905e 100644 --- a/packages/eas-cli/src/submit/android/ServiceAccountSource.ts +++ b/packages/eas-cli/src/submit/android/ServiceAccountSource.ts @@ -3,7 +3,7 @@ import chalk from 'chalk'; import fs from 'fs-extra'; import nullthrows from 'nullthrows'; -import { SetUpGoogleServiceAccountKey } from '../../credentials/android/actions/SetUpGoogleServiceAccountKey'; +import { SetUpGoogleServiceAccountKeyForSubmissions } from '../../credentials/android/actions/SetUpGoogleServiceAccountKeyForSubmissions'; import { readAndValidateServiceAccountKey } from '../../credentials/android/utils/googleServiceAccountKey'; import Log, { learnMore } from '../../log'; import { @@ -126,7 +126,9 @@ export async function getServiceAccountFromCredentialsServiceAsync( Log.log( `Looking up credentials configuration for ${appLookupParams.androidApplicationIdentifier}...` ); - const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams); + const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKeyForSubmissions( + appLookupParams + ); const androidAppCredentials = await setupGoogleServiceAccountKeyAction.runAsync( ctx.credentialsCtx ); diff --git a/packages/eas-cli/src/submit/android/__tests__/ServiceAccountSource-test.ts b/packages/eas-cli/src/submit/android/__tests__/ServiceAccountSource-test.ts index 5af20d5f42..74350e86ca 100644 --- a/packages/eas-cli/src/submit/android/__tests__/ServiceAccountSource-test.ts +++ b/packages/eas-cli/src/submit/android/__tests__/ServiceAccountSource-test.ts @@ -11,7 +11,7 @@ import { jester as mockJester, testProjectId, } from '../../../credentials/__tests__/fixtures-constants'; -import { SetUpGoogleServiceAccountKey } from '../../../credentials/android/actions/SetUpGoogleServiceAccountKey'; +import { SetUpGoogleServiceAccountKeyForSubmissions } from '../../../credentials/android/actions/SetUpGoogleServiceAccountKeyForSubmissions'; import { createTestProject } from '../../../project/__tests__/project-utils'; import { getOwnerAccountForProjectIdAsync } from '../../../project/projectUtils'; import { promptAsync } from '../../../prompts'; @@ -55,7 +55,7 @@ beforeAll(() => { '/other_dir/invalid_file.txt': 'this is not even a JSON', }); jest - .spyOn(SetUpGoogleServiceAccountKey.prototype, 'runAsync') + .spyOn(SetUpGoogleServiceAccountKeyForSubmissions.prototype, 'runAsync') .mockImplementation(async () => testAndroidAppCredentialsFragment); }); afterAll(() => {