Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions api-js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const {
}),
}

Server({
const server = await Server({
maxDepth,
complexityCost,
scalarCost,
Expand All @@ -71,7 +71,9 @@ const {
transaction,
pubsubs,
},
}).listen(PORT, (err) => {
})

await server.listen(PORT, (err) => {
if (err) throw err
console.log(`🚀 Server ready at http://localhost:${PORT}/graphql`)
console.log(`🚀 Subscriptions ready at ws://localhost:${PORT}/graphql`)
Expand Down
1,509 changes: 532 additions & 977 deletions api-js/package-lock.json

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions api-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
"lin-clean": "npx lingui extract --clean"
},
"dependencies": {
"@graphql-tools/schema": "^7.1.5",
"@lingui/core": "^3.10.4",
"apollo-server": "^2.25.2",
"apollo-server-express": "^2.25.2",
"apollo-server": "^3.0.0",
"apollo-server-core": "^3.0.0",
"apollo-server-express": "^3.0.0",
"arango-tools": "^0.5.0",
"arangojs": "^7.5.0",
"bcryptjs": "^2.4.3",
Expand All @@ -50,6 +52,7 @@
"moment": "^2.29.1",
"notifications-node-client": "^5.1.0",
"psl": "^1.8.0",
"subscriptions-transport-ws": "^0.9.19",
"url-slug": "^3.0.2",
"uuid": "^8.3.2",
"validator": "^13.6.0"
Expand Down Expand Up @@ -77,7 +80,7 @@
"jest": "^27.0.6",
"jest-fetch-mock": "^3.0.3",
"jest-matcher-utils": "^27.0.6",
"nodemon": "^2.0.10",
"nodemon": "^2.0.12",
"prettier": "^2.3.2",
"supertest": "^6.1.3"
}
Expand Down
4 changes: 2 additions & 2 deletions api-js/src/__tests__/create-context.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('given the create context function', () => {
const context = await createContext({})({
req: {},
res: {},
connection: { context: { authorization: token } },
connection: { authorization: token },
})

expect(context.userKey).toEqual('1234')
Expand All @@ -42,7 +42,7 @@ describe('given the create context function', () => {
const context = await createContext({})({
req: {},
res: {},
connection: { context: { language: 'en' } },
connection: { language: 'en' },
})

expect(context.request.language).toEqual('en')
Expand Down
32 changes: 19 additions & 13 deletions api-js/src/__tests__/on-connect.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ensure, dbNameFromFile } from 'arango-tools'

import { createContext } from '../create-context'
import { customOnConnect } from '../on-connect'
import { verifyToken, tokenize, userRequired } from '../auth'
import { createI18n } from '../create-i18n'
Expand Down Expand Up @@ -41,15 +42,16 @@ describe('given the customOnConnect function', () => {
}

const onConnect = await customOnConnect({
context: {},
createContext,
serverContext: {},
createI18n,
verifyToken,
userRequired: mockedUserRequired,
loadUserByKey: jest.fn(),
verifiedRequired: jest.fn(),
})(connectionParams, webSocket)
})(connectionParams, webSocket, { request: {} })

expect(onConnect.language).toEqual('en')
expect(onConnect.request.language).toEqual('en')
expect(consoleOutput).toEqual([
'User: 1234, connected to subscription.',
])
Expand All @@ -72,15 +74,16 @@ describe('given the customOnConnect function', () => {
}

const onConnect = await customOnConnect({
context: {},
createContext,
serverContext: {},
createI18n,
verifyToken,
userRequired: mockedUserRequired,
loadUserByKey: jest.fn(),
verifiedRequired: jest.fn(),
})(connectionParams, webSocket)
})(connectionParams, webSocket, { request: {} })

expect(onConnect.language).toEqual('fr')
expect(onConnect.request.language).toEqual('fr')
expect(consoleOutput).toEqual([
'User: 1234, connected to subscription.',
])
Expand All @@ -104,15 +107,16 @@ describe('given the customOnConnect function', () => {
}

const onConnect = await customOnConnect({
context: {},
createContext,
serverContext: {},
createI18n,
verifyToken,
userRequired: mockedUserRequired,
loadUserByKey: jest.fn(),
verifiedRequired: jest.fn(),
})(connectionParams, webSocket)
})(connectionParams, webSocket, { request: {} })

expect(onConnect.authorization).toEqual(token)
expect(onConnect.request.headers.authorization).toEqual(token)
expect(consoleOutput).toEqual(['User: 1234, connected to subscription.'])
})
})
Expand Down Expand Up @@ -152,12 +156,13 @@ describe('given the customOnConnect function', () => {

try {
await customOnConnect({
context: { query },
createContext,
serverContext: { query },
createI18n,
verifyToken,
userRequired,
loadUserByKey: jest.fn().mockReturnValue({ load: jest.fn() }),
})(connectionParams, webSocket)
})(connectionParams, webSocket, { request: {} })
} catch (err) {
expect(err).toEqual(
new Error('Authentication error. Please sign in.'),
Expand All @@ -183,12 +188,13 @@ describe('given the customOnConnect function', () => {

try {
await customOnConnect({
context: { query },
createContext,
serverContext: { query },
createI18n,
verifyToken,
userRequired,
loadUserByKey: jest.fn().mockReturnValue({ load: jest.fn() }),
})(connectionParams, webSocket)
})(connectionParams, webSocket, { request: {} })
} catch (err) {
expect(err).toEqual(
new Error("Erreur d'authentification. Veuillez vous connecter."),
Expand Down
10 changes: 5 additions & 5 deletions api-js/src/__tests__/server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ describe('parse server', () => {

describe('/alive', () => {
it('returns 200', async () => {
const response = await request(Server({ query: jest.fn() })).get('/alive')
const response = await request(await Server({ query: jest.fn() })).get('/alive')
expect(response.status).toEqual(200)
})
})

describe('/ready', () => {
it('returns 200', async () => {
const response = await request(Server({ query: jest.fn() })).get('/ready')
const response = await request(await Server({ query: jest.fn() })).get('/ready')
expect(response.status).toEqual(200)
})
})
Expand All @@ -40,7 +40,7 @@ describe('parse server', () => {
describe('endpoint is alive', () => {
it('returns 200', async () => {
const response = await request(
Server({
await Server({
maxDepth,
complexityCost,
scalarCost,
Expand All @@ -65,7 +65,7 @@ describe('parse server', () => {
describe('query cost is too high', () => {
it('returns an error message', async () => {
const response = await request(
Server({
await Server({
maxDepth,
complexityCost: 1,
scalarCost: 100,
Expand All @@ -91,7 +91,7 @@ describe('parse server', () => {
describe('query depth is too high', () => {
it('returns an error message', async () => {
const response = await request(
Server({
await Server({
maxDepth: 1,
complexityCost: 1000,
scalarCost: 1,
Expand Down
6 changes: 3 additions & 3 deletions api-js/src/create-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ export const createContext =
if (connection) {
req = {
headers: {
authorization: connection.context.authorization,
authorization: connection.authorization,
},
language: connection.context.language,
language: connection.language,
}
return createContextObject({ context, req, res })
return createContextObject({ context, req })
} else {
return createContextObject({ context, req, res })
}
Expand Down
23 changes: 12 additions & 11 deletions api-js/src/on-connect.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
export const customOnConnect =
({
context,
createContext,
serverContext,
createI18n,
verifyToken,
userRequired,
loadUserByKey,
verifiedRequired,
}) =>
async (connectionParams, webSocket) => {
async (connectionParams, webSocket, context) => {
const expandedContext = { ...serverContext, ...context }

const factoryFunc = createContext(expandedContext)

const enLangPos = String(
webSocket.upgradeReq.headers['accept-language'],
).indexOf('en')
Expand All @@ -20,10 +25,7 @@ export const customOnConnect =
language = 'fr'
}

let authorization
if (connectionParams.authorization) {
authorization = connectionParams.authorization
}
const authorization = connectionParams?.authorization

const i18n = createI18n(language)
const verify = verifyToken({ i18n })
Expand All @@ -34,7 +36,7 @@ export const customOnConnect =
userKey = verify({ token }).userKey
}

const { query } = context
const { query } = serverContext

const user = await userRequired({
i18n,
Expand All @@ -46,8 +48,7 @@ export const customOnConnect =

console.info(`User: ${userKey}, connected to subscription.`)

return {
language,
authorization,
}
const finalContext = await factoryFunc({ req: context.request, connection: { language, authorization } })

return finalContext
}
48 changes: 31 additions & 17 deletions api-js/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import cookieParser from 'cookie-parser'
import cors from 'cors'
import express from 'express'
import http from 'http'
import { ApolloServerPluginLandingPageGraphQLPlayground as enablePlayground } from 'apollo-server-core'
import { ApolloServer } from 'apollo-server-express'
import requestLanguage from 'express-request-language'
import { GraphQLSchema } from 'graphql'
import { execute, subscribe, GraphQLSchema } from 'graphql'
import depthLimit from 'graphql-depth-limit'
import { createComplexityLimitRule } from 'graphql-validation-complexity'
import { SubscriptionServer } from 'subscriptions-transport-ws'

import { createContext } from './create-context'
import { createQuerySchema } from './query'
Expand Down Expand Up @@ -45,7 +47,7 @@ const createValidationRules = (
]
}

export const Server = ({
export const Server = async ({
maxDepth,
complexityCost,
scalarCost,
Expand Down Expand Up @@ -74,19 +76,13 @@ export const Server = ({
res.json({ ok: 'yes' })
})

const httpServer = http.createServer(app)

const schema = createSchema()

const server = new ApolloServer({
schema: createSchema(),
schema,
context: createContext(context),
subscriptions: {
onConnect: customOnConnect({
context,
createI18n,
verifyToken,
userRequired,
loadUserByKey,
verifiedRequired,
}),
},
validationRules: createValidationRules(
maxDepth,
complexityCost,
Expand All @@ -95,15 +91,33 @@ export const Server = ({
listFactor,
),
introspection: true,
playground: true,
tracing,
plugins: [enablePlayground()],
})

await server.start()
server.applyMiddleware({ app })

const httpServer = http.createServer(app)

server.installSubscriptionHandlers(httpServer)
SubscriptionServer.create(
{
schema,
execute,
subscribe,
onConnect: customOnConnect({
createContext,
serverContext: context,
createI18n,
verifyToken,
userRequired,
loadUserByKey,
verifiedRequired,
}),
},
{
server: httpServer,
path: server.graphqlPath,
},
)

return httpServer
}