Skip to content

Commit

Permalink
Merge pull request #5094 from apollographql/release-2.23.0
Browse files Browse the repository at this point in the history
Release 2.23.0
  • Loading branch information
glasser committed Apr 14, 2021
2 parents 46bd10f + 8a4cc58 commit 1de9af0
Show file tree
Hide file tree
Showing 24 changed files with 124 additions and 96 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ The version headers in this history reflect the versions of Apollo Server itself

> The changes noted within this `vNEXT` section have not been released yet. New PRs and commits which introduce changes should include an entry in this `vNEXT` section as part of their development. With few exceptions, the format of the entry should follow convention (i.e., prefix with package name, use markdown `backtick formatting` for package names and code, suffix with a link to the change-set 脿 la `[PR #YYY](https://link/pull/YYY)`, etc.). When a release is being prepared, a new header will be (manually) created below and the appropriate changes within that release will be moved into the new section.
## v2.23.0

- `apollo-server-core`: Add optional argument to `ApolloServer.executeOperation` allowing the caller to manually specify an argument to the `config` function analogous to that provided by integration packages. [PR #4166](https://github.com/apollographql/apollo-server/pull/4166) [Issue #2886](https://github.com/apollographql/apollo-server/issues/2886)
- `apollo-server-cache-redis`: New `BaseRedisCache` class which takes an `ioredis`-compatible Redis client as an argument. The existing classes `RedisCache` and `RedisClusterCache` (which pass their arguments to `ioredis` constructors) are now implemented in terms of this class. This allows you to use any of the `ioredis` constructor forms rather than just the ones recognized by our classes. This also fixes a long-standing bug where the Redis cache implementations returned a number from `delete()`; it now returns a number, matching what the `KeyValueCache` interface and the TypeScript types expect. [PR #5034](https://github.com/apollographql/apollo-server/pull/5034) [PR #5088](https://github.com/apollographql/apollo-server/pull/5088) [Issue #4870](https://github.com/apollographql/apollo-server/issues/4870) [Issue #5006](https://github.com/apollographql/apollo-server/issues/5006)
- `apollo-server-cache-redis@1.4.0`: New `BaseRedisCache` class which takes an `ioredis`-compatible Redis client as an argument. The existing classes `RedisCache` and `RedisClusterCache` (which pass their arguments to `ioredis` constructors) are now implemented in terms of this class. This allows you to use any of the `ioredis` constructor forms rather than just the ones recognized by our classes. This also fixes a long-standing bug where the Redis cache implementations returned a number from `delete()`; it now returns a number, matching what the `KeyValueCache` interface and the TypeScript types expect. [PR #5034](https://github.com/apollographql/apollo-server/pull/5034) [PR #5088](https://github.com/apollographql/apollo-server/pull/5088) [Issue #4870](https://github.com/apollographql/apollo-server/issues/4870) [Issue #5006](https://github.com/apollographql/apollo-server/issues/5006)
- `apollo-server-core`: Fix type for `formatResponse` function. It never is called with a `null` argument, and is allowed to return `null`. [Issue #5009](https://github.com/apollographql/apollo-server/issues/5009) [PR #5089](https://github.com/apollographql/apollo-server/pull/5089)
- `apollo-server-lambda`: Fix regression in v2.21.2 where thrown errors were replaced by throwing the JS Error class itself. [PR #5085](https://github.com/apollographql/apollo-server/pull/5085)
- `apollo-server-core`: If a client sends a variable of the wrong type, this is now reported as an error with an `extensions.code` of `BAD_USER_INPUT` rather than `INTERNAL_SERVER_ERROR`. [PR #5091](https://github.com/apollographql/apollo-server/pull/5091) [Issue #3498](https://github.com/apollographql/apollo-server/issues/3498)
- `apollo-server-lambda`: Explicitly support API Gateway `payloadFormatVersion` 2.0. Previously some codepaths did appropriate checks to partially support 2.0 and other codepaths could lead to errors like `event.path.endsWith is not a function` (especially since v2.21.1). Note that this changes the TypeScript typing of the `onHealthCheck` callback passed to `createHandler` to indicate that it can receive either type of event. If you are using TypeScript and care about having a precise typing for the argument to your `onHealthCheck` callback, you should determine which payload format you want to support and write `new ApolloServer<APIGatewayProxyEvent>(...)` or `new ApolloServer<APIGatewayProxyEventV2>(...)` (importing these types from `aws-lambda`), or differentiate between the two formats by checking to see if `'path' in event`. [Issue #5084](https://github.com/apollographql/apollo-server/issues/5084) [Issue #5016](https://github.com/apollographql/apollo-server/issues/5016)

## v2.22.2

Expand Down
4 changes: 2 additions & 2 deletions docs/source/performance/apq.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,9 @@ const server = new ApolloServer({
// highlight-start
persistedQueries: {
cache: new BaseRedisCache({
// Note that this uses the "clusterClient" option rather than "client",
// Note that this uses the "noMgetClient" option rather than "client",
// which avoids using the mget command which doesn't work in cluster mode.
clusterClient: new Redis.Cluster(
noMgetClient: new Redis.Cluster(
[{
host: 'redis-node-01-host',
}],
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-datasource-rest/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-datasource-rest",
"version": "0.11.0",
"version": "0.12.0",
"author": "Apollo <opensource@apollographql.com>",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-azure-functions/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-azure-functions",
"version": "2.22.2",
"version": "2.23.0",
"description": "Production-ready Node.js GraphQL server for Azure Functions",
"keywords": [
"GraphQL",
Expand Down
4 changes: 2 additions & 2 deletions packages/apollo-server-cache-redis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ It currently supports a single instance of Redis, [Cluster](http://redis.io/topi

## Usage

This package is built to be compatible with the [ioredis](https://www.npmjs.com/package/ioredis) Redis client. The recommended usage is to use the `BaseRedisCache` class which takes either a `client` option (a client that talks to a single server) or a `clusterClient` option (a client that talks to Redis Cluster). (The difference is that ioredis [only supports the `mget` multi-get command in non-cluster mode](https://github.com/luin/ioredis/issues/811), so using `clusterClient` tells `BaseRedisCache` to use parallel `get` commands instead.)
This package is built to be compatible with the [ioredis](https://www.npmjs.com/package/ioredis) Redis client. The recommended usage is to use the `BaseRedisCache` class which takes either a `client` option (a client that talks to a single server) or a `noMgetClient` option (a client that talks to Redis Cluster). (The difference is that ioredis [only supports the `mget` multi-get command in non-cluster mode](https://github.com/luin/ioredis/issues/811), so using `noMgetClient` tells `BaseRedisCache` to use parallel `get` commands instead.)

You may also use the older `RedisCache` and `RedisClusterCache` classes, which allow you to pass the ioredis constructor arguments directly to the cache class's constructor.
### Single instance
Expand Down Expand Up @@ -67,7 +67,7 @@ const server = new ApolloServer({
typeDefs,
resolvers,
cache: new BaseRedisCache({
clusterClient: new Redis.Cluster(
noMgetClient: new Redis.Cluster(
[{
host: 'redis-node-01-host',
// Options are passed through to the Redis cluster client
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-cache-redis/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-cache-redis",
"version": "1.3.0",
"version": "1.4.0",
"author": "Apollo <opensource@apollographql.com>",
"license": "MIT",
"repository": {
Expand Down
24 changes: 12 additions & 12 deletions packages/apollo-server-cache-redis/src/BaseRedisCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ export interface RedisClient extends BaseRedisClient {
mget: (...key: Array<string>) => Promise<Array<string | null>>;
}

export interface RedisClusterClient extends BaseRedisClient {
export interface RedisNoMgetClient extends BaseRedisClient {
get: (key: string) => Promise<string | null>;
}

/**
* Provide exactly one of the options `client` and `clusterClient`. `client` is
* Provide exactly one of the options `client` and `noMgetClient`. `client` is
* a client that supports the `mget` multiple-get command.
*
* ioredis does not support `mget` for cluster mode (see
* https://github.com/luin/ioredis/issues/811), so if you're using cluster mode,
* pass `clusterClient` instead, which has a `get` method instead of `mget`;
* pass `noMgetClient` instead, which has a `get` method instead of `mget`;
* this package will issue parallel `get` commands instead of a single `mget`
* command if `clusterClient` is provided.
* command if `noMgetClient` is provided.
*/
export interface BaseRedisCacheOptions {
client?: RedisClient;
clusterClient?: RedisClusterClient;
noMgetClient?: RedisNoMgetClient;
}

export class BaseRedisCache implements TestableKeyValueCache<string> {
Expand All @@ -48,25 +48,25 @@ export class BaseRedisCache implements TestableKeyValueCache<string> {
private loader: DataLoader<string, string | null>;

constructor(options: BaseRedisCacheOptions) {
const { client, clusterClient } = options;
if (client && clusterClient) {
throw Error('You may only provide one of `client` and `clusterClient`');
const { client, noMgetClient } = options;
if (client && noMgetClient) {
throw Error('You may only provide one of `client` and `noMgetClient`');
} else if (client) {
this.client = client;
this.loader = new DataLoader((keys) => client.mget(...keys), {
cache: false,
});
} else if (clusterClient) {
this.client = clusterClient;
} else if (noMgetClient) {
this.client = noMgetClient;
this.loader = new DataLoader(
(keys) =>
Promise.all(keys.map((key) => clusterClient.get(key).catch(() => null))),
Promise.all(keys.map((key) => noMgetClient.get(key).catch(() => null))),
{
cache: false,
},
);
} else {
throw Error('You must provide `client` or `clusterClient`');
throw Error('You must provide `client` or `noMgetClient`');
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class RedisClusterCache extends BaseRedisCache {

constructor(nodes: ClusterNode[], options?: ClusterOptions) {
const clusterClient = new Redis.Cluster(nodes, options);
super({ clusterClient });
super({ noMgetClient: clusterClient });
this.clusterClient = clusterClient;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-cloud-functions/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-cloud-functions",
"version": "2.22.2",
"version": "2.23.0",
"description": "Production-ready Node.js GraphQL server for Google Cloud Functions",
"keywords": [
"GraphQL",
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-cloudflare/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-cloudflare",
"version": "2.22.2",
"version": "2.23.0",
"description": "Production-ready Node.js GraphQL server for Cloudflare workers",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-core",
"version": "2.22.2",
"version": "2.23.0",
"description": "Core engine for Apollo GraphQL server",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-errors/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-errors",
"version": "2.4.2",
"version": "2.5.0",
"author": "Apollo <opensource@apollographql.com>",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-express/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-express",
"version": "2.22.2",
"version": "2.23.0",
"description": "Production-ready Node.js GraphQL server for Express and Connect",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-fastify/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-fastify",
"version": "2.22.2",
"version": "2.23.0",
"description": "Production-ready Node.js GraphQL server for Fastify",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-hapi/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-hapi",
"version": "2.22.2",
"version": "2.23.0",
"description": "Production-ready Node.js GraphQL server for Hapi",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-integration-testsuite/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "apollo-server-integration-testsuite",
"private": true,
"version": "2.22.2",
"version": "2.23.0",
"description": "Apollo Server Integrations testsuite",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
80 changes: 43 additions & 37 deletions packages/apollo-server-integration-testsuite/src/ApolloServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { PubSub } from 'graphql-subscriptions';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import WebSocket from 'ws';
import resolvable from '@josephg/resolvable';

import { execute } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';
Expand Down Expand Up @@ -2099,8 +2100,8 @@ export function testApolloServer<AS extends ApolloServerBase>(
.catch(done.fail);
});

it('takes websocket server subscriptions configuration', done => {
const onConnect = jest.fn(connectionParams => ({
it('takes websocket server subscriptions configuration', async () => {
const onConnect = jest.fn((connectionParams) => ({
...connectionParams,
}));
const typeDefs = gql`
Expand Down Expand Up @@ -2136,47 +2137,52 @@ export function testApolloServer<AS extends ApolloServerBase>(
};

const path = '/sub';
createApolloServer({
const { server } = await createApolloServer({
typeDefs,
resolvers,
subscriptions: { onConnect, path },
})
.then(({ port, server }) => {
const subPort = (typeof port === "number" ? port : parseInt(port)) + 1
const websocketServer = new WebSocket.Server({port: subPort})
server.installSubscriptionHandlers(websocketServer);
expect(onConnect).not.toBeCalled();
});
const listening = resolvable();
const websocketServer = new WebSocket.Server({ port: 0 }, () =>
listening.resolve(),
);
await listening;
server.installSubscriptionHandlers(websocketServer);
expect(onConnect).not.toBeCalled();

expect(server.subscriptionsPath).toEqual(path);
const client = new SubscriptionClient(
`ws://localhost:${
(websocketServer.address() as WebSocket.AddressInfo).port
}${server.subscriptionsPath}`,
{},
WebSocket,
);

expect(server.subscriptionsPath).toEqual(path);
const client = new SubscriptionClient(
`ws://localhost:${subPort}${server.subscriptionsPath}`,
{},
WebSocket,
);
const observable = client.request({ query });

const observable = client.request({ query });
const p = resolvable();

let i = 1;
subscription = observable.subscribe({
next: ({ data }) => {
try {
expect(onConnect).toHaveBeenCalledTimes(1);
expect(data.num).toEqual(i);
if (i === 3) {
done();
}
i++;
} catch (e) {
done.fail(e);
}
},
error: done.fail,
complete: () => {
done.fail(new Error('should not complete'));
},
});
})
.catch(done.fail);
let i = 1;
subscription = observable.subscribe({
next: ({ data }) => {
try {
expect(onConnect).toHaveBeenCalledTimes(1);
expect(data.num).toEqual(i);
if (i === 3) {
p.resolve();
}
i++;
} catch (e) {
p.reject(e);
}
},
error: p.reject,
complete: () => {
p.reject(new Error('should not complete'));
},
});
await p;
});

it('allows introspection when introspection is enabled on ApolloServer', done => {
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-koa/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-koa",
"version": "2.22.2",
"version": "2.23.0",
"description": "Production-ready Node.js GraphQL server for Koa",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-lambda/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-lambda",
"version": "2.22.2",
"version": "2.23.0",
"description": "Production-ready Node.js GraphQL server for AWS Lambda",
"keywords": [
"GraphQL",
Expand Down

0 comments on commit 1de9af0

Please sign in to comment.