Skip to content

Commit

Permalink
Remove createContext (#8073)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Cousens <dcousens@users.noreply.github.com>
  • Loading branch information
Josh Calder and dcousens committed Nov 17, 2022
1 parent ee78151 commit 94e12b4
Show file tree
Hide file tree
Showing 30 changed files with 213 additions and 259 deletions.
5 changes: 5 additions & 0 deletions .changeset/goodbye-busted-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-6/core': major
---

Removes `context.startSession` and `context.endSession` - use `context.sessionStrategy.*` directly if required
5 changes: 5 additions & 0 deletions .changeset/goodbye-busted-session.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-6/core': major
---

Removes `createContext`, `createRequestContext` - replace any relevant usage with `.sudo`, `.withSession` or `withRequest`
44 changes: 43 additions & 1 deletion docs/pages/docs/config/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,13 @@ export default config({
port: 3000,
maxFileSize: 200 * 1024 * 1024,
healthCheck: true,
{% if $nextRelease %}
extendExpressApp: (app, context) => { /* ... */ },
extendHttpServer: (httpServer, context, graphQLSchema) => { /* ... */ },
{% else /%}
extendExpressApp: (app, createContext) => { /* ... */ },
extendHttpServer: (httpServer, createContext, graphQLSchema) => { /* ... */ },
{% /if %}
},
/* ... */
});
Expand Down Expand Up @@ -268,7 +273,11 @@ This lets you modify the express app that Keystone creates _before_ the Apollo S
The function is passed two arguments:

- `app`: The express app keystone has created
{% if $nextRelease %}
- `context`: A Keystone Context
{% else /%}
- `async createContext(req, res)`: A function you can call to create a Keystone Context for the request
{% /if %}

For example, you could add your own request logging middleware:

Expand Down Expand Up @@ -304,13 +313,23 @@ You could also use it to add custom REST endpoints to your server, by creating a
```ts
export default config({
server: {
{% if $nextRelease %}
extendExpressApp: (app, _context) => {
app.get('/api/users', async (req, res) => {
const context = _context.withRequest(req, res);
const users = await context.query.User.findMany();
res.json(users);
});
},
{% else /%}
extendExpressApp: (app, createContext) => {
app.get('/api/users', async (req, res) => {
const context = await createContext(req, res);
const users = await context.query.User.findMany();
res.json(users);
});
},
{% /if %}
},
});
```
Expand All @@ -326,7 +345,11 @@ This lets you interact with the node [http.Server](https://nodejs.org/api/http.h
The function is passed in 3 arguments:

- `server` - this is the HTTP server that you can then extend
- `async createContext(req, res)`: A function you can call to create a Keystone Context for the request
{% if $nextRelease %}
- `context`: A Keystone Context
{% else /%}
- `async createRequestContext(req, res)`: A function you can call to create a Keystone Context for the request
{% /if %}
- `graphqlSchema` - this is the keystone graphql schema that can be used in a WebSocket GraphQL server for subscriptions

For example, this function could be used to listen for `'upgrade'` requests for a WebSocket server when adding support for GraphQL subscriptions
Expand All @@ -337,6 +360,16 @@ import { useServer as wsUseServer } from 'graphql-ws/lib/use/ws';

export default config({
server: {
{% if $nextRelease %}
extendHttpServer: (httpServer, context, graphqlSchema) => {
const wss = new WebSocketServer({
server: httpServer,
path: '/api/graphql',
});

wsUseServer({ schema: graphqlSchema }, wss);
},
{% else /%}
extendHttpServer: (httpServer, createRequestContext, graphqlSchema) => {
const wss = new WebSocketServer({
server: httpServer,
Expand All @@ -345,6 +378,7 @@ export default config({

wsUseServer({ schema: graphqlSchema }, wss);
},
{% /if %}
},
});
```
Expand All @@ -360,10 +394,18 @@ import type { SessionStrategy } from '@keystone-6/core/types';
The `session` config option allows you to configure session management of your Keystone system.
It has a TypeScript type of `SessionStrategy<any>`.

{% if $nextRelease %}
In general you will use `SessionStrategy` objects from the `@keystone-6/auth/session` package, rather than writing this yourself.
{% else /%}
In general you will use `SessionStrategy` objects from the `@keystone-6/core/session` package, rather than writing this yourself.
{% /if %}

```typescript
{% if $nextRelease %}
import { statelessSessions } from '@keystone-6/auth/session';
{% else /%}
import { statelessSessions } from '@keystone-6/core/session';
{% /if %}

export default config({
session: statelessSessions({ /* ... */ }),
Expand Down
12 changes: 12 additions & 0 deletions docs/pages/docs/config/session.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ description: "Reference docs for the session property of Keystone’s system con

The `session` property of the [system configuration](./config) object allows you to configure session management of your Keystone system.
It has a TypeScript type of `SessionStrategy<any>`.
{% if $nextRelease %}
In general you will use `SessionStrategy` objects from the `@keystone-6/auth/session` package, rather than writing this yourself.
{% else /%}
In general you will use `SessionStrategy` objects from the `@keystone-6/core/session` package, rather than writing this yourself.
{% /if %}

```typescript
import { config } from '@keystone-6/core';
{% if $nextRelease %}
import { statelessSessions } from '@keystone-6/auth/session';
{% else /%}
import { statelessSessions } from '@keystone-6/core/session';
{% /if %}

export default config({
session: statelessSessions({
Expand Down Expand Up @@ -37,7 +45,11 @@ Both `statelessSessions()` and `storedSessions()` accept a common set of argumen

```typescript
import { config } from '@keystone-6/core';
{% if $nextRelease %}
import { statelessSessions, storedSessions } from '@keystone-6/auth/session';
{% else /%}
import { statelessSessions, storedSessions } from '@keystone-6/core/session';
{% /if %}

export default config({
// Stateless
Expand Down
8 changes: 4 additions & 4 deletions examples/custom-session-validation/keystone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ const withTimeData = (
const { get, start, ...sessionStrategy } = _sessionStrategy;
return {
...sessionStrategy,
get: async ({ req, createContext }) => {
get: async ({ context }) => {
// Get the session from the cookie stored by keystone
const session = await get({ req, createContext });
const session = await get({ context });
// If there is no session returned from keystone or there is no startTime on the session return an invalid session
// If session.startTime is null session.data.passwordChangedAt > session.startTime will always be true and therefore
// the session will never be invalid until the maxSessionAge is reached.
Expand All @@ -56,14 +56,14 @@ const withTimeData = (

return session;
},
start: async ({ res, data, createContext }) => {
start: async ({ data, context }) => {
// Add the current time to the session data
const withTimeData = {
...data,
startTime: new Date(),
};
// Start the keystone session and include the startTime
return await start({ res, data: withTimeData, createContext });
return await start({ data: withTimeData, context });
},
};
};
Expand Down
12 changes: 4 additions & 8 deletions examples/extend-graphql-subscriptions/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import { WebSocketServer } from 'ws';
import { PubSub } from 'graphql-subscriptions';
import { parse } from 'graphql';

import {
CreateRequestContext,
KeystoneGraphQLAPI,
BaseKeystoneTypeInfo,
} from '@keystone-6/core/types';
import { KeystoneGraphQLAPI } from '@keystone-6/core/types';
import { Context } from '.keystone/types';

// Setup pubsub as a Global variable in dev so it survives Hot Reloads.
declare global {
Expand All @@ -22,7 +19,7 @@ globalThis.graphqlSubscriptionPubSub = pubSub;

export const extendHttpServer = (
httpServer: http.Server,
createRequestContext: CreateRequestContext<BaseKeystoneTypeInfo>,
_context: Context,
graphqlSchema: KeystoneGraphQLAPI['schema']
): void => {
// Setup WebSocket server using 'ws'
Expand All @@ -37,8 +34,7 @@ export const extendHttpServer = (
schema: graphqlSchema,
// run these onSubscribe functions as needed or remove them if you don't need them
onSubscribe: async (ctx: any, msg) => {
// @ts-expect-error CreateRequestContext requires `req` and `res` but only `req` is available here
const context = await createRequestContext(ctx.extra.request);
const context = await _context.withRequest(ctx.extra.request);
// Return the execution args for this subscription passing through the Keystone Context
return {
schema: graphqlSchema,
Expand Down
4 changes: 2 additions & 2 deletions examples/rest-api/keystone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ export default config({
- Adds a GET handler for tasks, which will query for tasks in the
Keystone schema and return the results as JSON
*/
extendExpressApp: (app, createContext) => {
extendExpressApp: (app, context) => {
app.use('/rest', async (req, res, next) => {
(req as any).context = await createContext(req, res);
(req as any).context = await context.withRequest(req, res);
next();
});

Expand Down
13 changes: 8 additions & 5 deletions packages/auth/src/gql/getBaseAuthSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function getBaseAuthSchema<I extends string, S extends string>({
[secretField]: graphql.arg({ type: graphql.nonNull(graphql.String) }),
},
async resolve(root, { [identityField]: identity, [secretField]: secret }, context) {
if (!context.startSession) {
if (!context.sessionStrategy) {
throw new Error('No session implementation available on context');
}

Expand All @@ -88,10 +88,13 @@ export function getBaseAuthSchema<I extends string, S extends string>({
}

// Update system state
const sessionToken = await context.startSession({
listKey,
itemId: result.item.id.toString(),
});
const sessionToken = (await context.sessionStrategy.start({
data: {
listKey,
itemId: result.item.id,
},
context,
})) as string;
return { sessionToken, item: result.item };
},
}),
Expand Down
7 changes: 5 additions & 2 deletions packages/auth/src/gql/getInitFirstItemSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function getInitFirstItemSchema({
type: graphql.nonNull(ItemAuthenticationWithPasswordSuccess),
args: { data: graphql.arg({ type: graphql.nonNull(initialCreateInput) }) },
async resolve(rootVal, { data }, context) {
if (!context.startSession) {
if (!context.sessionStrategy) {
throw new Error('No session implementation available on context');
}

Expand All @@ -56,7 +56,10 @@ export function getInitFirstItemSchema({
// (this is also mostly fine, the chance that people are using things where
// the input value can't round-trip like the Upload scalar here is quite low)
const item = await dbItemAPI.createOne({ data: { ...data, ...itemData } });
const sessionToken = await context.startSession({ listKey, itemId: item.id.toString() });
const sessionToken = (await context.sessionStrategy.start({
data: { listKey, itemId: item.id.toString() },
context,
})) as string;
return { item, sessionToken };
},
}),
Expand Down
13 changes: 8 additions & 5 deletions packages/auth/src/gql/getMagicAuthLinkSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function getMagicAuthLinkSchema<I extends string>({
token: graphql.arg({ type: graphql.nonNull(graphql.String) }),
},
async resolve(rootVal, { [identityField]: identity, token }, context) {
if (!context.startSession) {
if (!context.sessionStrategy) {
throw new Error('No session implementation available on context');
}

Expand All @@ -117,10 +117,13 @@ export function getMagicAuthLinkSchema<I extends string>({
data: { [`${tokenType}RedeemedAt`]: new Date().toISOString() },
});

const sessionToken = await context.startSession({
listKey,
itemId: result.item.id.toString(),
});
const sessionToken = (await context.sessionStrategy.start({
data: {
listKey,
itemId: result.item.id.toString(),
},
context,
})) as string;
return { token: sessionToken, item: result.item };
},
}),
Expand Down
6 changes: 3 additions & 3 deletions packages/auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,9 @@ export function createAuth<ListTypeInfo extends BaseListTypeInfo>({
const { get, ...sessionStrategy } = _sessionStrategy;
return {
...sessionStrategy,
get: async ({ req, createContext }) => {
const session = await get({ req, createContext });
const sudoContext = createContext({ sudo: true });
get: async ({ context }) => {
const session = await get({ context });
const sudoContext = context.sudo();
if (
!session ||
!session.listKey ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ export function nextGraphQLAPIRoute(keystoneConfig: KeystoneConfig, prismaClient
const initializedKeystoneConfig = initConfig(keystoneConfig);
const { graphQLSchema, getKeystone } = createSystem(initializedKeystoneConfig);

const keystone = getKeystone(prismaClient);
const { connect, context } = getKeystone(prismaClient);

keystone.connect();
connect();

const apolloServer = createApolloServerMicro({
graphQLSchema,
createContext: keystone.createContext,
context,
sessionStrategy: initializedKeystoneConfig.session,
graphqlConfig: initializedKeystoneConfig.graphql,
connectionPromise: keystone.connect(),
connectionPromise: connect(),
});

let startPromise = apolloServer.start();
Expand Down
Loading

1 comment on commit 94e12b4

@vercel
Copy link

@vercel vercel bot commented on 94e12b4 Nov 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.