Skip to content

Commit

Permalink
Add cloud endpoints for frontend (#6002)
Browse files Browse the repository at this point in the history
Adds functions and types to access backend endpoints.
This is in preparation for upcoming PRs that will flesh out the dashboard UI.

# Important Notes
Has not been tested since it is not currently used. It will be used (and tested) in future PRs.
  • Loading branch information
somebody1234 authored Apr 6, 2023
1 parent 83b10a2 commit 6a09f12
Show file tree
Hide file tree
Showing 10 changed files with 938 additions and 185 deletions.
8 changes: 6 additions & 2 deletions app/ide-desktop/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const RESTRICTED_SYNTAXES = [
},
{
selector:
':not(:matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression, SwitchStatement, SwitchCase, IfStatement:has(.consequent > :matches(ReturnStatement, ThrowStatement)):has(.alternate :matches(ReturnStatement, ThrowStatement)), TryStatement:has(.block > :matches(ReturnStatement, ThrowStatement)):has(:matches([handler=null], .handler :matches(ReturnStatement, ThrowStatement))):has(:matches([finalizer=null], .finalizer :matches(ReturnStatement, ThrowStatement))))) > * > ReturnStatement',
':not(:matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression, SwitchStatement, SwitchCase, IfStatement:has(.consequent > :matches(ReturnStatement, ThrowStatement)):has(.alternate :matches(ReturnStatement, ThrowStatement)), TryStatement:has(.block > :matches(ReturnStatement, ThrowStatement)):has(:matches([handler=null], .handler :matches(ReturnStatement, ThrowStatement))):has(:matches([finalizer=null], .finalizer :matches(ReturnStatement, ThrowStatement))))) > * > :matches(ReturnStatement, ThrowStatement)',
message: 'No early returns',
},
{
Expand Down Expand Up @@ -166,6 +166,10 @@ const RESTRICTED_SYNTAXES = [
'ImportDeclaration[source.value=/^(?:assert|async_hooks|buffer|child_process|cluster|console|constants|crypto|dgram|diagnostics_channel|dns|domain|events|fs|fs\\u002Fpromises|http|http2|https|inspector|module|net|os|path|perf_hooks|process|punycode|querystring|readline|repl|stream|string_decoder|timers|tls|trace_events|tty|url|util|v8|vm|wasi|worker_threads|zlib)$/]',
message: 'Use `node:` prefix to import builtin node modules',
},
{
selector: 'TSEnumDeclaration:not(:has(TSEnumMember))',
message: 'Enums must not be empty',
},
{
selector:
'ImportDeclaration[source.value=/^(?!node:)/] ~ ImportDeclaration[source.value=/^node:/]',
Expand Down Expand Up @@ -310,7 +314,7 @@ export default [
],
'no-redeclare': 'off',
// Important to warn on accidental duplicated `interface`s e.g. when writing API wrappers.
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/no-redeclare': ['error', { ignoreDeclarationMerge: false }],
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'warn',
'no-unused-expressions': 'off',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@
* pools, Amplify must be configured prior to use. This file defines all the information needed to
* connect to and use these pools. */

import * as utils from '../utils'
import * as newtype from '../newtype'

// =================
// === Constants ===
// =================

/** AWS region in which our Cognito pool is located. */
export const AWS_REGION = utils.brand<AwsRegion>('eu-west-1')
export const AWS_REGION = newtype.asNewtype<AwsRegion>('eu-west-1')
/** Complete list of OAuth scopes used by the app. */
export const OAUTH_SCOPES = [utils.brand<OAuthScope>('email'), utils.brand<OAuthScope>('openid')]
export const OAUTH_SCOPES = [
newtype.asNewtype<OAuthScope>('email'),
newtype.asNewtype<OAuthScope>('openid'),
]
/** OAuth response type used in the OAuth flows. */
export const OAUTH_RESPONSE_TYPE = utils.brand<OAuthResponseType>('code')
export const OAUTH_RESPONSE_TYPE = newtype.asNewtype<OAuthResponseType>('code')

// =============
// === Types ===
Expand All @@ -34,34 +37,34 @@ export const OAUTH_RESPONSE_TYPE = utils.brand<OAuthResponseType>('code')

/** The AWS region in which our Cognito pool is located. This is always set to `eu-west-1` because
* that is the only region in which our Cognito pools are currently available in. */
type AwsRegion = utils.Brand<'AwsRegion'> & string
type AwsRegion = newtype.Newtype<string, 'AwsRegion'>
/** ID of the "Cognito user pool" that contains authentication & identity data of our users.
*
* This is created automatically by our Terraform scripts when the backend infrastructure is
* created. Look in the `enso-org/cloud-v2` repo for details. */
export type UserPoolId = utils.Brand<'UserPoolId'> & string
export type UserPoolId = newtype.Newtype<string, 'UserPoolId'>
/** ID of an OAuth client authorized to interact with the Cognito user pool specified by the
* {@link UserPoolId}.
*
* This is created automatically by our Terraform scripts when the backend infrastructure is
* created. Look in the `enso-org/cloud-v2` repo for details. */
export type UserPoolWebClientId = utils.Brand<'UserPoolWebClientId'> & string
export type UserPoolWebClientId = newtype.Newtype<string, 'UserPoolWebClientId'>
/** Domain of the Cognito user pool used for authenticating/identifying the user.
*
* This must correspond to the public-facing domain name of the Cognito pool identified by the
* {@link UserPoolId}, and must not contain an HTTP scheme, or a pathname. */
export type OAuthDomain = utils.Brand<'OAuthDomain'> & string
export type OAuthDomain = newtype.Newtype<string, 'OAuthDomain'>
/** Possible OAuth scopes to request from the federated identity provider during OAuth sign-in. */
type OAuthScope = utils.Brand<'OAuthScope'> & string
type OAuthScope = newtype.Newtype<string, 'OAuthScope'>
/** The response type used to complete the OAuth flow. "code" means that the federated identity
* provider will return an authorization code that can be exchanged for an access token. The
* authorization code will be provided as a query parameter of the redirect URL. */
type OAuthResponseType = utils.Brand<'OAuthResponseType'> & string
type OAuthResponseType = newtype.Newtype<string, 'OAuthResponseType'>
/** The URL used as a redirect (minus query parameters like `code` which get appended later), once
* an OAuth flow (e.g., sign-in or sign-out) has completed. These must match the values set in the
* Cognito pool and during the creation of the OAuth client. See the `enso-org/cloud-v2` repo for
* details. */
export type OAuthRedirect = utils.Brand<'OAuthRedirect'> & string
export type OAuthRedirect = newtype.Newtype<string, 'OAuthRedirect'>
/** Callback used to open URLs for the OAuth flow. This is only used in the desktop app (i.e., not in
* the cloud). This is because in the cloud we just keep the user in their browser, but in the app
* we want to open OAuth URLs in the system browser. This is because the user can't be expected to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as authServiceModule from '../service'
import * as backendService from '../../dashboard/service'
import * as errorModule from '../../error'
import * as loggerProvider from '../../providers/logger'
import * as newtype from '../../newtype'
import * as sessionProvider from './session'

// =================
Expand Down Expand Up @@ -47,7 +48,7 @@ export interface FullUserSession {
/** User's email address. */
email: string
/** User's organization information. */
organization: backendService.Organization
organization: backendService.UserOrOrganization
}

/** Object containing the currently signed-in user's session data, if the user has not yet set their
Expand Down Expand Up @@ -155,7 +156,7 @@ export function AuthProvider(props: AuthProviderProps) {
const { accessToken, email } = session.val

const backend = backendService.createBackend(accessToken, logger)
const organization = await backend.getUser()
const organization = await backend.usersMe()
let newUserSession: UserSession
if (!organization) {
newUserSession = {
Expand Down Expand Up @@ -239,17 +240,15 @@ export function AuthProvider(props: AuthProviderProps) {
})

const setUsername = async (accessToken: string, username: string, email: string) => {
const body: backendService.SetUsernameRequestBody = {
userName: username,
userEmail: email,
}

/** TODO [NP]: https://github.com/enso-org/cloud-v2/issues/343
* The API client is reinitialised on every request. That is an inefficient way of usage.
* Fix it by using React context and implementing it as a singleton. */
const backend = backendService.createBackend(accessToken, logger)

await backend.setUsername(body)
await backend.createUser({
userName: username,
userEmail: newtype.asNewtype<backendService.EmailAddress>(email),
})
navigate(app.DASHBOARD_PATH)
toast.success(MESSAGES.setUsernameSuccess)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import * as cognito from './cognito'
import * as config from '../config'
import * as listen from './listen'
import * as loggerProvider from '../providers/logger'
import * as newtype from '../newtype'
import * as platformModule from '../platform'
import * as utils from '../utils'

// =================
// === Constants ===
Expand All @@ -32,7 +32,7 @@ const CONFIRM_REGISTRATION_PATHNAME = '//auth/confirmation'
const LOGIN_PATHNAME = '//auth/login'

/** URL used as the OAuth redirect when running in the desktop app. */
const DESKTOP_REDIRECT = utils.brand<auth.OAuthRedirect>(`${common.DEEP_LINK_SCHEME}://auth`)
const DESKTOP_REDIRECT = newtype.asNewtype<auth.OAuthRedirect>(`${common.DEEP_LINK_SCHEME}://auth`)
/** Map from platform to the OAuth redirect URL that should be used for that platform. */
const PLATFORM_TO_CONFIG: Record<
platformModule.Platform,
Expand All @@ -58,16 +58,22 @@ const BASE_AMPLIFY_CONFIG = {
const AMPLIFY_CONFIGS = {
/** Configuration for @pbuchu's Cognito user pool. */
pbuchu: {
userPoolId: utils.brand<auth.UserPoolId>('eu-west-1_jSF1RbgPK'),
userPoolWebClientId: utils.brand<auth.UserPoolWebClientId>('1bnib0jfon3aqc5g3lkia2infr'),
domain: utils.brand<auth.OAuthDomain>('pb-enso-domain.auth.eu-west-1.amazoncognito.com'),
userPoolId: newtype.asNewtype<auth.UserPoolId>('eu-west-1_jSF1RbgPK'),
userPoolWebClientId: newtype.asNewtype<auth.UserPoolWebClientId>(
'1bnib0jfon3aqc5g3lkia2infr'
),
domain: newtype.asNewtype<auth.OAuthDomain>(
'pb-enso-domain.auth.eu-west-1.amazoncognito.com'
),
...BASE_AMPLIFY_CONFIG,
} satisfies Partial<auth.AmplifyConfig>,
/** Configuration for the production Cognito user pool. */
production: {
userPoolId: utils.brand<auth.UserPoolId>('eu-west-1_9Kycu2SbD'),
userPoolWebClientId: utils.brand<auth.UserPoolWebClientId>('4j9bfs8e7415erf82l129v0qhe'),
domain: utils.brand<auth.OAuthDomain>(
userPoolId: newtype.asNewtype<auth.UserPoolId>('eu-west-1_9Kycu2SbD'),
userPoolWebClientId: newtype.asNewtype<auth.UserPoolWebClientId>(
'4j9bfs8e7415erf82l129v0qhe'
),
domain: newtype.asNewtype<auth.OAuthDomain>(
'production-enso-domain.auth.eu-west-1.amazoncognito.com'
),
...BASE_AMPLIFY_CONFIG,
Expand Down
12 changes: 6 additions & 6 deletions app/ide-desktop/lib/dashboard/src/authentication/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** @file Configuration definition for the Dashboard. */

import * as auth from './authentication/config'
import * as utils from './utils'
import * as newtype from './newtype'

// =================
// === Constants ===
Expand All @@ -16,14 +16,14 @@ const CLOUD_REDIRECTS = {
* The redirect URL must be known ahead of time because it is registered with the OAuth provider
* when it is created. In the native app, the port is unpredictable, but this is not a problem
* because the native app does not use port-based redirects, but deep links. */
development: utils.brand<auth.OAuthRedirect>('http://localhost:8081'),
production: utils.brand<auth.OAuthRedirect>('https://cloud.enso.org'),
development: newtype.asNewtype<auth.OAuthRedirect>('http://localhost:8081'),
production: newtype.asNewtype<auth.OAuthRedirect>('https://cloud.enso.org'),
}

/** All possible API URLs, sorted by environment. */
const API_URLS = {
pbuchu: utils.brand<ApiUrl>('https://xw0g8j3tsb.execute-api.eu-west-1.amazonaws.com'),
production: utils.brand<ApiUrl>('https://7aqkn3tnbc.execute-api.eu-west-1.amazonaws.com'),
pbuchu: newtype.asNewtype<ApiUrl>('https://xw0g8j3tsb.execute-api.eu-west-1.amazonaws.com'),
production: newtype.asNewtype<ApiUrl>('https://7aqkn3tnbc.execute-api.eu-west-1.amazonaws.com'),
}

/** All possible configuration options, sorted by environment. */
Expand Down Expand Up @@ -65,4 +65,4 @@ export type Environment = 'pbuchu' | 'production'
// ===========

/** Base URL for requests to our Cloud API backend. */
type ApiUrl = utils.Brand<'ApiUrl'> & string
type ApiUrl = newtype.Newtype<string, 'ApiUrl'>
Loading

0 comments on commit 6a09f12

Please sign in to comment.