Skip to content

Commit

Permalink
#102 🐘 logger WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
RiceWithMeat committed Nov 19, 2023
1 parent 31a89a9 commit 155955e
Show file tree
Hide file tree
Showing 34 changed files with 632 additions and 100 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ module.exports = {
'@typescript-eslint/no-unsafe-enum-comparison': 'off',
'@typescript-eslint/no-var-requires': 'off',
'no-restricted-syntax': 'off',
'promise/always-return': 'off'
'promise/always-return': 'off',
'@typescript-eslint/no-namespace': 'off'
}
}
]
Expand Down
18 changes: 9 additions & 9 deletions src/core/graphql/createGraphQLRoutes/createGraphQLRoutes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ describe('createGraphQLRoutes', () => {
graphql: GraphqlConfig;
}
) => {
const { baseUrl, graphql, interceptors } = mockServerConfig;
const server = express();
const routerBase = express.Router();
const routerWithRoutes = createGraphQLRoutes(
routerBase,
mockServerConfig.graphql,
mockServerConfig.interceptors?.response
);
const routerWithRoutes = createGraphQLRoutes({
router: routerBase,
graphqlConfig: graphql,
serverInterceptors: interceptors
});

const graphqlBaseUrl = urlJoin(
mockServerConfig.baseUrl ?? '/',
mockServerConfig.graphql?.baseUrl ?? '/'
);
const serverBaseUrl = baseUrl ?? '/';

const graphqlBaseUrl = urlJoin(serverBaseUrl, graphql?.baseUrl ?? '/');

server.use(express.json());
server.use(graphqlBaseUrl, routerWithRoutes);
Expand Down
73 changes: 61 additions & 12 deletions src/core/graphql/createGraphQLRoutes/createGraphQLRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { flatten } from 'flat';
import {
asyncHandler,
callRequestInterceptor,
callRequestLogger,
callResponseInterceptors,
callResponseLoggers,
convertToEntityDescriptor,
getGraphQLInput,
isEntityDescriptor,
Expand All @@ -17,16 +19,29 @@ import type {
GraphQLEntityDescriptorOrValue,
GraphQLEntityName,
GraphQLMappedEntityName,
Interceptors
Interceptors,
Loggers
} from '@/utils/types';

import { prepareGraphQLRequestConfigs } from './helpers';

export const createGraphQLRoutes = (
router: IRouter,
graphqlConfig: GraphqlConfig,
serverResponseInterceptors?: Interceptors['response']
) => {
interface CreateGraphQLRoutesParams {
router: IRouter;
graphqlConfig: GraphqlConfig;
apiInterceptors?: Interceptors;
serverInterceptors?: Interceptors;
apiLoggers?: Loggers;
serverLoggers?: Loggers;
}

export const createGraphQLRoutes = ({
router,
graphqlConfig,
apiInterceptors,
serverInterceptors,
apiLoggers,
serverLoggers
}: CreateGraphQLRoutesParams) => {
const preparedGraphQLRequestConfig = prepareGraphQLRequestConfigs(graphqlConfig.configs);

const graphqlMiddleware = async (request: Request, response: Response, next: NextFunction) => {
Expand Down Expand Up @@ -69,11 +84,6 @@ export const createGraphQLRoutes = (
return next();
}

const requestInterceptor = matchedRequestConfig.interceptors?.request;
if (requestInterceptor) {
await callRequestInterceptor({ request, interceptor: requestInterceptor });
}

const matchedRouteConfig = matchedRequestConfig.routes.find(({ entities }) => {
if (!entities) return true;
const entries = Object.entries(entities) as [
Expand Down Expand Up @@ -117,10 +127,37 @@ export const createGraphQLRoutes = (
});
});

let isRequestLoggerResolved = !!serverLoggers?.request;

const apiRequestInterceptor = apiInterceptors?.request;
if (apiRequestInterceptor) {
await callRequestInterceptor({ request, interceptor: apiRequestInterceptor });
}
const apiRequestLogger = apiLoggers?.request;
if (!isRequestLoggerResolved && apiRequestLogger) {
await callRequestLogger({ request, logger: apiRequestLogger });
isRequestLoggerResolved = true;
}

const requestRequestInterceptor = matchedRequestConfig.interceptors?.request;
if (requestRequestInterceptor) {
await callRequestInterceptor({ request, interceptor: requestRequestInterceptor });
}
const requestRequestLogger = matchedRequestConfig.loggers?.request;
if (!isRequestLoggerResolved && requestRequestLogger) {
await callRequestLogger({ request, logger: requestRequestLogger });
isRequestLoggerResolved = true;
}

if (!matchedRouteConfig) {
return next();
}

const routeRequestLogger = matchedRouteConfig.loggers?.request;
if (routeRequestLogger) {
await callRequestLogger({ request, logger: routeRequestLogger });
}

const matchedRouteConfigData =
typeof matchedRouteConfig.data === 'function'
? await matchedRouteConfig.data(request, matchedRouteConfig.entities ?? {})
Expand All @@ -134,10 +171,22 @@ export const createGraphQLRoutes = (
routeInterceptor: matchedRouteConfig.interceptors?.response,
requestInterceptor: matchedRequestConfig.interceptors?.response,
apiInterceptor: graphqlConfig.interceptors?.response,
serverInterceptor: serverResponseInterceptors
serverInterceptor: serverInterceptors?.response
}
});

await callResponseLoggers({
request,
response,
responseLoggers: {
routeLogger: matchedRouteConfig.loggers?.response,
requestLogger: matchedRequestConfig.loggers?.response,
apiLogger: graphqlConfig.loggers?.response,
serverLogger: serverLoggers?.response
},
data
});

// ✅ important:
// set 'Cache-Control' header for explicit browsers response revalidate
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
Expand Down
2 changes: 2 additions & 0 deletions src/core/middlewares/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ export * from './destroyerMiddleware/destroyerMiddleware';
export * from './errorMiddleware/errorMiddleware';
export * from './noCorsMiddleware/noCorsMiddleware';
export * from './notFoundMiddleware/notFoundMiddleware';
export * from './requestInfoMiddleware/requestInfoMiddleware';
export * from './requestInterceptorMiddleware/requestInterceptorMiddleware';
export * from './requestLoggerMiddleware/requestLoggerMiddleware';
export * from './staticMiddleware/staticMiddleware';
20 changes: 10 additions & 10 deletions src/core/middlewares/notFoundMiddleware/notFoundMiddleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ const createServer = (
const serverBaseUrl = baseUrl ?? '/';

const restBaseUrl = urlJoin(serverBaseUrl, rest?.baseUrl ?? '/');
const routerWithRestRoutes = createRestRoutes(
express.Router(),
{ configs: rest?.configs ?? [] },
interceptors?.response
);
const routerWithRestRoutes = createRestRoutes({
router: express.Router(),
restConfig: { configs: rest?.configs ?? [] },
serverInterceptors: interceptors
});
server.use(restBaseUrl, routerWithRestRoutes);

const graphqlBaseUrl = urlJoin(serverBaseUrl, graphql?.baseUrl ?? '/');
const routerWithGraphqlRoutes = createGraphQLRoutes(
express.Router(),
{ configs: graphql?.configs ?? [] },
interceptors?.response
);
const routerWithGraphqlRoutes = createGraphQLRoutes({
router: express.Router(),
graphqlConfig: { configs: graphql?.configs ?? [] },
serverInterceptors: interceptors
});
server.use(graphqlBaseUrl, routerWithGraphqlRoutes);

server.set('view engine', 'ejs');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Express } from 'express';

declare global {
namespace Express {
export interface Request {
id: number;
unixTimestamp: number;
}
}
}

export const requestInfoMiddleware = (server: Express) => {
let requestId = 0;

server.use((request, _response, next) => {
requestId += 1;
request.id = requestId;

request.unixTimestamp = Date.now();

return next();
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ import type { Express } from 'express';
import { asyncHandler, callRequestInterceptor } from '@/utils/helpers';
import type { RequestInterceptor } from '@/utils/types';

export const requestInterceptorMiddleware = (server: Express, interceptor: RequestInterceptor) => {
export const requestInterceptorMiddleware = (
server: Express,
interceptor: RequestInterceptor,
path: string = '*'
) => {
server.use(
path,
asyncHandler(async (request, _response, next) => {
await callRequestInterceptor({ request, interceptor });
return next();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Express } from 'express';

import { asyncHandler, callRequestLogger } from '@/utils/helpers';
import type { RequestLogger } from '@/utils/types';

export const requestLoggerMiddleware = (server: Express, logger: RequestLogger) => {
server.use(
asyncHandler(async (request, _response, next) => {
await callRequestLogger({ request, logger });
return next();
})
);
};
18 changes: 9 additions & 9 deletions src/core/rest/createRestRoutes/createRestRoutes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ describe('createRestRoutes', () => {
rest: RestConfig;
}
) => {
const { baseUrl, rest, interceptors } = mockServerConfig;
const server = express();
const routerBase = express.Router();
const routerWithRoutes = createRestRoutes(
routerBase,
mockServerConfig.rest,
mockServerConfig.interceptors?.response
);
const routerWithRoutes = createRestRoutes({
router: routerBase,
restConfig: rest,
serverInterceptors: interceptors
});

const restBaseUrl = urlJoin(
mockServerConfig.baseUrl ?? '/',
mockServerConfig.rest?.baseUrl ?? '/'
);
const serverBaseUrl = baseUrl ?? '/';

const restBaseUrl = urlJoin(serverBaseUrl, rest?.baseUrl ?? '/');

server.use(express.json());
server.use(restBaseUrl, routerWithRoutes);
Expand Down
71 changes: 60 additions & 11 deletions src/core/rest/createRestRoutes/createRestRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import { flatten } from 'flat';
import {
asyncHandler,
callRequestInterceptor,
callRequestLogger,
callResponseInterceptors,
callResponseLoggers,
convertToEntityDescriptor,
isEntityDescriptor,
resolveEntityValues
} from '@/utils/helpers';
import type {
Interceptors,
Loggers,
RestConfig,
RestEntityDescriptorOnly,
RestEntityDescriptorOrValue,
Expand All @@ -20,19 +23,26 @@ import type {

import { prepareRestRequestConfigs } from './helpers';

export const createRestRoutes = (
router: IRouter,
restConfig: RestConfig,
serverResponseInterceptors?: Interceptors['response']
) => {
interface CreateRestRoutesParams {
router: IRouter;
restConfig: RestConfig;
apiInterceptors?: Interceptors;
serverInterceptors?: Interceptors;
apiLoggers?: Loggers;
serverLoggers?: Loggers;
}

export const createRestRoutes = ({
router,
restConfig,
apiInterceptors,
serverInterceptors,
apiLoggers,
serverLoggers
}: CreateRestRoutesParams) => {
prepareRestRequestConfigs(restConfig.configs).forEach((requestConfig) => {
router.route(requestConfig.path)[requestConfig.method](
asyncHandler(async (request, response, next) => {
const requestInterceptor = requestConfig.interceptors?.request;
if (requestInterceptor) {
await callRequestInterceptor({ request, interceptor: requestInterceptor });
}

const matchedRouteConfig = requestConfig.routes.find(({ entities }) => {
if (!entities) return true;
const entries = Object.entries(entities) as [
Expand Down Expand Up @@ -72,10 +82,37 @@ export const createRestRoutes = (
});
});

let isRequestLoggerResolved = !!serverLoggers?.request;

const apiRequestInterceptor = apiInterceptors?.request;
if (apiRequestInterceptor) {
await callRequestInterceptor({ request, interceptor: apiRequestInterceptor });
}
const apiRequestLogger = apiLoggers?.request;
if (!isRequestLoggerResolved && apiRequestLogger) {
await callRequestLogger({ request, logger: apiRequestLogger });
isRequestLoggerResolved = true;
}

const requestRequestInterceptor = requestConfig.interceptors?.request;
if (requestRequestInterceptor) {
await callRequestInterceptor({ request, interceptor: requestRequestInterceptor });
}
const requestRequestLogger = requestConfig.loggers?.request;
if (!isRequestLoggerResolved && requestRequestLogger) {
await callRequestLogger({ request, logger: requestRequestLogger });
isRequestLoggerResolved = true;
}

if (!matchedRouteConfig) {
return next();
}

const routeRequestLogger = matchedRouteConfig.loggers?.request;
if (routeRequestLogger) {
await callRequestLogger({ request, logger: routeRequestLogger });
}

const matchedRouteConfigData =
typeof matchedRouteConfig.data === 'function'
? await matchedRouteConfig.data(request, matchedRouteConfig.entities ?? {})
Expand All @@ -89,10 +126,22 @@ export const createRestRoutes = (
routeInterceptor: matchedRouteConfig.interceptors?.response,
requestInterceptor: requestConfig.interceptors?.response,
apiInterceptor: restConfig.interceptors?.response,
serverInterceptor: serverResponseInterceptors
serverInterceptor: serverInterceptors?.response
}
});

await callResponseLoggers({
request,
response,
responseLoggers: {
routeLogger: matchedRouteConfig.loggers?.response,
requestLogger: requestConfig.loggers?.response,
apiLogger: restConfig.loggers?.response,
serverLogger: serverLoggers?.response
},
data
});

// ✅ important:
// set 'Cache-Control' header for explicit browsers response revalidate
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
Expand Down
Loading

0 comments on commit 155955e

Please sign in to comment.