@graphql-hive/federation-gateway-audit
Version:
Audit tool for Apollo Federation Gateway
382 lines (365 loc) • 8.27 kB
JavaScript
import { c as createSubgraph, a as createTest, s as serve } from './testkit-BtZjdl4n.js';
import '@apollo/composition';
import 'graphql';
import 'fets';
import '@apollo/subgraph';
import 'graphql-yoga';
let products = [];
let categories = [];
const numbers = {};
async function getProducts() {
return products;
}
function getCategories() {
return categories;
}
async function addProduct(name, price) {
const newProduct = {
id: "p-added-" + products.length,
name,
price
};
products.push(newProduct);
return newProduct;
}
async function addCategory(name, requestId) {
if (categories.some((c) => c.id === "c-added-" + requestId)) {
throw new Error("Category with this requestId was already added");
}
const newCategory = {
id: "c-added-" + requestId,
name
};
categories.push(newCategory);
return newCategory;
}
async function deleteProduct(id) {
const after = products.filter((p) => p.id !== id);
products = after;
}
function initProducts() {
const product = {
id: "p1",
name: "p1-name",
price: 9.99
};
if (products.some((p) => p.id === product.id)) {
return;
}
products.push(product);
}
function getNumber(requestId) {
const num = numbers[requestId];
return num ?? 0;
}
function addNumber(num, requestId) {
const existingNumber = getNumber(requestId);
const sum = existingNumber + num;
numbers[requestId] = sum;
return sum;
}
function multiplyNumber(by, requestId) {
const existingNumber = getNumber(requestId);
const result = existingNumber * by;
numbers[requestId] = result;
return result;
}
function deleteNumber(requestId) {
const num = getNumber(requestId);
if (typeof numbers[requestId] === "number") {
delete numbers[requestId];
}
return num;
}
var a = createSubgraph("a", {
typeDefs: (
/* GraphQL */
`
extend schema
@link(
url: "https://specs.apollo.dev/federation/v2.3"
import: ["@key", "@shareable"]
)
type Mutation {
addProduct(input: AddProductInput!): Product!
addCategory(name: String!, requestId: String!): Category! @shareable
multiply(by: Int!, requestId: String!): Int!
}
type Query {
product(id: ID!): Product!
products: [Product!]!
}
input AddProductInput {
name: String!
price: Float!
}
type Product @key(fields: "id") {
id: ID!
name: String!
price: Float!
}
type Category @key(fields: "id") {
id: ID!
}
`
),
resolvers: {
Query: {
async product(_, { id }) {
initProducts();
const product = (await getProducts()).find((p) => p.id === id);
if (!product) {
return null;
}
return {
id: product.id,
name: product.name,
price: product.price
};
},
async products() {
initProducts();
return getProducts();
}
},
Mutation: {
async multiply(_, args) {
return multiplyNumber(args.by, args.requestId);
},
async addProduct(_, {
input
}) {
return addProduct(input.name, input.price);
},
async addCategory(_, {
name,
requestId
}) {
return addCategory(name, requestId);
}
},
Product: {
async __resolveReference(key) {
const products = await getProducts();
const product = products.find((p) => p.id === key.id);
if (!product) {
return null;
}
await deleteProduct(product.id);
return {
id: product.id,
name: product.name,
price: product.price
};
}
},
Category: {
__resolveReference(key) {
const categories = getCategories();
const category = categories.find((p) => p.id === key.id);
if (!category) {
return null;
}
return {
id: category.id
};
}
}
}
});
var b = createSubgraph("b", {
typeDefs: (
/* GraphQL */
`
extend schema
@link(
url: "https://specs.apollo.dev/federation/v2.3"
import: ["@key", "@external", "@requires", "@shareable"]
)
type Product @key(fields: "id") {
id: ID!
price: Float! @external
isExpensive: Boolean! @requires(fields: "price")
isAvailable: Boolean!
}
type Mutation {
delete(requestId: String!): Int!
addCategory(name: String!, requestId: String!): Category! @shareable
}
type Category @key(fields: "id") {
id: ID!
name: String!
}
`
),
resolvers: {
Mutation: {
delete(_, args) {
return deleteNumber(args.requestId);
},
async addCategory(_, {
name,
requestId
}) {
return addCategory(name, requestId);
}
},
Product: {
async __resolveReference(key) {
const product = (await getProducts()).find(
(product2) => product2.id === key.id
);
if (!product) {
return null;
}
await deleteProduct(product.id);
if (typeof product.price === "number") {
return {
id: product.id,
isAvailable: true,
price: product.price
};
}
return {
id: product.id,
isAvailable: false
};
},
isExpensive(product) {
if (typeof product.price !== "number") {
throw new Error("Price is not available");
}
return product.price > 100;
}
},
Category: {
__resolveReference(key) {
const categories = getCategories();
const category = categories.find((p) => p.id === key.id);
if (!category) {
return null;
}
return {
id: category.id,
name: category.name
};
}
}
}
});
var c = createSubgraph("c", {
typeDefs: (
/* GraphQL */
`
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key"])
type Mutation {
add(num: Int!, requestId: String!): Int!
}
`
),
resolvers: {
Mutation: {
add(_, args) {
return addNumber(args.num, args.requestId);
}
}
}
});
var test = () => {
const randomId = Math.random().toString(16).substr(2);
return [
createTest(
/* GraphQL */
`
mutation {
addProduct(input: { name: "new", price: 599.99 }) {
name
price
isExpensive
isAvailable
}
}
`,
{
data: {
addProduct: {
name: "new",
price: 599.99,
isExpensive: true,
isAvailable: true
}
}
}
),
createTest(
/* GraphQL */
`
query {
product(id: "p1") {
id
name
price
isExpensive
isAvailable
}
}
`,
{
data: {
product: {
id: "p1",
name: "p1-name",
price: 9.99,
isExpensive: false,
isAvailable: true
}
}
}
),
// Test correct order of execution
// It obviously does not solve a problem with shared state and race conditions,
// but at least it reduces the risk a bit
createTest(
/* GraphQL */
`
mutation {
five: add(num: 5, requestId: "${randomId}")
ten: multiply(by: 2, requestId: "${randomId}")
twelve: add(num: 2, requestId: "${randomId}")
final: delete(requestId: "${randomId}")
}
`,
{
data: {
five: 5,
ten: 10,
twelve: 12,
final: 12
}
}
),
// shared-root (mutation)
createTest(
/* GraphQL */
`
mutation {
addCategory(name: "new", requestId: "${randomId}") {
id
name
}
}
`,
{
data: {
addCategory: {
id: "c-added-" + randomId,
name: "new"
}
}
}
)
];
};
var index = serve("mutations", [a, b, c], test);
export { index as default };