From a2126620034da95f8f1b47e009200f5f331c74d4 Mon Sep 17 00:00:00 2001 From: Will Schurman Date: Wed, 23 Oct 2024 08:52:41 -0700 Subject: [PATCH] [eas-cli] Create dynamic logged in context field and clean up erroneous SessionManager context field uses (#2648) --- CHANGELOG.md | 2 ++ .../eas-cli/src/commandUtils/EasCommand.ts | 9 +++++ .../context/DynamicLoggedInContextField.ts | 36 +++++++++++++++++++ .../context/LoggedInContextField.ts | 18 ++++++---- .../context/MaybeLoggedInContextField.ts | 17 +++++---- packages/eas-cli/src/commands/account/view.ts | 6 ++-- packages/eas-cli/src/commands/config.ts | 10 ++---- .../commands/project/__tests__/init.test.ts | 1 + .../commands/update/__tests__/index.test.ts | 1 + .../update/__tests__/republish.test.ts | 1 + .../__tests__/roll-back-to-embedded.test.ts | 1 + packages/eas-cli/src/user/SessionManager.ts | 2 +- 12 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 packages/eas-cli/src/commandUtils/context/DynamicLoggedInContextField.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 9927ee0110..5f3a8cdeef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ This is the log of notable changes to EAS CLI and related packages. ### ๐Ÿงน Chores +- Create dynamic logged in context field and clean up erroneous SessionManager context field uses. ([#2648](https://github.com/expo/eas-cli/pull/2648) by [@wschurman](https://github.com/wschurman)) + ## [12.6.0](https://github.com/expo/eas-cli/releases/tag/v12.6.0) - 2024-10-21 ### ๐ŸŽ‰ New features diff --git a/packages/eas-cli/src/commandUtils/EasCommand.ts b/packages/eas-cli/src/commandUtils/EasCommand.ts index 9123f0c6de..8a8819ae80 100644 --- a/packages/eas-cli/src/commandUtils/EasCommand.ts +++ b/packages/eas-cli/src/commandUtils/EasCommand.ts @@ -6,6 +6,7 @@ import nullthrows from 'nullthrows'; import AnalyticsContextField from './context/AnalyticsContextField'; import ContextField from './context/ContextField'; +import DynamicLoggedInContextField from './context/DynamicLoggedInContextField'; import { DynamicPrivateProjectConfigContextField, DynamicPublicProjectConfigContextField, @@ -61,8 +62,16 @@ export default abstract class EasCommand extends Command { MaybeLoggedIn: { maybeLoggedIn: new MaybeLoggedInContextField(), }, + /** + * Specify this context if the logged-in requirement is only necessary in a particular execution of the command. + */ + DynamicLoggedIn: { + // eslint-disable-next-line async-protect/async-suffix + getDynamicLoggedInAsync: new DynamicLoggedInContextField(), + }, /** * Specify this context requirement if the command needs to mutate the user session. + * @deprecated Should not be used outside of session management commands, which currently only includes `login` and `logout`. */ SessionManagment: { sessionManager: new SessionManagementContextField(), diff --git a/packages/eas-cli/src/commandUtils/context/DynamicLoggedInContextField.ts b/packages/eas-cli/src/commandUtils/context/DynamicLoggedInContextField.ts new file mode 100644 index 0000000000..13dd4be9a1 --- /dev/null +++ b/packages/eas-cli/src/commandUtils/context/DynamicLoggedInContextField.ts @@ -0,0 +1,36 @@ +import ContextField, { ContextOptions } from './ContextField'; +import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient'; +import { LoggedInAuthenticationInfo } from '../../user/SessionManager'; +import { Actor } from '../../user/User'; +import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides'; +import FeatureGating from '../gating/FeatureGating'; + +export type DynamicLoggedInContextFn = () => Promise<{ + actor: Actor; + featureGating: FeatureGating; + graphqlClient: ExpoGraphqlClient; + authenticationInfo: LoggedInAuthenticationInfo; +}>; + +export default class DynamicLoggedInContextField extends ContextField { + async getValueAsync({ + nonInteractive, + sessionManager, + }: ContextOptions): Promise { + return async () => { + const { actor, authenticationInfo } = await sessionManager.ensureLoggedInAsync({ + nonInteractive, + }); + const featureGateServerValues: { [key: string]: boolean } = actor?.featureGates ?? {}; + + const graphqlClient = createGraphqlClient(authenticationInfo); + + return { + actor, + featureGating: new FeatureGating(featureGateServerValues, new FeatureGateEnvOverrides()), + graphqlClient, + authenticationInfo, + }; + }; + } +} diff --git a/packages/eas-cli/src/commandUtils/context/LoggedInContextField.ts b/packages/eas-cli/src/commandUtils/context/LoggedInContextField.ts index b6e8c76594..bc4a118634 100644 --- a/packages/eas-cli/src/commandUtils/context/LoggedInContextField.ts +++ b/packages/eas-cli/src/commandUtils/context/LoggedInContextField.ts @@ -1,19 +1,22 @@ import ContextField, { ContextOptions } from './ContextField'; import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient'; +import { LoggedInAuthenticationInfo } from '../../user/SessionManager'; import { Actor } from '../../user/User'; import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides'; import FeatureGating from '../gating/FeatureGating'; -export default class LoggedInContextField extends ContextField<{ +type LoggedInContextType = { actor: Actor; featureGating: FeatureGating; graphqlClient: ExpoGraphqlClient; -}> { - async getValueAsync({ nonInteractive, sessionManager }: ContextOptions): Promise<{ - actor: Actor; - featureGating: FeatureGating; - graphqlClient: ExpoGraphqlClient; - }> { + authenticationInfo: LoggedInAuthenticationInfo; +}; + +export default class LoggedInContextField extends ContextField { + async getValueAsync({ + nonInteractive, + sessionManager, + }: ContextOptions): Promise { const { actor, authenticationInfo } = await sessionManager.ensureLoggedInAsync({ nonInteractive, }); @@ -25,6 +28,7 @@ export default class LoggedInContextField extends ContextField<{ actor, featureGating: new FeatureGating(featureGateServerValues, new FeatureGateEnvOverrides()), graphqlClient, + authenticationInfo, }; } } diff --git a/packages/eas-cli/src/commandUtils/context/MaybeLoggedInContextField.ts b/packages/eas-cli/src/commandUtils/context/MaybeLoggedInContextField.ts index 5a0e9e59f3..ca70abe068 100644 --- a/packages/eas-cli/src/commandUtils/context/MaybeLoggedInContextField.ts +++ b/packages/eas-cli/src/commandUtils/context/MaybeLoggedInContextField.ts @@ -4,16 +4,18 @@ import { Actor } from '../../user/User'; import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides'; import FeatureGating from '../gating/FeatureGating'; -export default class MaybeLoggedInContextField extends ContextField<{ +type MaybeLoggedInContextType = { actor: Actor | null; featureGating: FeatureGating; graphqlClient: ExpoGraphqlClient; -}> { - async getValueAsync({ sessionManager }: ContextOptions): Promise<{ - actor: Actor | null; - featureGating: FeatureGating; - graphqlClient: ExpoGraphqlClient; - }> { + authenticationInfo: { + accessToken: string | null; + sessionSecret: string | null; + }; +}; + +export default class MaybeLoggedInContextField extends ContextField { + async getValueAsync({ sessionManager }: ContextOptions): Promise { const authenticationInfo = { accessToken: sessionManager.getAccessToken(), sessionSecret: sessionManager.getSessionSecret(), @@ -27,6 +29,7 @@ export default class MaybeLoggedInContextField extends ContextField<{ actor: actor ?? null, featureGating: new FeatureGating(featureGateServerValues, new FeatureGateEnvOverrides()), graphqlClient, + authenticationInfo, }; } } diff --git a/packages/eas-cli/src/commands/account/view.ts b/packages/eas-cli/src/commands/account/view.ts index 0b8dc45027..37d699d6ab 100644 --- a/packages/eas-cli/src/commands/account/view.ts +++ b/packages/eas-cli/src/commands/account/view.ts @@ -12,16 +12,14 @@ export default class AccountView extends EasCommand { static override contextDefinition = { ...this.ContextOptions.MaybeLoggedIn, - ...this.ContextOptions.SessionManagment, }; async runAsync(): Promise { const { - maybeLoggedIn: { actor }, - sessionManager, + maybeLoggedIn: { actor, authenticationInfo }, } = await this.getContextAsync(AccountView, { nonInteractive: true }); if (actor) { - const loggedInAs = sessionManager.getAccessToken() + const loggedInAs = authenticationInfo.accessToken ? `${getActorDisplayName(actor)} (authenticated using EXPO_TOKEN)` : getActorDisplayName(actor); Log.log(chalk.green(loggedInAs)); diff --git a/packages/eas-cli/src/commands/config.ts b/packages/eas-cli/src/commands/config.ts index c1adad6eaf..89d1b7ede2 100644 --- a/packages/eas-cli/src/commands/config.ts +++ b/packages/eas-cli/src/commands/config.ts @@ -6,7 +6,6 @@ import chalk from 'chalk'; import { evaluateConfigWithEnvVarsAsync } from '../build/evaluateConfigWithEnvVarsAsync'; import EasCommand from '../commandUtils/EasCommand'; -import { createGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient'; import { EasNonInteractiveAndJsonFlags } from '../commandUtils/flags'; import { toAppPlatform } from '../graphql/types/AppPlatform'; import Log from '../log'; @@ -35,7 +34,7 @@ export default class Config extends EasCommand { static override contextDefinition = { ...this.ContextOptions.DynamicProjectConfig, ...this.ContextOptions.ProjectDir, - ...this.ContextOptions.SessionManagment, + ...this.ContextOptions.DynamicLoggedIn, }; async runAsync(): Promise { @@ -48,7 +47,7 @@ export default class Config extends EasCommand { profile: maybeProfile, 'non-interactive': nonInteractive, } = flags; - const { getDynamicPublicProjectConfigAsync, projectDir, sessionManager } = + const { getDynamicPublicProjectConfigAsync, projectDir, getDynamicLoggedInAsync } = await this.getContextAsync(Config, { nonInteractive, }); @@ -88,10 +87,7 @@ export default class Config extends EasCommand { Log.log(JSON.stringify(profile, null, 2)); } } else { - const { authenticationInfo } = await sessionManager.ensureLoggedInAsync({ - nonInteractive, - }); - const graphqlClient = createGraphqlClient(authenticationInfo); + const { graphqlClient } = await getDynamicLoggedInAsync(); const { exp: appConfig } = await evaluateConfigWithEnvVarsAsync({ buildProfile: profile, buildProfileName: profileName, diff --git a/packages/eas-cli/src/commands/project/__tests__/init.test.ts b/packages/eas-cli/src/commands/project/__tests__/init.test.ts index 7ea1c0f237..7e9fd7c1f6 100644 --- a/packages/eas-cli/src/commands/project/__tests__/init.test.ts +++ b/packages/eas-cli/src/commands/project/__tests__/init.test.ts @@ -90,6 +90,7 @@ function mockTestProject(options: { actor: jester, featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()), graphqlClient, + authenticationInfo: { accessToken: null, sessionSecret: '1234' }, }); } diff --git a/packages/eas-cli/src/commands/update/__tests__/index.test.ts b/packages/eas-cli/src/commands/update/__tests__/index.test.ts index b942b30860..30bf1d253f 100644 --- a/packages/eas-cli/src/commands/update/__tests__/index.test.ts +++ b/packages/eas-cli/src/commands/update/__tests__/index.test.ts @@ -254,6 +254,7 @@ function mockTestProject({ actor: jester, featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()), graphqlClient, + authenticationInfo: { accessToken: null, sessionSecret: '1234' }, }); jest .spyOn(VcsClientContextField.prototype, 'getValueAsync') diff --git a/packages/eas-cli/src/commands/update/__tests__/republish.test.ts b/packages/eas-cli/src/commands/update/__tests__/republish.test.ts index 0569dffbf9..a04a5deae2 100644 --- a/packages/eas-cli/src/commands/update/__tests__/republish.test.ts +++ b/packages/eas-cli/src/commands/update/__tests__/republish.test.ts @@ -467,6 +467,7 @@ function mockTestProject({ actor: jester, featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()), graphqlClient, + authenticationInfo: { accessToken: null, sessionSecret: '1234' }, }); jest.mocked(AppQuery.byIdAsync).mockResolvedValue({ diff --git a/packages/eas-cli/src/commands/update/__tests__/roll-back-to-embedded.test.ts b/packages/eas-cli/src/commands/update/__tests__/roll-back-to-embedded.test.ts index 0ffd93be63..b7e6a1f932 100644 --- a/packages/eas-cli/src/commands/update/__tests__/roll-back-to-embedded.test.ts +++ b/packages/eas-cli/src/commands/update/__tests__/roll-back-to-embedded.test.ts @@ -255,6 +255,7 @@ function mockTestProject({ actor: jester, featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()), graphqlClient, + authenticationInfo: { accessToken: null, sessionSecret: '1234' }, }); jest .spyOn(VcsClientContextField.prototype, 'getValueAsync') diff --git a/packages/eas-cli/src/user/SessionManager.ts b/packages/eas-cli/src/user/SessionManager.ts index 83d2a1bf9a..9a59126b8a 100644 --- a/packages/eas-cli/src/user/SessionManager.ts +++ b/packages/eas-cli/src/user/SessionManager.ts @@ -41,7 +41,7 @@ type SecondFactorDevice = { is_primary: boolean; }; -type LoggedInAuthenticationInfo = +export type LoggedInAuthenticationInfo = | { accessToken: string; sessionSecret: null;