@newmo/graphql-fake-server 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/bin.js +7 -7
- package/dist/esm/bin.js.map +1 -1
- package/dist/esm/cli.d.ts +4 -2
- package/dist/esm/cli.d.ts.map +1 -1
- package/dist/esm/cli.js +25 -18
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/createMock.d.ts +15 -0
- package/dist/esm/createMock.d.ts.map +1 -0
- package/dist/esm/createMock.js +39 -0
- package/dist/esm/createMock.js.map +1 -0
- package/dist/esm/index.d.ts +2 -20
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -65
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/server.d.ts +77 -0
- package/dist/esm/server.d.ts.map +1 -0
- package/dist/esm/server.js +262 -0
- package/dist/esm/server.js.map +1 -0
- package/package.json +9 -11
- package/src/bin.ts +7 -7
- package/src/cli.ts +31 -26
- package/src/createMock.ts +47 -0
- package/src/index.ts +2 -87
- package/src/server.ts +348 -0
package/dist/esm/bin.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { run } from "./cli.js";
|
|
3
3
|
const ret = await run();
|
|
4
|
-
if (
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
if (ret.stdout) {
|
|
5
|
+
console.log(ret.stdout);
|
|
6
|
+
}
|
|
7
|
+
if (ret.stderr) {
|
|
8
|
+
console.error(ret.stderr);
|
|
9
|
+
}
|
|
10
|
+
if (!ret.doNotExit) {
|
|
11
11
|
process.exit(ret.exitCode);
|
|
12
12
|
}
|
|
13
13
|
//# sourceMappingURL=bin.js.map
|
package/dist/esm/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,MAAM,GAAG,GAAG,MAAM,GAAG,EAAE,CAAC;AACxB,IAAI,
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,MAAM,GAAG,GAAG,MAAM,GAAG,EAAE,CAAC;AACxB,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;IACb,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AACD,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;IACb,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AACD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;IACjB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC/B,CAAC"}
|
package/dist/esm/cli.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
export declare const cli: {
|
|
3
3
|
values: {
|
|
4
4
|
schema: string | undefined;
|
|
5
|
-
|
|
5
|
+
mainPort: string | undefined;
|
|
6
|
+
apolloPort: string | undefined;
|
|
6
7
|
logLevel: string | undefined;
|
|
7
8
|
};
|
|
8
9
|
positionals: [];
|
|
@@ -11,5 +12,6 @@ export declare const run: ({ values, }?: typeof cli) => Promise<{
|
|
|
11
12
|
stdout: string;
|
|
12
13
|
stderr: string | Error;
|
|
13
14
|
exitCode: number;
|
|
14
|
-
|
|
15
|
+
doNotExit?: boolean;
|
|
16
|
+
}>;
|
|
15
17
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/esm/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";AAgBA,eAAO,MAAM,GAAG;;;;;;;;CAyBd,CAAC;AACH,eAAO,MAAM,GAAG,iBAEb,UAAU,KAAS,QAAQ;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB,CAuDA,CAAC"}
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import * as fs from "node:fs/promises";
|
|
3
2
|
import { parseArgs } from "node:util";
|
|
4
|
-
import {
|
|
5
|
-
import { createMock, startFakeServer } from "./index.js";
|
|
3
|
+
import { createFakeServer } from "./index.js";
|
|
6
4
|
import { createLogger } from "./logger.js";
|
|
7
5
|
const HELP = `
|
|
8
6
|
Usage: npx @newmo/graphql-fake-server --schema <path> [options]
|
|
@@ -24,11 +22,16 @@ export const cli = parseArgs({
|
|
|
24
22
|
description: "Path to the schema file. e.g. schema.graphql",
|
|
25
23
|
},
|
|
26
24
|
// --port
|
|
27
|
-
|
|
25
|
+
mainPort: {
|
|
28
26
|
type: "string",
|
|
29
27
|
description: "Port to run the server on",
|
|
30
28
|
default: "4000",
|
|
31
29
|
},
|
|
30
|
+
apolloPort: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Port to run the server on",
|
|
33
|
+
default: "4002",
|
|
34
|
+
},
|
|
32
35
|
logLevel: {
|
|
33
36
|
type: "string",
|
|
34
37
|
description: "log level: debug, info, warn, error",
|
|
@@ -55,28 +58,32 @@ export const run = async ({ values, } = cli) => {
|
|
|
55
58
|
exitCode: 1,
|
|
56
59
|
};
|
|
57
60
|
}
|
|
58
|
-
const
|
|
59
|
-
|
|
61
|
+
const mainPort = values.mainPort ? Number.parseInt(values.mainPort, 10) : Number.NaN;
|
|
62
|
+
const apolloPort = values.apolloPort ? Number.parseInt(values.apolloPort, 10) : Number.NaN;
|
|
63
|
+
if (Number.isNaN(mainPort) || Number.isNaN(apolloPort)) {
|
|
60
64
|
logger.info(HELP);
|
|
61
65
|
return {
|
|
62
66
|
stdout: "",
|
|
63
|
-
stderr: "
|
|
67
|
+
stderr: "port must be a number",
|
|
64
68
|
exitCode: 1,
|
|
65
69
|
};
|
|
66
70
|
}
|
|
67
71
|
try {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
mockObject,
|
|
75
|
-
port,
|
|
76
|
-
schema,
|
|
72
|
+
const server = await createFakeServer({
|
|
73
|
+
schemaFilePath: schemaPath,
|
|
74
|
+
ports: {
|
|
75
|
+
fakeServer: mainPort,
|
|
76
|
+
apolloServer: apolloPort,
|
|
77
|
+
},
|
|
77
78
|
});
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
const { urls } = await server.start();
|
|
80
|
+
logger.info(`🚀 GraphQL Fake Server listening at: ${urls.fakeServer}`);
|
|
81
|
+
return {
|
|
82
|
+
stdout: "",
|
|
83
|
+
stderr: "",
|
|
84
|
+
exitCode: 0,
|
|
85
|
+
doNotExit: true,
|
|
86
|
+
};
|
|
80
87
|
}
|
|
81
88
|
catch (error) {
|
|
82
89
|
logger.error(error);
|
package/dist/esm/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { GraphQLSchema } from "graphql/index.js";
|
|
2
|
+
import { type LogLevel } from "./logger.js";
|
|
3
|
+
export type MockObject = Record<string, unknown>;
|
|
4
|
+
export type GenerateMockOptions = {
|
|
5
|
+
schema: GraphQLSchema;
|
|
6
|
+
maxFieldRecursionDepth?: number | undefined;
|
|
7
|
+
logLevel?: LogLevel | undefined;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Create mock object from schema
|
|
11
|
+
* It supports @example directive
|
|
12
|
+
* @param options
|
|
13
|
+
*/
|
|
14
|
+
export declare const createMock: (options: GenerateMockOptions) => Promise<MockObject>;
|
|
15
|
+
//# sourceMappingURL=createMock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createMock.d.ts","sourceRoot":"","sources":["../../src/createMock.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,aAAa,CAAC;AAC1D,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACjD,MAAM,MAAM,mBAAmB,GAAG;IAC9B,MAAM,EAAE,aAAa,CAAC;IACtB,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CACnC,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,UAAU,YAAmB,mBAAmB,KAAG,QAAQ,UAAU,CA4BjF,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import vm from "node:vm";
|
|
2
|
+
import { generateCode, getTypeInfos, normalizeConfig } from "@newmo/graphql-fake-core";
|
|
3
|
+
import { createLogger } from "./logger.js";
|
|
4
|
+
const cloneAsJSON = (obj) => {
|
|
5
|
+
return JSON.parse(JSON.stringify(obj));
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Create mock object from schema
|
|
9
|
+
* It supports @example directive
|
|
10
|
+
* @param options
|
|
11
|
+
*/
|
|
12
|
+
export const createMock = async (options) => {
|
|
13
|
+
const logger = createLogger(options.logLevel);
|
|
14
|
+
try {
|
|
15
|
+
const normalizedConfig = normalizeConfig({
|
|
16
|
+
maxFieldRecursionDepth: options.maxFieldRecursionDepth ?? 3,
|
|
17
|
+
});
|
|
18
|
+
const typeInfos = getTypeInfos(normalizedConfig, options.schema);
|
|
19
|
+
const code = generateCode({
|
|
20
|
+
...normalizedConfig,
|
|
21
|
+
outputType: "commonjs",
|
|
22
|
+
}, typeInfos);
|
|
23
|
+
logger.debug("Generated code:");
|
|
24
|
+
logger.debug(code);
|
|
25
|
+
// execute code in vm and get all exports
|
|
26
|
+
const exports = {};
|
|
27
|
+
vm.runInNewContext(code, { exports });
|
|
28
|
+
// Apollo Server does not support Function type in mock object
|
|
29
|
+
const plainObject = cloneAsJSON(exports);
|
|
30
|
+
logger.debug("Exports:");
|
|
31
|
+
logger.debug(JSON.stringify(plainObject, null, 2));
|
|
32
|
+
return plainObject;
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
logger.error(error);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=createMock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createMock.js","sourceRoot":"","sources":["../../src/createMock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEvF,OAAO,EAAiB,YAAY,EAAE,MAAM,aAAa,CAAC;AAO1D,MAAM,WAAW,GAAG,CAAC,GAAY,EAAE,EAAE;IACjC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3C,CAAC,CAAC;AACF;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,OAA4B,EAAuB,EAAE;IAClF,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9C,IAAI,CAAC;QACD,MAAM,gBAAgB,GAAG,eAAe,CAAC;YACrC,sBAAsB,EAAE,OAAO,CAAC,sBAAsB,IAAI,CAAC;SAC9D,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACjE,MAAM,IAAI,GAAG,YAAY,CACrB;YACI,GAAG,gBAAgB;YACnB,UAAU,EAAE,UAAU;SACzB,EACD,SAAS,CACZ,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnB,yCAAyC;QACzC,MAAM,OAAO,GAAG,EAAE,CAAC;QACnB,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACtC,8DAA8D;QAC9D,MAAM,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACzB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACnD,OAAO,WAAW,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACL,CAAC,CAAC"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type MockObject = Record<string, unknown>;
|
|
4
|
-
export type StartFakeServerOptions = {
|
|
5
|
-
schema: GraphQLSchema;
|
|
6
|
-
mockObject: MockObject;
|
|
7
|
-
port?: number;
|
|
8
|
-
logLevel?: LogLevel;
|
|
9
|
-
};
|
|
10
|
-
export declare const startFakeServer: ({ schema, mockObject, port, logLevel, }: StartFakeServerOptions) => Promise<() => void>;
|
|
11
|
-
export type GenerateMockOptions = {
|
|
12
|
-
schema: GraphQLSchema;
|
|
13
|
-
logLevel?: LogLevel;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Create mock object from schema
|
|
17
|
-
* It supports @example directive
|
|
18
|
-
* @param options
|
|
19
|
-
*/
|
|
20
|
-
export declare const createMock: (options: GenerateMockOptions) => Promise<MockObject>;
|
|
1
|
+
export { createFakeServer } from "./server.js";
|
|
2
|
+
export type { CreateFakeServerOptions } from "./server.js";
|
|
21
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,66 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import { ApolloServer } from "@apollo/server";
|
|
3
|
-
import { startStandaloneServer } from "@apollo/server/standalone";
|
|
4
|
-
import { addMocksToSchema } from "@graphql-tools/mock";
|
|
5
|
-
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
6
|
-
import { generateCode, getTypeInfos, normalizeConfig } from "@newmo/graphql-fake-core";
|
|
7
|
-
//@ts-expect-error
|
|
8
|
-
import depthLimit from "graphql-depth-limit";
|
|
9
|
-
import { createLogger } from "./logger.js";
|
|
10
|
-
export const startFakeServer = async ({ schema, mockObject, port, logLevel, }) => {
|
|
11
|
-
const logger = createLogger(logLevel);
|
|
12
|
-
const mocks = Object.fromEntries(Object.entries(mockObject).map(([key, value]) => {
|
|
13
|
-
return [key, () => value];
|
|
14
|
-
}));
|
|
15
|
-
const server = new ApolloServer({
|
|
16
|
-
schema: addMocksToSchema({
|
|
17
|
-
schema: makeExecutableSchema({
|
|
18
|
-
typeDefs: schema,
|
|
19
|
-
}),
|
|
20
|
-
mocks,
|
|
21
|
-
}),
|
|
22
|
-
validationRules: [depthLimit(3)],
|
|
23
|
-
});
|
|
24
|
-
const { url } = await startStandaloneServer(server, { listen: { port: port } });
|
|
25
|
-
logger.info(`🚀 Server listening at: ${url}`);
|
|
26
|
-
return () => {
|
|
27
|
-
// close
|
|
28
|
-
server.stop();
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
const cloneAsJSON = (obj) => {
|
|
32
|
-
return JSON.parse(JSON.stringify(obj));
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* Create mock object from schema
|
|
36
|
-
* It supports @example directive
|
|
37
|
-
* @param options
|
|
38
|
-
*/
|
|
39
|
-
export const createMock = async (options) => {
|
|
40
|
-
const logger = createLogger(options.logLevel);
|
|
41
|
-
try {
|
|
42
|
-
const normalizedConfig = normalizeConfig({
|
|
43
|
-
maxFieldRecursionDepth: 4,
|
|
44
|
-
});
|
|
45
|
-
const typeInfos = getTypeInfos(normalizedConfig, options.schema);
|
|
46
|
-
const code = generateCode({
|
|
47
|
-
...normalizedConfig,
|
|
48
|
-
outputType: "commonjs",
|
|
49
|
-
}, typeInfos);
|
|
50
|
-
logger.debug("Generated code:");
|
|
51
|
-
logger.debug(code);
|
|
52
|
-
// execute code in vm and get all exports
|
|
53
|
-
const exports = {};
|
|
54
|
-
vm.runInNewContext(code, { exports });
|
|
55
|
-
// Apollo Server does not support Function type in mock object
|
|
56
|
-
const plainObject = cloneAsJSON(exports);
|
|
57
|
-
logger.debug("Exports:");
|
|
58
|
-
logger.debug(JSON.stringify(plainObject, null, 2));
|
|
59
|
-
return plainObject;
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
logger.error(error);
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
1
|
+
export { createFakeServer } from "./server.js";
|
|
66
2
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { GraphQLSchema } from "graphql/index.js";
|
|
2
|
+
import { type MockObject } from "./createMock.js";
|
|
3
|
+
import { type LogLevel } from "./logger.js";
|
|
4
|
+
export type CreateFakeServerOptions = {
|
|
5
|
+
schemaFilePath: string;
|
|
6
|
+
ports?: {
|
|
7
|
+
fakeServer: number;
|
|
8
|
+
apolloServer: number;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* maxDepth for depthLimit
|
|
12
|
+
* Default is 3
|
|
13
|
+
*/
|
|
14
|
+
maxDepth?: number;
|
|
15
|
+
/**
|
|
16
|
+
* maxFieldRecursionDepth for Mocking
|
|
17
|
+
* Default is maxDepth + 1
|
|
18
|
+
*/
|
|
19
|
+
maxFieldRecursionDepth?: number;
|
|
20
|
+
/**
|
|
21
|
+
* max number of registered sequences
|
|
22
|
+
* Default is 100
|
|
23
|
+
* If the number of registered sequences exceeds this number, the oldest sequence is deleted.
|
|
24
|
+
*/
|
|
25
|
+
maxRegisteredSequences?: number;
|
|
26
|
+
logLevel?: LogLevel;
|
|
27
|
+
};
|
|
28
|
+
type FakeServerInternal = {
|
|
29
|
+
schema: GraphQLSchema;
|
|
30
|
+
mockObject: MockObject;
|
|
31
|
+
ports: {
|
|
32
|
+
fakeServer: number;
|
|
33
|
+
apolloServer: number;
|
|
34
|
+
};
|
|
35
|
+
maxDepth: number;
|
|
36
|
+
maxFieldRecursionDepth: number;
|
|
37
|
+
maxRegisteredSequences: number;
|
|
38
|
+
logLevel: LogLevel;
|
|
39
|
+
};
|
|
40
|
+
export type RegisterSequenceNetworkError = {
|
|
41
|
+
type: "network-error";
|
|
42
|
+
operationName: string;
|
|
43
|
+
responseStatusCode: number;
|
|
44
|
+
errors: Record<string, unknown>[];
|
|
45
|
+
};
|
|
46
|
+
export type RegisterSequenceOperation = {
|
|
47
|
+
type: "operation";
|
|
48
|
+
operationName: string;
|
|
49
|
+
data: Record<string, unknown>;
|
|
50
|
+
};
|
|
51
|
+
export type RegisterSequenceOptions = RegisterSequenceNetworkError | RegisterSequenceOperation;
|
|
52
|
+
export type RegisterOperationResponse = {
|
|
53
|
+
ok: true;
|
|
54
|
+
} | {
|
|
55
|
+
ok: false;
|
|
56
|
+
errors: string[];
|
|
57
|
+
};
|
|
58
|
+
export declare const createFakeServer: (options: CreateFakeServerOptions) => Promise<{
|
|
59
|
+
start: () => Promise<{
|
|
60
|
+
urls: {
|
|
61
|
+
fakeServer: string;
|
|
62
|
+
apolloServer: string;
|
|
63
|
+
};
|
|
64
|
+
}>;
|
|
65
|
+
stop: () => void;
|
|
66
|
+
}>;
|
|
67
|
+
export declare const createFakeServerInternal: (options: FakeServerInternal) => Promise<{
|
|
68
|
+
start: () => Promise<{
|
|
69
|
+
urls: {
|
|
70
|
+
fakeServer: string;
|
|
71
|
+
apolloServer: string;
|
|
72
|
+
};
|
|
73
|
+
}>;
|
|
74
|
+
stop: () => void;
|
|
75
|
+
}>;
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,aAAa,CAAC;AAE1D,MAAM,MAAM,uBAAuB,GAAG;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE;QACJ,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACxB,CAAC;IACF;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACvB,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACtB,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE;QACH,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,QAAQ,EAAE,QAAQ,CAAC;CACtB,CAAC;AAiBF,MAAM,MAAM,4BAA4B,GAAG;IACvC,IAAI,EAAE,eAAe,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CACrC,CAAC;AACF,MAAM,MAAM,yBAAyB,GAAG;IACpC,IAAI,EAAE,WAAW,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC,CAAC;AACF,MAAM,MAAM,uBAAuB,GAAG,4BAA4B,GAAG,yBAAyB,CAAC;AAC/F,MAAM,MAAM,yBAAyB,GAC/B;IACI,EAAE,EAAE,IAAI,CAAC;CACZ,GACD;IACI,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AA+MR,eAAO,MAAM,gBAAgB,YAAmB,uBAAuB;;;;;;;;EAuBtE,CAAC;AAEF,eAAO,MAAM,wBAAwB,YAAmB,kBAAkB;;;;;;;;EA6BzE,CAAC"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { ApolloServer } from "@apollo/server";
|
|
3
|
+
import { startStandaloneServer } from "@apollo/server/standalone";
|
|
4
|
+
import { addMocksToSchema } from "@graphql-tools/mock";
|
|
5
|
+
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
6
|
+
import { serve } from "@hono/node-server";
|
|
7
|
+
//@ts-expect-error
|
|
8
|
+
import depthLimit from "graphql-depth-limit";
|
|
9
|
+
import { buildSchema } from "graphql/utilities/index.js";
|
|
10
|
+
import { Hono } from "hono";
|
|
11
|
+
import { createMock } from "./createMock.js";
|
|
12
|
+
import { createLogger } from "./logger.js";
|
|
13
|
+
const creteApolloServer = async (options) => {
|
|
14
|
+
const mocks = Object.fromEntries(Object.entries(options.mockObject).map(([key, value]) => {
|
|
15
|
+
return [key, () => value];
|
|
16
|
+
}));
|
|
17
|
+
return new ApolloServer({
|
|
18
|
+
schema: addMocksToSchema({
|
|
19
|
+
schema: makeExecutableSchema({
|
|
20
|
+
typeDefs: options.schema,
|
|
21
|
+
}),
|
|
22
|
+
mocks,
|
|
23
|
+
}),
|
|
24
|
+
validationRules: [depthLimit(options.maxDepth)],
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
const validateSequenceRegistration = (data) => {
|
|
28
|
+
if (typeof data !== "object" || data === null)
|
|
29
|
+
return false;
|
|
30
|
+
if ("type" in data && typeof data.type === "string") {
|
|
31
|
+
if (data.type === "network-error") {
|
|
32
|
+
return ("errors" in data &&
|
|
33
|
+
Array.isArray(data.errors) &&
|
|
34
|
+
"responseStatusCode" in data &&
|
|
35
|
+
typeof data.responseStatusCode === "number" &&
|
|
36
|
+
"operationName" in data &&
|
|
37
|
+
typeof data.operationName === "string");
|
|
38
|
+
}
|
|
39
|
+
if (data.type === "operation") {
|
|
40
|
+
return ("data" in data &&
|
|
41
|
+
typeof data.data === "object" &&
|
|
42
|
+
"operationName" in data &&
|
|
43
|
+
typeof data.operationName === "string");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
};
|
|
48
|
+
class LRUMap {
|
|
49
|
+
map = new Map();
|
|
50
|
+
keys = [];
|
|
51
|
+
maxSize;
|
|
52
|
+
constructor({ maxSize }) {
|
|
53
|
+
this.maxSize = maxSize;
|
|
54
|
+
}
|
|
55
|
+
set(key, value) {
|
|
56
|
+
this.map.set(key, value);
|
|
57
|
+
this.keys.push(key);
|
|
58
|
+
if (this.keys.length > this.maxSize) {
|
|
59
|
+
const oldestKey = this.keys.shift();
|
|
60
|
+
if (oldestKey) {
|
|
61
|
+
this.map.delete(oldestKey);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
get(key) {
|
|
66
|
+
return this.map.get(key);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const createRoutingServer = async ({ logLevel, ports, maxRegisteredSequences, }) => {
|
|
70
|
+
const logger = createLogger(logLevel);
|
|
71
|
+
// pass through to apollo server
|
|
72
|
+
const passToApollo = async (c) => {
|
|
73
|
+
// remove prefix
|
|
74
|
+
// prefix = /app1/*, path = /app1/a/b
|
|
75
|
+
// => suffix_path = /a/b
|
|
76
|
+
// let path = new URL(c.req.raw.url).pathname
|
|
77
|
+
let path = c.req.path;
|
|
78
|
+
path = path.replace(new RegExp(`^${c.req.routePath.replace("*", "")}`), "/");
|
|
79
|
+
let url = `http://127.0.0.1:${ports.apolloServer}${path}`;
|
|
80
|
+
// add params to URL
|
|
81
|
+
if (c.req.query())
|
|
82
|
+
url = `${url}?${new URLSearchParams(c.req.query())}`;
|
|
83
|
+
// request
|
|
84
|
+
const rep = await fetch(url, {
|
|
85
|
+
method: c.req.method,
|
|
86
|
+
headers: c.req.raw.headers,
|
|
87
|
+
body: c.req.raw.body,
|
|
88
|
+
duplex: "half",
|
|
89
|
+
});
|
|
90
|
+
// log response with pipe
|
|
91
|
+
if (rep.status === 101)
|
|
92
|
+
return rep;
|
|
93
|
+
return new Response(rep.body, rep);
|
|
94
|
+
};
|
|
95
|
+
const sequenceLruMap = new LRUMap({
|
|
96
|
+
maxSize: maxRegisteredSequences,
|
|
97
|
+
});
|
|
98
|
+
const app = new Hono();
|
|
99
|
+
app.post("/fake", async (c) => {
|
|
100
|
+
logger.debug("/fake");
|
|
101
|
+
const sequenceId = c.req.header("sequence-id");
|
|
102
|
+
if (!sequenceId) {
|
|
103
|
+
return Response.json(JSON.stringify({ ok: false, errors: ["sequence-id is required"] }), {
|
|
104
|
+
status: 400,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const body = await c.req.json();
|
|
108
|
+
logger.debug("/fake: got fake body", {
|
|
109
|
+
sequenceId,
|
|
110
|
+
body,
|
|
111
|
+
});
|
|
112
|
+
if (!validateSequenceRegistration(body)) {
|
|
113
|
+
return Response.json(JSON.stringify({ ok: false, errors: ["invalid fake body"] }), {
|
|
114
|
+
status: 400,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
logger.debug("/fake got body type", {
|
|
118
|
+
sequenceId,
|
|
119
|
+
type: body.type,
|
|
120
|
+
});
|
|
121
|
+
sequenceLruMap.set(sequenceId, body);
|
|
122
|
+
return Response.json(JSON.stringify({ ok: true }), {
|
|
123
|
+
status: 200,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
const fakeGraphQLQuery = async (c) => {
|
|
127
|
+
/**
|
|
128
|
+
* Steps:
|
|
129
|
+
* 1. Receive a request for a GraphQL query
|
|
130
|
+
* 2. Does it contain a sequence id?
|
|
131
|
+
* - if Yes: type is network error → return an error
|
|
132
|
+
* - if No: Pass through to Apollo Server -> exit
|
|
133
|
+
* 3. Send a request to Apollo Server
|
|
134
|
+
* 4. Merge the registration data with the response from 3
|
|
135
|
+
* 5. Return the merged data
|
|
136
|
+
*/
|
|
137
|
+
const sequenceId = c.req.header("sequence-id");
|
|
138
|
+
// 2. Does it contain a sequence id?
|
|
139
|
+
if (!sequenceId)
|
|
140
|
+
return passToApollo(c);
|
|
141
|
+
const sequence = sequenceLruMap.get(sequenceId);
|
|
142
|
+
logger.debug(`/query: sequence-id: ${sequenceId}, sequence exists: ${Boolean(sequence)}`, {
|
|
143
|
+
sequence,
|
|
144
|
+
sequenceId,
|
|
145
|
+
});
|
|
146
|
+
if (!sequence)
|
|
147
|
+
return passToApollo(c);
|
|
148
|
+
const requestBody = await c.req.raw.clone().json();
|
|
149
|
+
const requestOperationName = typeof requestBody === "object" &&
|
|
150
|
+
requestBody !== null &&
|
|
151
|
+
"operationName" in requestBody &&
|
|
152
|
+
requestBody.operationName;
|
|
153
|
+
logger.debug(`operationName: ${requestOperationName} sequenceId: ${sequenceId}`, {
|
|
154
|
+
sequenceId,
|
|
155
|
+
});
|
|
156
|
+
if (requestOperationName !== sequence.operationName) {
|
|
157
|
+
return Response.json(JSON.stringify({
|
|
158
|
+
errors: [
|
|
159
|
+
`operationName does not match. operationName: ${requestOperationName} sequenceId: ${sequenceId}`,
|
|
160
|
+
],
|
|
161
|
+
}), {
|
|
162
|
+
status: 400,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (sequence.type === "network-error") {
|
|
166
|
+
return new Response(JSON.stringify(sequence.errors), {
|
|
167
|
+
status: sequence.responseStatusCode,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// 3. Send a request to Apollo Server
|
|
171
|
+
logger.debug("request to apollo-server", {
|
|
172
|
+
sequenceId,
|
|
173
|
+
});
|
|
174
|
+
const rep = await fetch(`http://127.0.0.1:${ports.apolloServer}/graphql`, {
|
|
175
|
+
method: c.req.method,
|
|
176
|
+
headers: c.req.raw.headers,
|
|
177
|
+
body: c.req.raw.body,
|
|
178
|
+
duplex: "half",
|
|
179
|
+
});
|
|
180
|
+
logger.debug("/query: response from apollo-server", {
|
|
181
|
+
sequenceId,
|
|
182
|
+
rep,
|
|
183
|
+
});
|
|
184
|
+
if (rep.status === 101)
|
|
185
|
+
return rep;
|
|
186
|
+
// 4. Does the request contain a sequence id?
|
|
187
|
+
const responseBody = await rep.json();
|
|
188
|
+
// 5. Merge the registration data with the response from 2
|
|
189
|
+
const data = sequence.data;
|
|
190
|
+
logger.debug(`/query: merge sequence-id: ${sequenceId}`, {
|
|
191
|
+
data,
|
|
192
|
+
responseBody,
|
|
193
|
+
});
|
|
194
|
+
const merged = {
|
|
195
|
+
//@ts-expect-error
|
|
196
|
+
...responseBody.data,
|
|
197
|
+
...data,
|
|
198
|
+
};
|
|
199
|
+
return Response.json({
|
|
200
|
+
data: merged,
|
|
201
|
+
}, rep);
|
|
202
|
+
};
|
|
203
|
+
app.use("/graphql", fakeGraphQLQuery);
|
|
204
|
+
app.use("/query", fakeGraphQLQuery);
|
|
205
|
+
app.all("*", passToApollo);
|
|
206
|
+
return app;
|
|
207
|
+
};
|
|
208
|
+
export const createFakeServer = async (options) => {
|
|
209
|
+
const schema = buildSchema(await fs.readFile(options.schemaFilePath, "utf-8"));
|
|
210
|
+
const mockObject = await createMock({
|
|
211
|
+
schema,
|
|
212
|
+
logLevel: options.logLevel,
|
|
213
|
+
maxFieldRecursionDepth: options.maxFieldRecursionDepth,
|
|
214
|
+
});
|
|
215
|
+
const ports = {
|
|
216
|
+
fakeServer: options.ports?.fakeServer ?? 4000,
|
|
217
|
+
apolloServer: options.ports?.apolloServer ?? 4001,
|
|
218
|
+
};
|
|
219
|
+
const maxDepth = options.maxDepth ?? 3;
|
|
220
|
+
const maxFieldRecursionDepth = options.maxFieldRecursionDepth ?? maxDepth + 1;
|
|
221
|
+
const maxRegisteredSequences = options.maxRegisteredSequences ?? 100;
|
|
222
|
+
return createFakeServerInternal({
|
|
223
|
+
ports,
|
|
224
|
+
schema,
|
|
225
|
+
mockObject,
|
|
226
|
+
maxDepth,
|
|
227
|
+
maxFieldRecursionDepth,
|
|
228
|
+
maxRegisteredSequences,
|
|
229
|
+
logLevel: options.logLevel ?? "info",
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
export const createFakeServerInternal = async (options) => {
|
|
233
|
+
const apolloServer = await creteApolloServer(options);
|
|
234
|
+
const routingServer = await createRoutingServer({
|
|
235
|
+
logLevel: options.logLevel,
|
|
236
|
+
ports: options.ports,
|
|
237
|
+
maxRegisteredSequences: options.maxRegisteredSequences,
|
|
238
|
+
});
|
|
239
|
+
let routerServer = null;
|
|
240
|
+
return {
|
|
241
|
+
start: async () => {
|
|
242
|
+
const { url } = await startStandaloneServer(apolloServer, {
|
|
243
|
+
listen: { port: options.ports.apolloServer },
|
|
244
|
+
});
|
|
245
|
+
routerServer = serve({
|
|
246
|
+
fetch: routingServer.fetch,
|
|
247
|
+
port: options.ports.fakeServer,
|
|
248
|
+
});
|
|
249
|
+
return {
|
|
250
|
+
urls: {
|
|
251
|
+
fakeServer: `http://127.0.0.1:${options.ports.fakeServer}`,
|
|
252
|
+
apolloServer: `http://127.0.0.1:${options.ports.apolloServer}`,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
},
|
|
256
|
+
stop: () => {
|
|
257
|
+
apolloServer.stop();
|
|
258
|
+
routerServer?.close();
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
//# sourceMappingURL=server.js.map
|