xref: /expo/packages/@expo/cli/src/api/user/user.ts (revision e712b0bc)
1import gql from 'graphql-tag';
2
3import * as Log from '../../log';
4import * as Analytics from '../../utils/analytics/rudderstackClient';
5import { graphqlClient } from '../graphql/client';
6import { CurrentUserQuery } from '../graphql/generated';
7import { UserQuery } from '../graphql/queries/UserQuery';
8import { fetchAsync } from '../rest/client';
9import UserSettings from './UserSettings';
10
11export type Actor = NonNullable<CurrentUserQuery['meActor']>;
12
13let currentUser: Actor | undefined;
14
15export const ANONYMOUS_USERNAME = 'anonymous';
16
17/**
18 * Resolve the name of the actor, either normal user or robot user.
19 * This should be used whenever the "current user" needs to be displayed.
20 * The display name CANNOT be used as project owner.
21 */
22export function getActorDisplayName(user?: Actor): string {
23  switch (user?.__typename) {
24    case 'User':
25      return user.username;
26    case 'Robot':
27      return user.firstName ? `${user.firstName} (robot)` : 'robot';
28    default:
29      return ANONYMOUS_USERNAME;
30  }
31}
32
33export async function getUserAsync(): Promise<Actor | undefined> {
34  if (!currentUser && (UserSettings.getAccessToken() || UserSettings.getSession()?.sessionSecret)) {
35    const user = await UserQuery.currentUserAsync();
36    currentUser = user ?? undefined;
37    if (user) {
38      await Analytics.setUserDataAsync(user.id, {
39        username: getActorDisplayName(user),
40        user_id: user.id,
41        user_type: user.__typename,
42      });
43    }
44  }
45  return currentUser;
46}
47
48export async function loginAsync(json: {
49  username: string;
50  password: string;
51  otp?: string;
52}): Promise<void> {
53  const res = await fetchAsync('auth/loginAsync', {
54    method: 'POST',
55    body: JSON.stringify(json),
56  });
57  const {
58    data: { sessionSecret },
59  } = await res.json();
60  const result = await graphqlClient
61    .query(
62      gql`
63        query UserQuery {
64          viewer {
65            id
66            username
67          }
68        }
69      `,
70      {},
71      {
72        fetchOptions: {
73          headers: {
74            'expo-session': sessionSecret,
75          },
76        },
77        additionalTypenames: [] /* UserQuery has immutable fields */,
78      }
79    )
80    .toPromise();
81  const {
82    data: { viewer },
83  } = result;
84  await UserSettings.setSessionAsync({
85    sessionSecret,
86    userId: viewer.id,
87    username: viewer.username,
88    currentConnection: 'Username-Password-Authentication',
89  });
90}
91
92export async function logoutAsync(): Promise<void> {
93  currentUser = undefined;
94  await UserSettings.setSessionAsync(undefined);
95  Log.log('Logged out');
96}
97