diff --git a/CHANGELOG.md b/CHANGELOG.md index cde67ff05a..44f5b91373 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ This is the log of notable changes to EAS CLI and related packages. ### ๐ŸŽ‰ New features +- Support configuring a Google Service Account Key via eas credentials, for sending Android Notifications via FCM V1. ([#2197](https://github.com/expo/eas-cli/pull/2197) by [@christopherwalter](https://github.com/christopherwalter)) + ### ๐Ÿ› Bug fixes ### ๐Ÿงน Chores 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/AssignGoogleServiceAccountKey.ts b/packages/eas-cli/src/credentials/android/actions/AssignGoogleServiceAccountKeyForSubmissions.ts similarity index 94% rename from packages/eas-cli/src/credentials/android/actions/AssignGoogleServiceAccountKey.ts rename to packages/eas-cli/src/credentials/android/actions/AssignGoogleServiceAccountKeyForSubmissions.ts index 5fd45fe4cd..629ad6d72d 100644 --- a/packages/eas-cli/src/credentials/android/actions/AssignGoogleServiceAccountKey.ts +++ b/packages/eas-cli/src/credentials/android/actions/AssignGoogleServiceAccountKeyForSubmissions.ts @@ -6,7 +6,7 @@ import Log from '../../../log'; import { CredentialsContext } from '../../context'; import { AppLookupParams } from '../api/GraphqlClient'; -export class AssignGoogleServiceAccountKey { +export class AssignGoogleServiceAccountKeyForSubmissions { constructor(private app: AppLookupParams) {} public async runAsync( diff --git a/packages/eas-cli/src/credentials/android/actions/CreateGoogleServiceAccountKey.ts b/packages/eas-cli/src/credentials/android/actions/CreateGoogleServiceAccountKey.ts index 65a4d4ed04..27fb7a27e1 100644 --- a/packages/eas-cli/src/credentials/android/actions/CreateGoogleServiceAccountKey.ts +++ b/packages/eas-cli/src/credentials/android/actions/CreateGoogleServiceAccountKey.ts @@ -46,7 +46,7 @@ export class CreateGoogleServiceAccountKey { Log.log( `${chalk.bold( - 'A Google Service Account JSON key is required to upload your app to Google Play Store' + 'A Google Service Account JSON key is required for uploading your app to Google Play Store, and for sending 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', 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..0dd999bc1b --- /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 { CreateGoogleServiceAccountKey } from './CreateGoogleServiceAccountKey'; +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 CreateGoogleServiceAccountKey(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 CreateGoogleServiceAccountKey(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 90% rename from packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKey.ts rename to packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKeyForSubmissions.ts index 41c747b9f4..22b3c539c9 100644 --- a/packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKey.ts +++ b/packages/eas-cli/src/credentials/android/actions/SetUpGoogleServiceAccountKeyForSubmissions.ts @@ -1,6 +1,6 @@ import nullthrows from 'nullthrows'; -import { AssignGoogleServiceAccountKey } from './AssignGoogleServiceAccountKey'; +import { AssignGoogleServiceAccountKeyForSubmissions } from './AssignGoogleServiceAccountKeyForSubmissions'; import { CreateGoogleServiceAccountKey } from './CreateGoogleServiceAccountKey'; import { UseExistingGoogleServiceAccountKey } from './UseExistingGoogleServiceAccountKey'; import { @@ -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 { @@ -46,7 +46,10 @@ export class SetUpGoogleServiceAccountKey { } else { googleServiceAccountKey = await this.createOrUseExistingKeyAsync(ctx); } - return await new AssignGoogleServiceAccountKey(this.app).runAsync(ctx, googleServiceAccountKey); + return await new AssignGoogleServiceAccountKeyForSubmissions(this.app).runAsync( + ctx, + googleServiceAccountKey + ); } private async isGoogleServiceAccountKeySetupAsync(ctx: CredentialsContext): Promise { diff --git a/packages/eas-cli/src/credentials/android/actions/__tests__/AssignGoogleServiceAccountKey-test.ts b/packages/eas-cli/src/credentials/android/actions/__tests__/AssignGoogleServiceAccountKeyForSubmissions-test.ts similarity index 85% rename from packages/eas-cli/src/credentials/android/actions/__tests__/AssignGoogleServiceAccountKey-test.ts rename to packages/eas-cli/src/credentials/android/actions/__tests__/AssignGoogleServiceAccountKeyForSubmissions-test.ts index 7265664a82..8375bd7907 100644 --- a/packages/eas-cli/src/credentials/android/actions/__tests__/AssignGoogleServiceAccountKey-test.ts +++ b/packages/eas-cli/src/credentials/android/actions/__tests__/AssignGoogleServiceAccountKeyForSubmissions-test.ts @@ -2,12 +2,12 @@ import { AppQuery } from '../../../../graphql/queries/AppQuery'; import { testGoogleServiceAccountKeyFragment } from '../../../__tests__/fixtures-android'; import { testAppQueryByIdResponse } from '../../../__tests__/fixtures-constants'; import { createCtxMock } from '../../../__tests__/fixtures-context'; -import { AssignGoogleServiceAccountKey } from '../AssignGoogleServiceAccountKey'; +import { AssignGoogleServiceAccountKeyForSubmissions } from '../AssignGoogleServiceAccountKeyForSubmissions'; import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils'; jest.mock('../../../../graphql/queries/AppQuery'); -describe(AssignGoogleServiceAccountKey, () => { +describe(AssignGoogleServiceAccountKeyForSubmissions, () => { beforeEach(() => { jest.mocked(AppQuery.byIdAsync).mockResolvedValue(testAppQueryByIdResponse); }); @@ -16,7 +16,9 @@ describe(AssignGoogleServiceAccountKey, () => { nonInteractive: false, }); const appLookupParams = await getAppLookupParamsFromContextAsync(ctx); - const assignGoogleServiceAccountKeyAction = new AssignGoogleServiceAccountKey(appLookupParams); + const assignGoogleServiceAccountKeyAction = new AssignGoogleServiceAccountKeyForSubmissions( + appLookupParams + ); await assignGoogleServiceAccountKeyAction.runAsync(ctx, testGoogleServiceAccountKeyFragment); // expect app credentials to be fetched/created, then updated @@ -30,7 +32,9 @@ describe(AssignGoogleServiceAccountKey, () => { nonInteractive: true, }); const appLookupParams = await getAppLookupParamsFromContextAsync(ctx); - const assignGoogleServiceAccountKeyAction = new AssignGoogleServiceAccountKey(appLookupParams); + const assignGoogleServiceAccountKeyAction = new AssignGoogleServiceAccountKeyForSubmissions( + appLookupParams + ); // dont fail if users are running in non-interactive mode await expect( 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/__tests__/__snapshots__/printCredentials-test.ts.snap b/packages/eas-cli/src/credentials/android/utils/__tests__/__snapshots__/printCredentials-test.ts.snap index da05fc1646..40b540527e 100644 --- a/packages/eas-cli/src/credentials/android/utils/__tests__/__snapshots__/printCredentials-test.ts.snap +++ b/packages/eas-cli/src/credentials/android/utils/__tests__/__snapshots__/printCredentials-test.ts.snap @@ -3,8 +3,8 @@ exports[`print credentials prints the AndroidAppCredentials fragment 1`] = ` "Android Credentials Project testApp -Application Identifier test.com.appPush Notifications (FCM) Key abcd...efgh -Updated 0 second agoGoogle Service Account Key For Submissions Project ID sdf.sdf.sdf +Application Identifier test.com.appPush Notifications (FCM Legacy) Key abcd...efgh +Updated 0 second agoPush Notifications (FCM V1): Google Service Account Key For FCM V1 None assigned yetSubmissions: Google Service Account Key for Play Store Submissions Project ID sdf.sdf.sdf Client Email quin@expo.io Client ID test-client-identifier Private Key ID test-private-key-identifier diff --git a/packages/eas-cli/src/credentials/android/utils/__tests__/printCredentials-test.ts b/packages/eas-cli/src/credentials/android/utils/__tests__/printCredentials-test.ts index bd9fb3fb1b..86b31beba8 100644 --- a/packages/eas-cli/src/credentials/android/utils/__tests__/printCredentials-test.ts +++ b/packages/eas-cli/src/credentials/android/utils/__tests__/printCredentials-test.ts @@ -13,7 +13,11 @@ import { getAppLookupParamsFromContextAsync } from '../../actions/BuildCredentia import { displayAndroidAppCredentials } from '../printCredentials'; jest.mock('../../../../log'); -jest.mock('chalk', () => ({ bold: jest.fn(log => log), cyan: { bold: jest.fn(log => log) } })); +jest.mock('chalk', () => ({ + bold: jest.fn(log => log), + cyan: { bold: jest.fn(log => log) }, + dim: jest.fn(log => log), +})); jest.mock('../../../../graphql/queries/AppQuery'); mockdate.set(testLegacyAndroidFcmFragment.updatedAt); 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..50c44ceb6f 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,20 +92,56 @@ 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', 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..a0c997ac87 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'; @@ -19,7 +21,8 @@ import Log, { learnMore } from '../../log'; import { GradleBuildContext, resolveGradleBuildContextAsync } from '../../project/android/gradle'; import { promptAsync } from '../../prompts'; import { AssignFcm } from '../android/actions/AssignFcm'; -import { AssignGoogleServiceAccountKey } from '../android/actions/AssignGoogleServiceAccountKey'; +import { AssignGoogleServiceAccountKeyForFcmV1 } from '../android/actions/AssignGoogleServiceAccountKeyForFcmV1'; +import { AssignGoogleServiceAccountKeyForSubmissions } from '../android/actions/AssignGoogleServiceAccountKeyForSubmissions'; 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,31 @@ 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); + await new AssignGoogleServiceAccountKeyForSubmissions(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 764034d281..17d4d4c2a8 100644 --- a/packages/eas-cli/src/graphql/generated.ts +++ b/packages/eas-cli/src/graphql/generated.ts @@ -6143,7 +6143,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']; @@ -6151,7 +6151,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']; @@ -6159,7 +6159,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; @@ -6213,7 +6221,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']; @@ -6902,7 +6910,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..4095d049d4 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'; @@ -27,7 +27,7 @@ import { jest.mock('fs'); jest.mock('../../../prompts'); jest.mock('../../../project/projectUtils'); -jest.mock('../../../credentials/android/actions/SetUpGoogleServiceAccountKey'); +jest.mock('../../../credentials/android/actions/SetUpGoogleServiceAccountKeyForSubmissions'); jest.mock('../../../user/User', () => ({ getUserAsync: jest.fn(() => mockJester), })); @@ -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(() => {