initial update
This commit is contained in:
21
node_modules/@apollo/utils.usagereporting/LICENSE
generated
vendored
Normal file
21
node_modules/@apollo/utils.usagereporting/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Apollo Graph, Inc. (Formerly Meteor Development Group, Inc.)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
21
node_modules/@apollo/utils.usagereporting/README.md
generated
vendored
Normal file
21
node_modules/@apollo/utils.usagereporting/README.md
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# usageReporting
|
||||
|
||||
This package exports the useful bits of usage reporting related to signature computation and referenced field calculation.
|
||||
|
||||
## `usageReportingSignature`
|
||||
|
||||
In Apollo Studio, we want to group requests making the same query together, and treat different queries distinctly. But what does it mean for two queries to be "the same"? And what if you don't want to send the full text of the query to Apollo's servers, either because it contains sensitive data orw because it contains extraneous operations or fragments?
|
||||
|
||||
To solve these problems, Apollo Studio and related components have the concept of "signatures". We don't (by default) send the full query string of queries to Apollo's servers. Instead, each trace has its query string's "signature".
|
||||
|
||||
The `usageReportingSignature` function is a combination of the following transforms:
|
||||
|
||||
- `dropUnusedDefinitions`: removes operations and fragments that aren't going to be used in execution
|
||||
- `stripSensitiveLiterals`: replaces all numeric and string literals as well as list and object input values with "empty" values
|
||||
- `removeAliases`: removes field aliasing from the operation
|
||||
- `sortAST`: sorts the children of most multi-child nodes consistently
|
||||
- `printWithReducedWhitespace`, a variant on graphql-js's `print` which gets rid of unneeded whitespace
|
||||
|
||||
## `calculateReferencedFieldsByType`
|
||||
|
||||
Given a `DocumentNode` (operation) and a `GraphQLSchema`, `calculateReferencedFieldsByType` constructs a record of `typeName -> { fieldNames: string[], isInterface: boolean }`. This record _is_ the field usage data sent to Apollo Studio keyed by operation signature (`usageReportingSignature`).
|
||||
13
node_modules/@apollo/utils.usagereporting/dist/calculateReferencedFieldsByType.d.ts
generated
vendored
Normal file
13
node_modules/@apollo/utils.usagereporting/dist/calculateReferencedFieldsByType.d.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { type DocumentNode, type GraphQLSchema } from "graphql";
|
||||
import { ReferencedFieldsForType } from "@apollo/usage-reporting-protobuf";
|
||||
export interface OperationDerivedData {
|
||||
signature: string;
|
||||
referencedFieldsByType: ReferencedFieldsByType;
|
||||
}
|
||||
export type ReferencedFieldsByType = Record<string, ReferencedFieldsForType>;
|
||||
export declare function calculateReferencedFieldsByType({ document, schema, resolvedOperationName, }: {
|
||||
document: DocumentNode;
|
||||
resolvedOperationName: string | null;
|
||||
schema: GraphQLSchema;
|
||||
}): ReferencedFieldsByType;
|
||||
//# sourceMappingURL=calculateReferencedFieldsByType.d.ts.map
|
||||
1
node_modules/@apollo/utils.usagereporting/dist/calculateReferencedFieldsByType.d.ts.map
generated
vendored
Normal file
1
node_modules/@apollo/utils.usagereporting/dist/calculateReferencedFieldsByType.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"calculateReferencedFieldsByType.d.ts","sourceRoot":"","sources":["../src/calculateReferencedFieldsByType.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,aAAa,EAMnB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,sBAAsB,CAAC;CAChD;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;AAE7E,wBAAgB,+BAA+B,CAAC,EAC9C,QAAQ,EACR,MAAM,EACN,qBAAqB,GACtB,EAAE;IACD,QAAQ,EAAE,YAAY,CAAC;IACvB,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,MAAM,EAAE,aAAa,CAAC;CACvB,GAAG,sBAAsB,CA8DzB"}
|
||||
42
node_modules/@apollo/utils.usagereporting/dist/calculateReferencedFieldsByType.js
generated
vendored
Normal file
42
node_modules/@apollo/utils.usagereporting/dist/calculateReferencedFieldsByType.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.calculateReferencedFieldsByType = void 0;
|
||||
const graphql_1 = require("graphql");
|
||||
const usage_reporting_protobuf_1 = require("@apollo/usage-reporting-protobuf");
|
||||
function calculateReferencedFieldsByType({ document, schema, resolvedOperationName, }) {
|
||||
const documentSeparatedByOperation = (0, graphql_1.separateOperations)(document);
|
||||
const filteredDocument = documentSeparatedByOperation[resolvedOperationName !== null && resolvedOperationName !== void 0 ? resolvedOperationName : ""];
|
||||
if (!filteredDocument) {
|
||||
throw Error(`shouldn't happen: operation '${resolvedOperationName !== null && resolvedOperationName !== void 0 ? resolvedOperationName : ""}' not found`);
|
||||
}
|
||||
const typeInfo = new graphql_1.TypeInfo(schema);
|
||||
const interfaces = new Set();
|
||||
const referencedFieldSetByType = Object.create(null);
|
||||
(0, graphql_1.visit)(filteredDocument, (0, graphql_1.visitWithTypeInfo)(typeInfo, {
|
||||
Field(field) {
|
||||
const fieldName = field.name.value;
|
||||
const parentType = typeInfo.getParentType();
|
||||
if (!parentType) {
|
||||
throw Error(`shouldn't happen: missing parent type for field ${fieldName}`);
|
||||
}
|
||||
const parentTypeName = parentType.name;
|
||||
if (!referencedFieldSetByType[parentTypeName]) {
|
||||
referencedFieldSetByType[parentTypeName] = new Set();
|
||||
if ((0, graphql_1.isInterfaceType)(parentType)) {
|
||||
interfaces.add(parentTypeName);
|
||||
}
|
||||
}
|
||||
referencedFieldSetByType[parentTypeName].add(fieldName);
|
||||
},
|
||||
}));
|
||||
const referencedFieldsByType = Object.create(null);
|
||||
for (const [typeName, fieldNames] of Object.entries(referencedFieldSetByType)) {
|
||||
referencedFieldsByType[typeName] = new usage_reporting_protobuf_1.ReferencedFieldsForType({
|
||||
fieldNames: [...fieldNames],
|
||||
isInterface: interfaces.has(typeName),
|
||||
});
|
||||
}
|
||||
return referencedFieldsByType;
|
||||
}
|
||||
exports.calculateReferencedFieldsByType = calculateReferencedFieldsByType;
|
||||
//# sourceMappingURL=calculateReferencedFieldsByType.js.map
|
||||
1
node_modules/@apollo/utils.usagereporting/dist/calculateReferencedFieldsByType.js.map
generated
vendored
Normal file
1
node_modules/@apollo/utils.usagereporting/dist/calculateReferencedFieldsByType.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"calculateReferencedFieldsByType.js","sourceRoot":"","sources":["../src/calculateReferencedFieldsByType.ts"],"names":[],"mappings":";;;AAAA,qCAQiB;AACjB,+EAA2E;AAS3E,SAAgB,+BAA+B,CAAC,EAC9C,QAAQ,EACR,MAAM,EACN,qBAAqB,GAKtB;IASC,MAAM,4BAA4B,GAAG,IAAA,4BAAkB,EAAC,QAAQ,CAAC,CAAC;IAClE,MAAM,gBAAgB,GACpB,4BAA4B,CAAC,qBAAqB,aAArB,qBAAqB,cAArB,qBAAqB,GAAI,EAAE,CAAC,CAAC;IAC5D,IAAI,CAAC,gBAAgB,EAAE;QAGrB,MAAM,KAAK,CACT,gCAAgC,qBAAqB,aAArB,qBAAqB,cAArB,qBAAqB,GAAI,EAAE,aAAa,CACzE,CAAC;KACH;IACD,MAAM,QAAQ,GAAG,IAAI,kBAAQ,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,wBAAwB,GAAgC,MAAM,CAAC,MAAM,CACzE,IAAI,CACL,CAAC;IACF,IAAA,eAAK,EACH,gBAAgB,EAChB,IAAA,2BAAiB,EAAC,QAAQ,EAAE;QAC1B,KAAK,CAAC,KAAK;YACT,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;YACnC,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,EAAE,CAAC;YAC5C,IAAI,CAAC,UAAU,EAAE;gBACf,MAAM,KAAK,CACT,mDAAmD,SAAS,EAAE,CAC/D,CAAC;aACH;YACD,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC;YACvC,IAAI,CAAC,wBAAwB,CAAC,cAAc,CAAC,EAAE;gBAC7C,wBAAwB,CAAC,cAAc,CAAC,GAAG,IAAI,GAAG,EAAU,CAAC;gBAC7D,IAAI,IAAA,yBAAe,EAAC,UAAU,CAAC,EAAE;oBAC/B,UAAU,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;iBAChC;aACF;YAGD,wBAAwB,CAAC,cAAc,CAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3D,CAAC;KACF,CAAC,CACH,CAAC;IAKF,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnD,KAAK,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CACjD,wBAAwB,CACzB,EAAE;QACD,sBAAsB,CAAC,QAAQ,CAAC,GAAG,IAAI,kDAAuB,CAAC;YAC7D,UAAU,EAAE,CAAC,GAAG,UAAU,CAAC;YAC3B,WAAW,EAAE,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;SACtC,CAAC,CAAC;KACJ;IACD,OAAO,sBAAsB,CAAC;AAChC,CAAC;AAtED,0EAsEC"}
|
||||
4
node_modules/@apollo/utils.usagereporting/dist/index.d.ts
generated
vendored
Normal file
4
node_modules/@apollo/utils.usagereporting/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export { calculateReferencedFieldsByType } from "./calculateReferencedFieldsByType";
|
||||
export type { OperationDerivedData, ReferencedFieldsByType, } from "./calculateReferencedFieldsByType";
|
||||
export { usageReportingSignature } from "./signature";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
node_modules/@apollo/utils.usagereporting/dist/index.d.ts.map
generated
vendored
Normal file
1
node_modules/@apollo/utils.usagereporting/dist/index.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AACpF,YAAY,EACV,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
|
||||
8
node_modules/@apollo/utils.usagereporting/dist/index.js
generated
vendored
Normal file
8
node_modules/@apollo/utils.usagereporting/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.usageReportingSignature = exports.calculateReferencedFieldsByType = void 0;
|
||||
var calculateReferencedFieldsByType_1 = require("./calculateReferencedFieldsByType");
|
||||
Object.defineProperty(exports, "calculateReferencedFieldsByType", { enumerable: true, get: function () { return calculateReferencedFieldsByType_1.calculateReferencedFieldsByType; } });
|
||||
var signature_1 = require("./signature");
|
||||
Object.defineProperty(exports, "usageReportingSignature", { enumerable: true, get: function () { return signature_1.usageReportingSignature; } });
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
node_modules/@apollo/utils.usagereporting/dist/index.js.map
generated
vendored
Normal file
1
node_modules/@apollo/utils.usagereporting/dist/index.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qFAAoF;AAA3E,kJAAA,+BAA+B,OAAA;AAKxC,yCAAsD;AAA7C,oHAAA,uBAAuB,OAAA"}
|
||||
3
node_modules/@apollo/utils.usagereporting/dist/signature.d.ts
generated
vendored
Normal file
3
node_modules/@apollo/utils.usagereporting/dist/signature.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import type { DocumentNode } from "graphql";
|
||||
export declare function usageReportingSignature(ast: DocumentNode, operationName: string): string;
|
||||
//# sourceMappingURL=signature.d.ts.map
|
||||
1
node_modules/@apollo/utils.usagereporting/dist/signature.d.ts.map
generated
vendored
Normal file
1
node_modules/@apollo/utils.usagereporting/dist/signature.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../src/signature.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,YAAY,EACjB,aAAa,EAAE,MAAM,GACpB,MAAM,CAUR"}
|
||||
15
node_modules/@apollo/utils.usagereporting/dist/signature.js
generated
vendored
Normal file
15
node_modules/@apollo/utils.usagereporting/dist/signature.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.usageReportingSignature = void 0;
|
||||
const utils_dropunuseddefinitions_1 = require("@apollo/utils.dropunuseddefinitions");
|
||||
const utils_stripsensitiveliterals_1 = require("@apollo/utils.stripsensitiveliterals");
|
||||
const utils_printwithreducedwhitespace_1 = require("@apollo/utils.printwithreducedwhitespace");
|
||||
const utils_removealiases_1 = require("@apollo/utils.removealiases");
|
||||
const utils_sortast_1 = require("@apollo/utils.sortast");
|
||||
function usageReportingSignature(ast, operationName) {
|
||||
return (0, utils_printwithreducedwhitespace_1.printWithReducedWhitespace)((0, utils_sortast_1.sortAST)((0, utils_removealiases_1.removeAliases)((0, utils_stripsensitiveliterals_1.stripSensitiveLiterals)((0, utils_dropunuseddefinitions_1.dropUnusedDefinitions)(ast, operationName), {
|
||||
hideListAndObjectLiterals: true,
|
||||
}))));
|
||||
}
|
||||
exports.usageReportingSignature = usageReportingSignature;
|
||||
//# sourceMappingURL=signature.js.map
|
||||
1
node_modules/@apollo/utils.usagereporting/dist/signature.js.map
generated
vendored
Normal file
1
node_modules/@apollo/utils.usagereporting/dist/signature.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"signature.js","sourceRoot":"","sources":["../src/signature.ts"],"names":[],"mappings":";;;AAqBA,qFAA4E;AAC5E,uFAA8E;AAC9E,+FAAsF;AACtF,qEAA4D;AAC5D,yDAAgD;AAGhD,SAAgB,uBAAuB,CACrC,GAAiB,EACjB,aAAqB;IAErB,OAAO,IAAA,6DAA0B,EAC/B,IAAA,uBAAO,EACL,IAAA,mCAAa,EACX,IAAA,qDAAsB,EAAC,IAAA,mDAAqB,EAAC,GAAG,EAAE,aAAa,CAAC,EAAE;QAChE,yBAAyB,EAAE,IAAI;KAChC,CAAC,CACH,CACF,CACF,CAAC;AACJ,CAAC;AAbD,0DAaC"}
|
||||
34
node_modules/@apollo/utils.usagereporting/package.json
generated
vendored
Normal file
34
node_modules/@apollo/utils.usagereporting/package.json
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@apollo/utils.usagereporting",
|
||||
"version": "2.1.0",
|
||||
"description": "Generate a signature for Apollo usage reporting",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/apollographql/apollo-utils.git",
|
||||
"directory": "packages/usageReporting/"
|
||||
},
|
||||
"keywords": [
|
||||
"apollo",
|
||||
"graphql",
|
||||
"typescript",
|
||||
"node"
|
||||
],
|
||||
"author": "Apollo <packages@apollographql.com>",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/usage-reporting-protobuf": "^4.1.0",
|
||||
"@apollo/utils.dropunuseddefinitions": "^2.0.1",
|
||||
"@apollo/utils.stripsensitiveliterals": "^2.0.1",
|
||||
"@apollo/utils.printwithreducedwhitespace": "^2.0.1",
|
||||
"@apollo/utils.removealiases": "2.0.1",
|
||||
"@apollo/utils.sortast": "^2.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "14.x || 15.x || 16.x"
|
||||
}
|
||||
}
|
||||
17
node_modules/@apollo/utils.usagereporting/src/__tests__/__snapshots__/signature.test.ts.snap
generated
vendored
Normal file
17
node_modules/@apollo/utils.usagereporting/src/__tests__/__snapshots__/signature.test.ts.snap
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`usageReportingSignature basic test 1`] = `"{user{name}}"`;
|
||||
|
||||
exports[`usageReportingSignature basic test with query 1`] = `"{user{name}}"`;
|
||||
|
||||
exports[`usageReportingSignature basic with operation name 1`] = `"query OpName{user{name}}"`;
|
||||
|
||||
exports[`usageReportingSignature fragment 1`] = `"fragment Bar on User{asd}{user{name...Bar}}"`;
|
||||
|
||||
exports[`usageReportingSignature fragments in various order 1`] = `"fragment Bar on User{asd}{user{name...Bar}}"`;
|
||||
|
||||
exports[`usageReportingSignature full test 1`] = `"fragment Bar on User{age@skip(if:$a)...Nested}fragment Nested on User{blah}query Foo($a:Boolean,$b:Int){user(age:0,name:""){name tz...Bar...on User{bee hello}}}"`;
|
||||
|
||||
exports[`usageReportingSignature with various argument types 1`] = `"query OpName($a:[[Boolean!]!],$b:EnumType,$c:Int!){user{name(apple:$a,bag:$b,cat:$c)}}"`;
|
||||
|
||||
exports[`usageReportingSignature with various inline types 1`] = `"query OpName{user{name(apple:[],bag:{},cat:ENUM_VALUE)}}"`;
|
||||
299
node_modules/@apollo/utils.usagereporting/src/__tests__/calculateReferencedFieldsByType.test.ts
generated
vendored
Normal file
299
node_modules/@apollo/utils.usagereporting/src/__tests__/calculateReferencedFieldsByType.test.ts
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
import { buildASTSchema, type DocumentNode, validate } from "graphql";
|
||||
import gql from "graphql-tag";
|
||||
import { calculateReferencedFieldsByType } from "..";
|
||||
|
||||
const schema = buildASTSchema(gql`
|
||||
type Query {
|
||||
f1: Int
|
||||
f2: Int
|
||||
a: A
|
||||
aa: A
|
||||
myInterface: MyInterface
|
||||
}
|
||||
|
||||
type A implements MyInterface {
|
||||
x: ID
|
||||
y: String!
|
||||
}
|
||||
|
||||
interface MyInterface {
|
||||
x: ID
|
||||
}
|
||||
`);
|
||||
|
||||
function validateAndCalculate({
|
||||
document,
|
||||
resolvedOperationName = null,
|
||||
}: {
|
||||
document: DocumentNode;
|
||||
resolvedOperationName?: string | null;
|
||||
}) {
|
||||
// First validate the document, since calculateReferencedFieldsByType expects
|
||||
// that.
|
||||
expect(validate(schema, document)).toStrictEqual([]);
|
||||
return calculateReferencedFieldsByType({
|
||||
schema,
|
||||
document,
|
||||
resolvedOperationName,
|
||||
});
|
||||
}
|
||||
|
||||
describe("calculateReferencedFieldsByType", () => {
|
||||
it("basic", () => {
|
||||
expect(
|
||||
validateAndCalculate({
|
||||
document: gql`
|
||||
{
|
||||
f1
|
||||
}
|
||||
`,
|
||||
}),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"Query": {
|
||||
"fieldNames": [
|
||||
"f1",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it("aliases use actual field name", () => {
|
||||
expect(
|
||||
validateAndCalculate({
|
||||
document: gql`
|
||||
{
|
||||
aliased: f1
|
||||
}
|
||||
`,
|
||||
}),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"Query": {
|
||||
"fieldNames": [
|
||||
"f1",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it("multiple operations and fragments", () => {
|
||||
expect(
|
||||
validateAndCalculate({
|
||||
document: gql`
|
||||
query Q1 {
|
||||
f1
|
||||
a {
|
||||
...AStuff
|
||||
}
|
||||
}
|
||||
query Q2 {
|
||||
f2
|
||||
aa {
|
||||
...OtherAStuff
|
||||
}
|
||||
}
|
||||
fragment AStuff on A {
|
||||
x
|
||||
}
|
||||
fragment OtherAStuff on A {
|
||||
y
|
||||
}
|
||||
`,
|
||||
resolvedOperationName: "Q1",
|
||||
}),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"A": {
|
||||
"fieldNames": [
|
||||
"x",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
"Query": {
|
||||
"fieldNames": [
|
||||
"f1",
|
||||
"a",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it("interfaces", () => {
|
||||
expect(
|
||||
validateAndCalculate({
|
||||
document: gql`
|
||||
query {
|
||||
myInterface {
|
||||
x
|
||||
}
|
||||
}
|
||||
`,
|
||||
}),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"MyInterface": {
|
||||
"fieldNames": [
|
||||
"x",
|
||||
],
|
||||
"isInterface": true,
|
||||
},
|
||||
"Query": {
|
||||
"fieldNames": [
|
||||
"myInterface",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it("interface with fragment", () => {
|
||||
expect(
|
||||
validateAndCalculate({
|
||||
document: gql`
|
||||
query {
|
||||
myInterface {
|
||||
x
|
||||
... on A {
|
||||
y
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
}),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"A": {
|
||||
"fieldNames": [
|
||||
"y",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
"MyInterface": {
|
||||
"fieldNames": [
|
||||
"x",
|
||||
],
|
||||
"isInterface": true,
|
||||
},
|
||||
"Query": {
|
||||
"fieldNames": [
|
||||
"myInterface",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
it("interface with fragment that uses interface field", () => {
|
||||
expect(
|
||||
validateAndCalculate({
|
||||
document: gql`
|
||||
query {
|
||||
myInterface {
|
||||
... on A {
|
||||
# Even though x exists on the interface, we only want this to
|
||||
# count towards A.x below, because this operation would work just
|
||||
# as well if x were removed from the interface as long as it was
|
||||
# left on A.
|
||||
x
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
}),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"A": {
|
||||
"fieldNames": [
|
||||
"x",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
"Query": {
|
||||
"fieldNames": [
|
||||
"myInterface",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it("using field both with interface and object should work", () => {
|
||||
expect(
|
||||
validateAndCalculate({
|
||||
document: gql`
|
||||
query {
|
||||
myInterface {
|
||||
x
|
||||
... on A {
|
||||
x
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
}),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"A": {
|
||||
"fieldNames": [
|
||||
"x",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
"MyInterface": {
|
||||
"fieldNames": [
|
||||
"x",
|
||||
],
|
||||
"isInterface": true,
|
||||
},
|
||||
"Query": {
|
||||
"fieldNames": [
|
||||
"myInterface",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it("using field multiple times (same level or otherwise) de-dupes", () => {
|
||||
expect(
|
||||
validateAndCalculate({
|
||||
document: gql`
|
||||
query {
|
||||
a1: a {
|
||||
y
|
||||
}
|
||||
a2: a {
|
||||
y
|
||||
}
|
||||
}
|
||||
`,
|
||||
}),
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"A": {
|
||||
"fieldNames": [
|
||||
"y",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
"Query": {
|
||||
"fieldNames": [
|
||||
"a",
|
||||
],
|
||||
"isInterface": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
141
node_modules/@apollo/utils.usagereporting/src/__tests__/signature.test.ts
generated
vendored
Normal file
141
node_modules/@apollo/utils.usagereporting/src/__tests__/signature.test.ts
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
import gql, { disableFragmentWarnings } from "graphql-tag";
|
||||
import { usageReportingSignature } from "..";
|
||||
|
||||
// The gql duplicate fragment warning feature really is just warnings; nothing
|
||||
// breaks if you turn it off in tests.
|
||||
disableFragmentWarnings();
|
||||
|
||||
describe("usageReportingSignature", () => {
|
||||
const cases = [
|
||||
{
|
||||
name: "basic test",
|
||||
operationName: "",
|
||||
input: gql`
|
||||
{
|
||||
user {
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "basic test with query",
|
||||
operationName: "",
|
||||
input: gql`
|
||||
query {
|
||||
user {
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "basic with operation name",
|
||||
operationName: "OpName",
|
||||
input: gql`
|
||||
query OpName {
|
||||
user {
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "with various inline types",
|
||||
operationName: "OpName",
|
||||
input: gql`
|
||||
query OpName {
|
||||
user {
|
||||
name(apple: [[10]], cat: ENUM_VALUE, bag: { input: "value" })
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "with various argument types",
|
||||
operationName: "OpName",
|
||||
input: gql`
|
||||
query OpName($c: Int!, $a: [[Boolean!]!], $b: EnumType) {
|
||||
user {
|
||||
name(apple: $a, cat: $c, bag: $b)
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "fragment",
|
||||
operationName: "",
|
||||
input: gql`
|
||||
{
|
||||
user {
|
||||
name
|
||||
...Bar
|
||||
}
|
||||
}
|
||||
|
||||
fragment Bar on User {
|
||||
asd
|
||||
}
|
||||
|
||||
fragment Baz on User {
|
||||
jkl
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "fragments in various order",
|
||||
operationName: "",
|
||||
input: gql`
|
||||
fragment Bar on User {
|
||||
asd
|
||||
}
|
||||
|
||||
{
|
||||
user {
|
||||
name
|
||||
...Bar
|
||||
}
|
||||
}
|
||||
|
||||
fragment Baz on User {
|
||||
jkl
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "full test",
|
||||
operationName: "Foo",
|
||||
input: gql`
|
||||
query Foo($b: Int, $a: Boolean) {
|
||||
user(name: "hello", age: 5) {
|
||||
...Bar
|
||||
... on User {
|
||||
hello
|
||||
bee
|
||||
}
|
||||
tz
|
||||
aliased: name
|
||||
}
|
||||
}
|
||||
|
||||
fragment Baz on User {
|
||||
asd
|
||||
}
|
||||
|
||||
fragment Bar on User {
|
||||
age @skip(if: $a)
|
||||
...Nested
|
||||
}
|
||||
|
||||
fragment Nested on User {
|
||||
blah
|
||||
}
|
||||
`,
|
||||
},
|
||||
];
|
||||
cases.forEach(({ name, operationName, input }) => {
|
||||
test(name, () => {
|
||||
expect(usageReportingSignature(input, operationName)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
10
node_modules/@apollo/utils.usagereporting/src/__tests__/tsconfig.json
generated
vendored
Normal file
10
node_modules/@apollo/utils.usagereporting/src/__tests__/tsconfig.json
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../../../tsconfig.test.base",
|
||||
"include": ["**/*"],
|
||||
"references": [
|
||||
{ "path": "../../" },
|
||||
{ "path": "../../../dropUnusedDefinitions" },
|
||||
{ "path": "../../../printWithReducedWhitespace" },
|
||||
{ "path": "../../../sortAST" }
|
||||
]
|
||||
}
|
||||
89
node_modules/@apollo/utils.usagereporting/src/calculateReferencedFieldsByType.ts
generated
vendored
Normal file
89
node_modules/@apollo/utils.usagereporting/src/calculateReferencedFieldsByType.ts
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
type DocumentNode,
|
||||
type GraphQLSchema,
|
||||
isInterfaceType,
|
||||
separateOperations,
|
||||
TypeInfo,
|
||||
visit,
|
||||
visitWithTypeInfo,
|
||||
} from "graphql";
|
||||
import { ReferencedFieldsForType } from "@apollo/usage-reporting-protobuf";
|
||||
|
||||
export interface OperationDerivedData {
|
||||
signature: string;
|
||||
referencedFieldsByType: ReferencedFieldsByType;
|
||||
}
|
||||
|
||||
export type ReferencedFieldsByType = Record<string, ReferencedFieldsForType>;
|
||||
|
||||
export function calculateReferencedFieldsByType({
|
||||
document,
|
||||
schema,
|
||||
resolvedOperationName,
|
||||
}: {
|
||||
document: DocumentNode;
|
||||
resolvedOperationName: string | null;
|
||||
schema: GraphQLSchema;
|
||||
}): ReferencedFieldsByType {
|
||||
// If the document contains multiple operations, we only care about fields
|
||||
// referenced in the operation we're using and in fragments that are
|
||||
// (transitively) spread by that operation. (This is because Studio's field
|
||||
// usage accounting is all by operation, not by document.) This does mean that
|
||||
// a field can be textually present in a GraphQL document (and need to exist
|
||||
// for validation) without being represented in the reported referenced fields
|
||||
// structure, but we'd need to change the data model of Studio to be based on
|
||||
// documents rather than fields if we wanted to improve that.
|
||||
const documentSeparatedByOperation = separateOperations(document);
|
||||
const filteredDocument =
|
||||
documentSeparatedByOperation[resolvedOperationName ?? ""];
|
||||
if (!filteredDocument) {
|
||||
// This shouldn't happen because we only should call this function on
|
||||
// properly executable documents.
|
||||
throw Error(
|
||||
`shouldn't happen: operation '${resolvedOperationName ?? ""}' not found`,
|
||||
);
|
||||
}
|
||||
const typeInfo = new TypeInfo(schema);
|
||||
const interfaces = new Set<string>();
|
||||
const referencedFieldSetByType: Record<string, Set<string>> = Object.create(
|
||||
null,
|
||||
);
|
||||
visit(
|
||||
filteredDocument,
|
||||
visitWithTypeInfo(typeInfo, {
|
||||
Field(field) {
|
||||
const fieldName = field.name.value;
|
||||
const parentType = typeInfo.getParentType();
|
||||
if (!parentType) {
|
||||
throw Error(
|
||||
`shouldn't happen: missing parent type for field ${fieldName}`,
|
||||
);
|
||||
}
|
||||
const parentTypeName = parentType.name;
|
||||
if (!referencedFieldSetByType[parentTypeName]) {
|
||||
referencedFieldSetByType[parentTypeName] = new Set<string>();
|
||||
if (isInterfaceType(parentType)) {
|
||||
interfaces.add(parentTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
// We know this is set to an empty Set if it didn't exist immediately above
|
||||
referencedFieldSetByType[parentTypeName]!.add(fieldName);
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// Convert from initial representation (which uses Sets to avoid quadratic
|
||||
// behavior) to the protobufjs objects. (We could also use js_use_toArray here
|
||||
// but that seems a little overkill.)
|
||||
const referencedFieldsByType = Object.create(null);
|
||||
for (const [typeName, fieldNames] of Object.entries(
|
||||
referencedFieldSetByType,
|
||||
)) {
|
||||
referencedFieldsByType[typeName] = new ReferencedFieldsForType({
|
||||
fieldNames: [...fieldNames],
|
||||
isInterface: interfaces.has(typeName),
|
||||
});
|
||||
}
|
||||
return referencedFieldsByType;
|
||||
}
|
||||
6
node_modules/@apollo/utils.usagereporting/src/index.ts
generated
vendored
Normal file
6
node_modules/@apollo/utils.usagereporting/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
export { calculateReferencedFieldsByType } from "./calculateReferencedFieldsByType";
|
||||
export type {
|
||||
OperationDerivedData,
|
||||
ReferencedFieldsByType,
|
||||
} from "./calculateReferencedFieldsByType";
|
||||
export { usageReportingSignature } from "./signature";
|
||||
42
node_modules/@apollo/utils.usagereporting/src/signature.ts
generated
vendored
Normal file
42
node_modules/@apollo/utils.usagereporting/src/signature.ts
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
// In Apollo Studio, we want to group requests making the same query together,
|
||||
// and treat different queries distinctly. But what does it mean for two queries
|
||||
// to be "the same"? And what if you don't want to send the full text of the
|
||||
// query to Apollo's servers, either because it contains sensitive data or
|
||||
// because it contains extraneous operations or fragments?
|
||||
//
|
||||
// To solve these problems, Apollo Studio and related components have the
|
||||
// concept of "signatures". We don't (by default) send the full query string of
|
||||
// queries to Apollo's servers. Instead, each trace has its query string's
|
||||
// "signature".
|
||||
//
|
||||
// This module combines several AST transformations to create its signature:
|
||||
//
|
||||
// - dropUnusedDefinitions, which removes operations and fragments that aren't
|
||||
// going to be used in execution
|
||||
// - stripSensitiveLiterals, which replaces all numeric and string literals as
|
||||
// well as list and object input values with "empty" values
|
||||
// - removeAliases, which removes field aliasing from the query
|
||||
// - sortAST, which sorts the children of most multi-child nodes consistently
|
||||
// - printWithReducedWhitespace, a variant on graphql-js's 'print' which gets
|
||||
// rid of unneeded whitespace
|
||||
import { dropUnusedDefinitions } from "@apollo/utils.dropunuseddefinitions";
|
||||
import { stripSensitiveLiterals } from "@apollo/utils.stripsensitiveliterals";
|
||||
import { printWithReducedWhitespace } from "@apollo/utils.printwithreducedwhitespace";
|
||||
import { removeAliases } from "@apollo/utils.removealiases";
|
||||
import { sortAST } from "@apollo/utils.sortast";
|
||||
import type { DocumentNode } from "graphql";
|
||||
|
||||
export function usageReportingSignature(
|
||||
ast: DocumentNode,
|
||||
operationName: string,
|
||||
): string {
|
||||
return printWithReducedWhitespace(
|
||||
sortAST(
|
||||
removeAliases(
|
||||
stripSensitiveLiterals(dropUnusedDefinitions(ast, operationName), {
|
||||
hideListAndObjectLiterals: true,
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user