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

#231 into release@5.0.0 🧊 add playground server #233

Open
wants to merge 1 commit into
base: release@5.0.0
Choose a base branch
from
Open
Changes from all commits
Commits
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 .swcrc
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
"baseUrl": "./",
"paths": {
"@/server": ["./src/server"],
"@/core/database": ["./src/core/database"],
"@/core/playground": ["./src/core/playground"],
"@/core/rest": ["./src/core/rest"],
"@/core/graphql": ["./src/core/graphql"],
"@/core/middlewares": ["./src/core/middlewares"],
45 changes: 43 additions & 2 deletions bin/cli.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import color from 'ansi-colors';
import fs from 'node:fs';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';

import type { MockServerConfigArgv } from '../src';
import { playgroundDataSchema } from '@/utils/validate';

import type {
MockServerConfigArgv,
MockServerConfigInitArgv,
MockServerConfigPlaygroundArgv
} from '../src';

import { build } from './build';
import { init } from './init';
import { playground } from './playground';

const initOptions = {
baseUrl: {
@@ -27,8 +36,40 @@ const initOptions = {
export const cli = () => {
const processArgv = hideBin(process.argv);

if (processArgv.includes('playground')) {
const argv = yargs(processArgv)
.command('playground <data>', 'Run playground server', (yargs) =>
yargs.positional('data', {
describe: 'Path to json file',
type: 'string',
required: true
})
)
.strict()
.parse() as MockServerConfigPlaygroundArgv;

if (!fs.existsSync(argv.data)) {
console.error(`${color.red('Error')}: File ${argv.data} does not exist`);
process.exit(1);
}

const dataContent = fs.readFileSync(argv.data, 'utf-8');
try {
const { error } = playgroundDataSchema.safeParse(JSON.parse(dataContent));

if (error) throw new Error(`Data file should be a valid object`);
} catch (error) {
console.error(
`${color.red('Error')}: ${error instanceof Error ? error.message : 'Unknown error'}`
);
process.exit(1);
}

return playground(argv);
}

if (processArgv.includes('init')) {
const argv = yargs(processArgv).options(initOptions).parse() as MockServerConfigArgv;
const argv = yargs(processArgv).options(initOptions).parse() as MockServerConfigInitArgv;

return init(argv);
}
4 changes: 2 additions & 2 deletions bin/helpers/createTemplate.ts
Original file line number Diff line number Diff line change
@@ -8,11 +8,11 @@ interface CreateTemplateOptions {
baseUrl: string;
port: number;
staticPath: string;
withTypescript: boolean;
ts: boolean;
}

export const createTemplate = (options: CreateTemplateOptions) => {
const language = options.withTypescript ? 'ts' : 'js';
const language = options.ts ? 'ts' : 'js';
const templatePath = path.join(__dirname, '..', `templates/${language}/${options.apiType}`);

fs.cpSync(`${templatePath}/mock-requests`, `${APP_PATH}/mock-requests`, {
6 changes: 3 additions & 3 deletions bin/init.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import color from 'ansi-colors';
import prompts from 'prompts';

import type { MockServerConfigArgv } from '@/utils/types';
import type { MockServerConfigInitArgv } from '@/utils/types';

import { baseUrlSchema, portSchema, staticPathSchema } from '@/utils/validate';

import { createTemplate } from './helpers';

export const init = async (argv: MockServerConfigArgv) => {
export const init = async (argv: MockServerConfigInitArgv) => {
try {
const response = await prompts(
[
{
name: 'withTypescript',
name: 'ts',
type: 'toggle',
message: 'Would you like to use TypeScript?',
initial: true,
17 changes: 17 additions & 0 deletions bin/playground.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { startPlaygroundServer } from 'src';

import type { DatabaseMockServerConfig, MockServerConfigPlaygroundArgv } from '@/utils/types';

export const playground = async (argv: MockServerConfigPlaygroundArgv) => {
try {
const mergedPlaygroundServerConfig = {
...(argv.baseUrl && { baseUrl: argv.baseUrl }),
...(argv.port && { port: argv.port }),
data: argv.data
} as DatabaseMockServerConfig;

return startPlaygroundServer(mergedPlaygroundServerConfig);
} catch (error: any) {
console.error(error.message);
}
};
9 changes: 9 additions & 0 deletions data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"users": [
{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com"
}
]
}
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import type {
MockServerConfig
} from '@/utils/types';

import { createOrm, createStorage } from '@/core/database';
import { createOrm, createStorage } from '@/core/playground';
import { getGraphQLInput, parseQuery } from '@/utils/helpers';

declare global {
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import request from 'supertest';

import type { DatabaseConfig, MockServerConfig } from '@/utils/types';

import { createDatabaseRoutes } from '@/core/database';
import { createDatabaseRoutes } from '@/core/playground';
import { createTmpDir } from '@/utils/helpers/tests';

import { findIndexById } from './helpers';
Original file line number Diff line number Diff line change
@@ -15,6 +15,8 @@ const isVariableJsonFile = (variable: unknown): variable is `${string}.json` =>
typeof variable === 'string' && variable.endsWith('.json');

export const createDatabaseRoutes = (router: IRouter, { data, routes }: DatabaseConfig) => {
console.log(data, routes);

if (routes) {
const storage = createStorage(routes);
createRewrittenDatabaseRoutes(router, storage.read());
File renamed without changes.
File renamed without changes.
59 changes: 0 additions & 59 deletions src/server/createDatabaseMockServer/createDatabaseMockServer.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/server/createFlatMockServer/createFlatMockServer.ts
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@ import type {
RestRequestConfig
} from '@/utils/types';

import { createDatabaseRoutes } from '@/core/database';
import { createGraphQLRoutes } from '@/core/graphql';
import {
contextMiddleware,
@@ -23,6 +22,7 @@ import {
requestInterceptorMiddleware,
staticMiddleware
} from '@/core/middlewares';
import { createDatabaseRoutes } from '@/core/playground';
import { createRestRoutes } from '@/core/rest';
import { validateFlatMockServerConfig } from '@/utils/validate';

Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@ import express from 'express';

import type { GraphQLMockServerConfig } from '@/utils/types';

import { createDatabaseRoutes } from '@/core/database';
import { createGraphQLRoutes } from '@/core/graphql';
import {
contextMiddleware,
@@ -16,6 +15,7 @@ import {
requestInterceptorMiddleware,
staticMiddleware
} from '@/core/middlewares';
import { createDatabaseRoutes } from '@/core/playground';
import { validateApiMockServerConfig } from '@/utils/validate';

export const createGraphQLMockServer = (
2 changes: 1 addition & 1 deletion src/server/createMockServer/createMockServer.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@ import express from 'express';

import type { MockServerConfig } from '@/utils/types';

import { createDatabaseRoutes } from '@/core/database';
import { createGraphQLRoutes } from '@/core/graphql';
import {
contextMiddleware,
@@ -16,6 +15,7 @@ import {
requestInterceptorMiddleware,
staticMiddleware
} from '@/core/middlewares';
import { createDatabaseRoutes } from '@/core/playground';
import { createRestRoutes } from '@/core/rest';
import { urlJoin } from '@/utils/helpers';
import { validateMockServerConfig } from '@/utils/validate';
36 changes: 36 additions & 0 deletions src/server/createPlaygroundServer/createPlaygroundServer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { Express } from 'express';

import bodyParser from 'body-parser';
import express from 'express';

import type { DatabaseMockServerConfig } from '@/utils/types';

import { cookieParseMiddleware, errorMiddleware, noCorsMiddleware } from '@/core/middlewares';
import { createDatabaseRoutes } from '@/core/playground';

export const createPlaygroundServer = (
databaseMockServerConfig: Omit<DatabaseMockServerConfig, 'port'>,
server: Express = express()
) => {
const { data, routes } = databaseMockServerConfig;

server.use(bodyParser.urlencoded({ extended: false }));

server.use(bodyParser.json({ limit: '10mb' }));
server.set('json spaces', 2);

server.use(bodyParser.text());

cookieParseMiddleware(server);

const baseUrl = databaseMockServerConfig.baseUrl ?? '/';

noCorsMiddleware(server);

const routerWithDatabaseRoutes = createDatabaseRoutes(express.Router(), { data, routes });
server.use(baseUrl, routerWithDatabaseRoutes);

errorMiddleware(server);

return server;
};
2 changes: 1 addition & 1 deletion src/server/createRestMockServer/createRestMockServer.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@ import express from 'express';

import type { RestMockServerConfig } from '@/utils/types';

import { createDatabaseRoutes } from '@/core/database';
import {
contextMiddleware,
cookieParseMiddleware,
@@ -15,6 +14,7 @@ import {
requestInterceptorMiddleware,
staticMiddleware
} from '@/core/middlewares';
import { createDatabaseRoutes } from '@/core/playground';
import { createRestRoutes } from '@/core/rest';
import { validateApiMockServerConfig } from '@/utils/validate';

4 changes: 2 additions & 2 deletions src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export * from './createDatabaseMockServer/createDatabaseMockServer';
export * from './createFlatMockServer/createFlatMockServer';
export * from './createGraphQLMockServer/createGraphQLMockServer';
export * from './createMockServer/createMockServer';
export * from './createPlaygroundServer/createPlaygroundServer';
export * from './createRestMockServer/createRestMockServer';
export * from './startDatabaseMockServer/startDatabaseMockServer';
export * from './startFlatMockServer/startFlatMockServer';
export * from './startGraphQLMockServer/startGraphQLMockServer';
export * from './startMockServer/startMockServer';
export * from './startPlaygroundServer/startPlaygroundServer';
export * from './startRestMockServer/startRestMockServer';
Original file line number Diff line number Diff line change
@@ -5,14 +5,14 @@ import type { DatabaseMockServerConfig } from '@/utils/types';
import { destroyerMiddleware } from '@/core/middlewares';
import { DEFAULT } from '@/utils/constants';

import { createDatabaseMockServer } from '../createDatabaseMockServer/createDatabaseMockServer';
import { createPlaygroundServer } from '../createPlaygroundServer/createPlaygroundServer';

export const startDatabaseMockServer = (databaseMockServerConfig: DatabaseMockServerConfig) => {
const mockServer = createDatabaseMockServer(databaseMockServerConfig);
export const startPlaygroundServer = (databaseMockServerConfig: DatabaseMockServerConfig) => {
const mockServer = createPlaygroundServer(databaseMockServerConfig);
const port = databaseMockServerConfig.port ?? DEFAULT.PORT;

const server = mockServer.listen(port, () => {
console.info(color.green(`🎉 Database Mock Server is running at http://localhost:${port}`));
console.info(color.green(`🎉 Playground server is running at http://localhost:${port}`));
});

// ✅ important: add destroy method for closing keep-alive connections after server shutdown
16 changes: 15 additions & 1 deletion src/utils/types/server.ts
Original file line number Diff line number Diff line change
@@ -67,8 +67,10 @@ export interface GraphQLMockServerConfig extends BaseMockServerConfig {
database?: DatabaseConfig;
}

export interface DatabaseMockServerConfig extends BaseMockServerConfig {
export interface DatabaseMockServerConfig {
baseUrl?: BaseUrl;
data: `${string}.json` | Record<string, unknown>;
port?: Port;
routes?: `${string}.json` | Record<`/${string}`, `/${string}`>;
}

@@ -80,6 +82,18 @@ export type MockServerConfigArgv = Arguments<{
watch?: boolean;
}>;

export type MockServerConfigInitArgv = Arguments<{
baseUrl?: string;
port?: number;
staticPath?: string;
}>;

export type MockServerConfigPlaygroundArgv = Arguments<{
baseUrl?: string;
port?: number;
data: `${string}.json`;
}>;

declare global {
namespace Express {
interface Request {
2 changes: 1 addition & 1 deletion src/utils/validate/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export * from './baseUrlSchema/baseUrlSchema';
export * from './corsSchema/corsSchema';
export * from './databaseConfigSchema/databaseConfigSchema';
export * from './getMostSpecificPathFromError';
export * from './getValidationMessageFromPath';
export * from './graphqlConfigSchema/graphqlConfigSchema';
export * from './interceptorsSchema/interceptorsSchema';
export * from './playgroundConfigSchema/playgroundConfigSchema';
export * from './portSchema/portSchema';
export * from './restConfigSchema/restConfigSchema';
export * from './staticPathSchema/staticPathSchema';
Original file line number Diff line number Diff line change
@@ -2,8 +2,13 @@ import { z } from 'zod';

import { plainObjectSchema, stringForwardSlashSchema, stringJsonFilenameSchema } from '../utils';

export const databaseConfigSchema = z.strictObject({
data: z.union([plainObjectSchema(z.record(z.unknown())), stringJsonFilenameSchema]),
export const playgroundDataSchema = z.union([
plainObjectSchema(z.record(z.unknown())),
stringJsonFilenameSchema
]);

export const playgroundConfigSchema = z.strictObject({
data: playgroundDataSchema,
routes: z
.union([
plainObjectSchema(z.record(stringForwardSlashSchema, stringForwardSlashSchema)),
4 changes: 2 additions & 2 deletions src/utils/validate/validateApiMockServerConfig.ts
Original file line number Diff line number Diff line change
@@ -4,11 +4,11 @@ import type { PlainObject } from '@/utils/types';

import { baseUrlSchema } from './baseUrlSchema/baseUrlSchema';
import { corsSchema } from './corsSchema/corsSchema';
import { databaseConfigSchema } from './databaseConfigSchema/databaseConfigSchema';
import { getMostSpecificPathFromError } from './getMostSpecificPathFromError';
import { getValidationMessageFromPath } from './getValidationMessageFromPath';
import { graphqlConfigSchema } from './graphqlConfigSchema/graphqlConfigSchema';
import { interceptorsSchema } from './interceptorsSchema/interceptorsSchema';
import { playgroundConfigSchema } from './playgroundConfigSchema/playgroundConfigSchema';
import { portSchema } from './portSchema/portSchema';
import { restConfigSchema } from './restConfigSchema/restConfigSchema';
import { staticPathSchema } from './staticPathSchema/staticPathSchema';
@@ -33,7 +33,7 @@ export const validateApiMockServerConfig = (
staticPath: staticPathSchema.optional(),
interceptors: plainObjectSchema(interceptorsSchema).optional(),
cors: corsSchema.optional(),
database: databaseConfigSchema.optional(),
database: playgroundConfigSchema.optional(),
...(isConfigsContainAtLeastOneElement &&
api === 'graphql' && { configs: graphqlConfigSchema.shape.configs }),
...(isConfigsContainAtLeastOneElement &&
4 changes: 2 additions & 2 deletions src/utils/validate/validateFlatMockServerConfig.ts
Original file line number Diff line number Diff line change
@@ -4,11 +4,11 @@ import type { PlainObject } from '@/utils/types';

import { baseUrlSchema } from './baseUrlSchema/baseUrlSchema';
import { corsSchema } from './corsSchema/corsSchema';
import { databaseConfigSchema } from './databaseConfigSchema/databaseConfigSchema';
import { getMostSpecificPathFromError } from './getMostSpecificPathFromError';
import { getValidationMessageFromPath } from './getValidationMessageFromPath';
import { graphqlRequestConfigSchema } from './graphqlConfigSchema/graphqlConfigSchema';
import { interceptorsSchema } from './interceptorsSchema/interceptorsSchema';
import { playgroundConfigSchema } from './playgroundConfigSchema/playgroundConfigSchema';
import { portSchema } from './portSchema/portSchema';
import { restRequestConfigSchema } from './restConfigSchema/restConfigSchema';
import { staticPathSchema } from './staticPathSchema/staticPathSchema';
@@ -27,7 +27,7 @@ export const validateFlatMockServerConfig = (flatMockServerConfig: PlainObject)
staticPath: staticPathSchema.optional(),
interceptors: plainObjectSchema(interceptorsSchema).optional(),
cors: corsSchema.optional(),
database: databaseConfigSchema.optional()
database: playgroundConfigSchema.optional()
});

const flatMockServerComponentSchema = z.strictObject({
4 changes: 2 additions & 2 deletions src/utils/validate/validateMockServerConfig.ts
Original file line number Diff line number Diff line change
@@ -4,11 +4,11 @@ import type { PlainObject } from '@/utils/types';

import { baseUrlSchema } from './baseUrlSchema/baseUrlSchema';
import { corsSchema } from './corsSchema/corsSchema';
import { databaseConfigSchema } from './databaseConfigSchema/databaseConfigSchema';
import { getMostSpecificPathFromError } from './getMostSpecificPathFromError';
import { getValidationMessageFromPath } from './getValidationMessageFromPath';
import { graphqlConfigSchema } from './graphqlConfigSchema/graphqlConfigSchema';
import { interceptorsSchema } from './interceptorsSchema/interceptorsSchema';
import { playgroundConfigSchema } from './playgroundConfigSchema/playgroundConfigSchema';
import { portSchema } from './portSchema/portSchema';
import { restConfigSchema } from './restConfigSchema/restConfigSchema';
import { staticPathSchema } from './staticPathSchema/staticPathSchema';
@@ -34,7 +34,7 @@ export const validateMockServerConfig = (mockServerConfig: PlainObject) => {
cors: corsSchema.optional(),
rest: restConfigSchema.optional(),
graphql: graphqlConfigSchema.optional(),
database: databaseConfigSchema.optional()
database: playgroundConfigSchema.optional()
});

const validationResult = mockServerConfigSchema.safeParse(mockServerConfig);
2 changes: 1 addition & 1 deletion tsconfig.dev.json
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
"moduleResolution": "node",
"paths": {
"@/server": ["./src/server"],
"@/core/database": ["./src/core/database"],
"@/core/playground": ["./src/core/playground"],
"@/core/rest": ["./src/core/rest"],
"@/core/graphql": ["./src/core/graphql"],
"@/core/middlewares": ["./src/core/middlewares"],
2 changes: 1 addition & 1 deletion tsconfig.production.json
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
"moduleResolution": "node",
"paths": {
"@/server": ["./src/server"],
"@/core/database": ["./src/core/database"],
"@/core/playground": ["./src/core/playground"],
"@/core/rest": ["./src/core/rest"],
"@/core/graphql": ["./src/core/graphql"],
"@/core/middlewares": ["./src/core/middlewares"],