Skip to content

Commit

Permalink
chore(auth, ts): slightly safer typing for graphql
Browse files Browse the repository at this point in the history
  • Loading branch information
Krisztiaan committed Oct 19, 2020
1 parent 98b3225 commit 905877a
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 38 deletions.
19 changes: 10 additions & 9 deletions packages/api/src/functions/authDecoder.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import mockedAPIGatewayProxyEvent from './fixtures/apiGatewayProxyEvent.fixture'
import * as auth0Decoder from './../auth/decoders/auth0'
import * as netlifyDecoder from './../auth/decoders/netlify'
Expand Down Expand Up @@ -25,7 +26,7 @@ describe('Uses correct Auth decoder', () => {
it('handles auth0', async () => {
const output = await decodeToken('auth0', MOCKED_JWT, {
event: mockedAPIGatewayProxyEvent,
context: {},
context: {} as any,
})

expect(auth0Decoder.auth0).toHaveBeenCalledWith(
Expand All @@ -41,7 +42,7 @@ describe('Uses correct Auth decoder', () => {
it('decodes goTrue with netlify decoder', async () => {
const output = await decodeToken('goTrue', MOCKED_JWT, {
event: mockedAPIGatewayProxyEvent,
context: {},
context: {} as any,
})

expect(netlifyDecoder.netlify).toHaveBeenCalledWith(
Expand All @@ -57,7 +58,7 @@ describe('Uses correct Auth decoder', () => {
it('decodes netlify with netlify decoder', async () => {
const output = await decodeToken('netlify', MOCKED_JWT, {
event: mockedAPIGatewayProxyEvent,
context: {},
context: {} as any,
})

expect(netlifyDecoder.netlify).toHaveBeenCalledWith(
Expand All @@ -73,7 +74,7 @@ describe('Uses correct Auth decoder', () => {
it('returns undecoded token for custom', async () => {
const output = await decodeToken('custom', MOCKED_JWT, {
event: mockedAPIGatewayProxyEvent,
context: {},
context: {} as any,
})

expect(output).toEqual(MOCKED_JWT)
Expand All @@ -82,7 +83,7 @@ describe('Uses correct Auth decoder', () => {
it('returns undecoded token for magicLink', async () => {
const output = await decodeToken('magicLink', MOCKED_JWT, {
event: mockedAPIGatewayProxyEvent,
context: {},
context: {} as any,
})

expect(output).toEqual(MOCKED_JWT)
Expand All @@ -91,7 +92,7 @@ describe('Uses correct Auth decoder', () => {
it('returns undecoded token for firebase', async () => {
const output = await decodeToken('firebase', MOCKED_JWT, {
event: mockedAPIGatewayProxyEvent,
context: {},
context: {} as any,
})

expect(output).toEqual(MOCKED_JWT)
Expand All @@ -100,16 +101,16 @@ describe('Uses correct Auth decoder', () => {
it('returns undecoded token for supabase', async () => {
const output = await decodeToken('supabase', MOCKED_JWT, {
event: mockedAPIGatewayProxyEvent,
context: {},
context: {} as any,
})

expect(output).toEqual(MOCKED_JWT)
})

it('returns undecoded token for unknown values', async () => {
const output = await decodeToken('SOMETHING_ELSE!', MOCKED_JWT, {
const output = await decodeToken('SOMETHING_ELSE!' as any, MOCKED_JWT, {
event: mockedAPIGatewayProxyEvent,
context: {},
context: {} as any,
})

expect(output).toEqual(MOCKED_JWT)
Expand Down
46 changes: 24 additions & 22 deletions packages/api/src/functions/graphql.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda'
import type { Config, CreateHandlerOptions } from 'apollo-server-lambda'
import type { Context, ContextFunction } from 'apollo-server-core'
import type { GlobalContext } from 'src/globalContext'
import type { AuthContextPayload } from 'src/auth'
import type { GlobalContext } from '../globalContext'
import type { AuthContextPayload } from '../auth'
import type { APIGatewayProxyCallback } from 'aws-lambda'
import { ApolloServer } from 'apollo-server-lambda'
import { getAuthenticationContext } from 'src/auth'
import { setContext } from 'src/globalContext'
import { getAuthenticationContext } from '../auth'
import { setContext } from '../globalContext'

export type GetCurrentUser = (
decoded: AuthContextPayload[0],
raw: AuthContextPayload[1]
) => Promise<null | Record<string, unknown> | string>

type ContextProps = {
event: APIGatewayProxyEvent
context: GlobalContext & LambdaContext
}

/**
* We use Apollo Server's `context` option as an entry point to construct our
* own global context.
Expand All @@ -22,17 +28,11 @@ export type GetCurrentUser = (
* dataloader instances, and anything else that should be taken into account when
* resolving the query.
*/
export const createContextHandler = (
userContext?: Context | ContextFunction,
export function createContextHandler<T>(
userContext?: Context<T> | ContextFunction<ContextProps, T>,
getCurrentUser?: GetCurrentUser
) => {
return async ({
event,
context,
}: {
event: APIGatewayProxyEvent
context: GlobalContext & LambdaContext
}) => {
) {
return async ({ event, context }: ContextProps) => {
// Prevent the Serverless function from waiting for all resources (db connections)
// to be released before returning a reponse.
context.callbackWaitsForEmptyEventLoop = false
Expand All @@ -46,11 +46,13 @@ export const createContextHandler = (
: authContext
}

let customUserContext = userContext
if (typeof userContext === 'function') {
// if userContext is a function, run that and return just the result
customUserContext = await userContext({ event, context })
}
const customUserContext =
typeof userContext === 'function'
? await (userContext as ContextFunction<ContextProps, T>)({
event,
context,
})
: userContext

// Sets the **global** context object, which can be imported with:
// import { context } from '@redwoodjs/api'
Expand Down Expand Up @@ -86,14 +88,14 @@ interface GraphQLHandlerOptions extends Config {
* export const handler = createGraphQLHandler({ schema, context, getCurrentUser })
* ```
*/
export const createGraphQLHandler = ({
export function createGraphQLHandler({
context,
getCurrentUser,
onException,
cors,
onHealthCheck,
...options
}: GraphQLHandlerOptions = {}) => {
}: GraphQLHandlerOptions = {}) {
const isDevEnv = process.env.NODE_ENV !== 'production'
const handler = new ApolloServer({
// Turn off playground, introspection and debug in production.
Expand Down Expand Up @@ -124,7 +126,7 @@ export const createGraphQLHandler = ({
return (
event: APIGatewayProxyEvent,
context: LambdaContext,
callback: any
callback: APIGatewayProxyCallback
): void => {
try {
handler(event, context, callback)
Expand Down
19 changes: 12 additions & 7 deletions packages/cli/src/commands/generate/auth/__tests__/auth.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
global.__dirname = __dirname

import {
waitFor,
} from '@testing-library/react'
import { waitFor } from '@testing-library/react'

jest.mock('fs')
jest.mock('src/lib', () => ({
Expand All @@ -17,28 +15,35 @@ import chalk from 'chalk'

import * as auth from '../auth'

const EXISTING_AUTH_PROVIDER_ERROR = 'Existing auth provider found.\nUse --force to override existing provider.';
const EXISTING_AUTH_PROVIDER_ERROR =
'Existing auth provider found.\nUse --force to override existing provider.'

test(`no error thrown when auth provider not found`, async () => {
// Mock process.exit to make sure CLI quites
const cSpy = jest.spyOn(console, 'log').mockImplementation(() => {})

auth.handler({ provider: 'netlify' })
await waitFor(() => expect(console.log).toHaveBeenCalledTimes(1))
expect(console.log).not.toHaveBeenCalledWith(chalk.bold.red(EXISTING_AUTH_PROVIDER_ERROR))
expect(console.log).not.toHaveBeenCalledWith(
chalk.bold.red(EXISTING_AUTH_PROVIDER_ERROR)
)

// Restore mocks
cSpy.mockRestore()
})

test('throws an error if auth provider exists', async () => {
// Mock process.exit to make sure CLI quites
const fsSpy = jest.spyOn(fs, 'readFileSync').mockImplementation(() => `import { AuthProvider } from '@redwoodjs/auth'`)
const fsSpy = jest
.spyOn(fs, 'readFileSync')
.mockImplementation(() => `import { AuthProvider } from '@redwoodjs/auth'`)
const cSpy = jest.spyOn(console, 'log').mockImplementation(() => {})

auth.handler({ provider: 'netlify' })
await waitFor(() => expect(console.log).toHaveBeenCalledTimes(1))
expect(console.log).toHaveBeenCalledWith(chalk.bold.red(EXISTING_AUTH_PROVIDER_ERROR))
expect(console.log).toHaveBeenCalledWith(
chalk.bold.red(EXISTING_AUTH_PROVIDER_ERROR)
)

// Restore mocks
fsSpy.mockRestore()
Expand Down

0 comments on commit 905877a

Please sign in to comment.