Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 2.19.0 #4626

Merged
merged 33 commits into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
34390d9
[GM-615] Rebase of #3465 onto v2.18
glasser Sep 23, 2020
e476ed3
Fix special signatures
Sep 25, 2020
0f7a57f
Fix comments
Sep 25, 2020
e5e8dbb
Dry up if blocks
Sep 28, 2020
e7d6e0c
Add an anonymous op test
Sep 28, 2020
fd9c1d5
More tests
Sep 28, 2020
03d0054
Fix tests names and comments
Sep 30, 2020
3f4f561
better message
Sep 30, 2020
d71abe9
FIx test
Sep 30, 2020
65b7392
Change name
Sep 30, 2020
943fe65
Add changelog and rename option
Oct 2, 2020
7b316b7
Merge branch 'main' into adam/19/10/avoid-cardinality-explosions-and-…
jsegaran Oct 2, 2020
5fd6b74
Update changelog
Oct 2, 2020
41bc758
Merge branch 'main' into release-2.19.0
abernix Oct 5, 2020
fc52194
Fix misspelling in Release template
abernix Oct 5, 2020
8d7f0d7
Update `@apollograpqh/graphql-playground-react` to v1.7.34.
abernix Oct 5, 2020
08434c3
Merge branch 'main' into release-2.19.0
abernix Oct 5, 2020
cc644c9
Merge branch 'main' into release-2.19.0
abernix Oct 5, 2020
90a23b8
Re-export `GetMiddlewareOptions` (#4599)
olyop Oct 5, 2020
6a51396
fix: Missing lru-cache dependency (#4600)
oliversalzburg Oct 5, 2020
43ba6fb
chore(apollo-server-testing): improve types (#4383)
DouglasGabr Oct 5, 2020
507081e
Release
abernix Oct 5, 2020
5297a8d
Merge branch 'main' into release-2.19.0
glasser Oct 5, 2020
49204ba
Update changelog
Oct 16, 2020
b427e78
Merge pull request #3465 from apollographql/adam/19/10/avoid-cardinal…
jsegaran Oct 16, 2020
495f8d8
Release
trevor-scheer Oct 19, 2020
5f10775
Merge branch 'main' into release-2.19.0
trevor-scheer Oct 29, 2020
7b31c3a
Revert "Update `@apollograpqh/graphql-playground-react` to v1.7.34."
trevor-scheer Oct 30, 2020
9666ae1
fix(lambda) File uploads (#4506)
mathix420 Oct 30, 2020
225a3ce
CHANGELOG update
trevor-scheer Oct 30, 2020
c7062dd
Release
trevor-scheer Oct 30, 2020
be5bdd3
Revert "Release"
trevor-scheer Oct 30, 2020
4c6d16f
Release
trevor-scheer Oct 30, 2020
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
2 changes: 1 addition & 1 deletion .github/APOLLO_RELEASE_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ As with [release PRs in the past](https://github.com/apollographql/apollo-server

Check the appropriate milestone (to the right) for more details on what we hope to get into this release!

The intention of these release branches is to gather changes which are intended to land in a specific version (again, indiciated by the subject of this PR). Release branches allow additional clarity into what is being staged, provide a forum for comments from the community pertaining to the release's stability, and to facilitate the creation of pre-releases (e.g. `alpha`, `beta`, `rc`) without affecting the `main` branch.
The intention of these release branches is to gather changes which are intended to land in a specific version (again, indicated by the subject of this PR). Release branches allow additional clarity into what is being staged, provide a forum for comments from the community pertaining to the release's stability, and to facilitate the creation of pre-releases (e.g. `alpha`, `beta`, `rc`) without affecting the `main` branch.

PRs for new features might be opened against or re-targeted to this branch by the project maintainers. The `main` branch may be periodically merged into this branch up until the point in time that this branch is being prepared for release. Depending on the size of the release, this may be once it reaches RC (release candidate) stage with an `-rc.x` release suffix. Some less substantial releases may be short-lived and may never have pre-release versions.

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ 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.19.0

- `apollo-server-testing`: types: Allow generic `variables` usage of `query` and `mutate` functions. [PR #4383](https://github.com/apollograpqh/apollo-server/pull/4383)
- `apollo-server-express`: Export the `GetMiddlewareOptions` type. [PR #4599](https://github.com/apollograpqh/apollo-server/pull/4599)
- `apollo-server-lambda`: Fix file uploads - ignore base64 decoding for multipart queries. [PR #4506](https://github.com/apollographql/apollo-server/pull/4506)
- `apollo-server-core`: Do not send operation documents that cannot be executed to Apollo Studio. Instead, information about these operations will be combined into one "operation" for parse failures, one for validation failures, and one for unknown operation names.

## v2.18.2

- `apollo-server-core`: Explicitly include `lru-cache` dependency in `apollo-server-core`'s dependencies. [PR #4600](https://github.com/apollographql/apollo-server/pull/4600)
Expand Down
14 changes: 14 additions & 0 deletions docs/source/api/plugin/usage-reporting.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,20 @@ If you're using the `overrideReportedSchema` option with the [schema reporting p
</td>
</tr>

<tr>
<td>

###### `sendUnexecutableOperationDocuments`

`Boolean`
</td>
<td>

Statistics about operations that your server cannot execute are not reported under each document separately to Apollo Studio, but are grouped together as "parse failure", "validation failure", or "unknown operation name". By default, the usage reporting plugin does not include the full operation document in reported traces, because it is challenging to strip potential private information (like string constants) from invalid operations. If you'd like the usage reporting plugin to send the full operation document and operation name so you can view it in Apollo Studio's trace view, set this to true.

</td>
</tr>

<tr>
<td colspan="2">

Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-cache-control/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-cache-control",
"version": "0.11.3",
"version": "0.11.4",
"description": "A GraphQL extension for cache control",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ describe('plugin', () => {
return pluginTestHarness({
pluginInstance,
overallCachePolicy,
graphqlRequest: { query: 'does not matter' },
// This query needs to pass graphql validation
graphqlRequest: { query: 'query { hello }' },
executor: () => {
const response: GraphQLResponse = {
http: {
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.9.4",
"version": "0.9.5",
"author": "Apollo <opensource@apollographql.com>",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-reporting-protobuf/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-reporting-protobuf",
"version": "0.6.0",
"version": "0.6.1",
"description": "Protobuf format for Apollo usage reporting",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
6 changes: 6 additions & 0 deletions packages/apollo-reporting-protobuf/src/reports.proto
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ message Trace {
// instead.
string signature = 19;

// Optional: when GraphQL parsing or validation against the GraphQL schema fails, these fields
// can include reference to the operation being sent for users to dig into the set of operations
// that are failing validation.
string unexecutedOperationBody = 27;
string unexecutedOperationName = 28;

Details details = 6;

// Note: engineproxy always sets client_name, client_version, and client_address to "none".
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.18.2",
"version": "2.19.0",
"description": "Production-ready Node.js GraphQL server for Azure Functions",
"keywords": [
"GraphQL",
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.18.2",
"version": "2.19.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.18.2",
"version": "2.19.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.18.2",
"version": "2.19.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-core/src/plugin/traceTreeBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class TraceTreeBuilder {
private nodes = new Map<string, Trace.Node>([
[responsePathAsString(), this.rootNode],
]);
private rewriteError?: (err: GraphQLError) => GraphQLError | null;
private readonly rewriteError?: (err: GraphQLError) => GraphQLError | null;

public constructor(options: {
logger?: Logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ describe('end-to-end', () => {
async function runTest({
pluginOptions = {},
expectReport = true,
query,
operationName,
}: {
pluginOptions?: ApolloServerPluginUsageReportingOptions<any>;
expectReport?: boolean;
query?: string;
operationName?: string | null;
}) {
const typeDefs = `
type User {
Expand All @@ -43,7 +47,7 @@ describe('end-to-end', () => {
}
`;

const query = `
const defaultQuery = `
query q {
author(id: 5) {
name
Expand Down Expand Up @@ -81,8 +85,10 @@ describe('end-to-end', () => {
pluginInstance,
schema,
graphqlRequest: {
query,
operationName: 'q',
query: query ?? defaultQuery,
// If operation name is specified use it. If it is specified as null convert it to
// undefined because graphqlRequest expects string | undefined
operationName: operationName === undefined ? 'q' : (operationName || undefined),
extensions: {
clientName: 'testing suite',
},
Expand Down Expand Up @@ -124,6 +130,47 @@ describe('end-to-end', () => {
).toBeTruthy();
});

it('fails parse for non-parseable gql', async () => {
const { report } = await runTest({ query: 'random text' });
expect(Object.keys(report!.tracesPerQuery)).toHaveLength(1);
expect(Object.keys(report!.tracesPerQuery)[0]).toBe(
'## GraphQLParseFailure\n',
);
const traces = Object.values(report!.tracesPerQuery)[0].trace;
expect(traces).toHaveLength(1);
});

it('validation fails for invalid operation', async () => {
const { report } = await runTest({ query: 'query q { nonExistentField }' });
expect(Object.keys(report!.tracesPerQuery)).toHaveLength(1);
expect(Object.keys(report!.tracesPerQuery)[0]).toBe(
'## GraphQLValidationFailure\n',
);
const traces = Object.values(report!.tracesPerQuery)[0].trace;
expect(traces).toHaveLength(1);
});

it('unknown operation error if not specified', async () => {
const { report } = await runTest({ query: 'query notQ { aString }' });
expect(Object.keys(report!.tracesPerQuery)).toHaveLength(1);
expect(Object.keys(report!.tracesPerQuery)[0]).toBe(
'## GraphQLUnknownOperationName\n',
);
const traces = Object.values(report!.tracesPerQuery)[0].trace;
expect(traces).toHaveLength(1);
});

it('handles anonymous operation', async () => {
const { report } = await runTest({
query: 'query { aString }',
operationName: null,
});
expect(Object.keys(report!.tracesPerQuery)).toHaveLength(1);
expect(Object.keys(report!.tracesPerQuery)[0]).toMatch(/^# -\n/);
const traces = Object.values(report!.tracesPerQuery)[0].trace;
expect(traces).toHaveLength(1);
});

describe('includeRequest', () => {
it('include based on operation name', async () => {
const { report, context } = await runTest({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe('signature cache key', () => {
expect(signatureCacheKey('abc123', '')).toEqual('abc123');
});

it('generates without the operationName', () => {
it('generates with the operationName', () => {
expect(signatureCacheKey('abc123', 'myOperation')).toEqual(
'abc123:myOperation',
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@ export interface ApolloServerPluginUsageReportingOptions<TContext> {
* ID that the other plugin reports.
*/
overrideReportedSchema?: string;
/**
* Whether to include the entire document in the trace if the operation
* was a GraphQL parse or validation error (i.e. failed the GraphQL parse or
* validation phases). This will be included as a separate field on the trace
* and the operation name and signature will always be reported with a cosntant
* identifier. Whether the operation was a parse failure or a validation
* failure will be embedded within the stats report key itself.
*/
sendUnexecutableOperationDocuments?: boolean;
//#endregion

//#region Configure the mechanics of communicating with Apollo's servers.
Expand Down
58 changes: 41 additions & 17 deletions packages/apollo-server-core/src/plugin/usageReporting/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ export function ApolloServerPluginUsageReporting<TContext>(
});
treeBuilder.startTiming();
metrics.startHrTime = treeBuilder.startHrTime;
let graphqlValidationFailure = false;
let graphqlUnknownOperationName = false;

if (http) {
treeBuilder.trace.http = new Trace.HTTP({
Expand Down Expand Up @@ -471,15 +473,35 @@ export function ApolloServerPluginUsageReporting<TContext>(
const reportData = getReportData(executableSchemaId);
const { report } = reportData;

let statsReportKey: string | undefined = undefined;
if (!requestContext.document) {
statsReportKey = `## GraphQLParseFailure\n`;
} else if (graphqlValidationFailure) {
statsReportKey = `## GraphQLValidationFailure\n`;
} else if (graphqlUnknownOperationName) {
statsReportKey = `## GraphQLUnknownOperationName\n`;
}

if (statsReportKey) {
if (
options.sendUnexecutableOperationDocuments
) {
treeBuilder.trace.unexecutedOperationBody =
requestContext.source;
treeBuilder.trace.unexecutedOperationName = operationName;
}
} else {
const signature = getTraceSignature();
statsReportKey = `# ${operationName || '-'}\n${signature}`;
}

const protobufError = Trace.verify(treeBuilder.trace);
if (protobufError) {
throw new Error(`Error encoding trace: ${protobufError}`);
}
const encodedTrace = Trace.encode(treeBuilder.trace).finish();

const signature = getTraceSignature();
const encodedTrace = Trace.encode(treeBuilder.trace).finish();

const statsReportKey = `# ${operationName || '-'}\n${signature}`;
if (!report.tracesPerQuery.hasOwnProperty(statsReportKey)) {
report.tracesPerQuery[statsReportKey] = new TracesAndStats();
(report.tracesPerQuery[statsReportKey] as any).encodedTraces = [];
Expand All @@ -489,6 +511,7 @@ export function ApolloServerPluginUsageReporting<TContext>(
(report.tracesPerQuery[statsReportKey] as any).encodedTraces.push(
encodedTrace,
);

reportData.size +=
encodedTrace.length + Buffer.byteLength(statsReportKey);

Expand All @@ -503,9 +526,10 @@ export function ApolloServerPluginUsageReporting<TContext>(
}

function getTraceSignature(): string {
if (!requestContext.document && !requestContext.source) {
// This shouldn't happen: one of those options must be passed to runQuery.
throw new Error('No document or source?');
if (!requestContext.document) {
// This shouldn't happen: no document means parse failure, which
// uses its own special statsReportKey.
throw new Error('No document?');
}

const cacheKey = signatureCacheKey(
Expand All @@ -521,17 +545,6 @@ export function ApolloServerPluginUsageReporting<TContext>(
return cachedSignature;
}

if (!requestContext.document) {
// We didn't get an AST, possibly because of a parse failure. Let's just
// use the full query string.
//
// XXX This does mean that even if you use a calculateSignature which
// hides literals, you might end up sending literals for queries
// that fail parsing or validation. Provide some way to mask them
// anyway?
return requestContext.source as string;
}

const generatedSignature = (
options.calculateSignature || defaultUsageReportingSignature
)(requestContext.document, operationName);
Expand Down Expand Up @@ -591,7 +604,18 @@ export function ApolloServerPluginUsageReporting<TContext>(
treeBuilder.trace.clientName = clientName || '';
}
},
validationDidStart() {
return (validationErrors?: ReadonlyArray<Error>) => {
graphqlValidationFailure = validationErrors
? validationErrors.length !== 0
: false;
};
},
async didResolveOperation(requestContext) {
// If operation is undefined then `getOperationAST` returned null
// and an unknown operation was specified.
graphqlUnknownOperationName =
requestContext.operation === undefined;
await shouldIncludeRequest(requestContext);

if (metrics.captureTraces === false) {
Expand Down
Loading