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

feat(core): Logger #212

Merged
merged 1 commit into from Jan 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 1 addition & 4 deletions packages/@integration/src/http/effects/static.effects.ts
Expand Up @@ -4,7 +4,7 @@ import { r, combineRoutes, use } from '@marblejs/core';
import { requestValidator$, t } from '@marblejs/middleware-io';
import { multipart$ } from '@marblejs/middleware-multipart';
import { streamFileTo } from '@marblejs/middleware-multipart/dist/multipart.util';
import { readFile } from '@marblejs/core/dist/+internal';
import { readFile } from '@marblejs/core/dist/+internal/files';
import { map, mergeMap } from 'rxjs/operators';

const STATIC_PATH = path.resolve(__dirname, '../../../../../assets');
Expand All @@ -18,7 +18,6 @@ const postFile$ = r.pipe(
r.matchPath('/upload'),
r.matchType('POST'),
r.useEffect(req$ => {
console.log('Effect bootstrapped: "postFile$"');

return req$.pipe(
use(multipart$({
Expand All @@ -38,7 +37,6 @@ const getFileStream$ = r.pipe(
r.matchPath('/stream/:dir*'),
r.matchType('GET'),
r.useEffect(req$ => {
console.log('Effect bootstrapped: "getFileStream$"');

return req$.pipe(
use(getFileValidator$),
Expand All @@ -52,7 +50,6 @@ const getFile$ = r.pipe(
r.matchPath('/:dir*'),
r.matchType('GET'),
r.useEffect(req$ => {
console.log('Effect bootstrapped: "getFile$"');

return req$.pipe(
use(getFileValidator$),
Expand Down
4 changes: 0 additions & 4 deletions packages/@integration/src/http/effects/user.effects.ts
Expand Up @@ -19,7 +19,6 @@ const getUserList$ = r.pipe(
r.matchPath('/'),
r.matchType('GET'),
r.useEffect(req$ => {
console.log('Effect bootstrapped: "getUserList$"');

return req$.pipe(
mergeMap(Dao.getUsers),
Expand All @@ -31,7 +30,6 @@ const getUser$ = r.pipe(
r.matchPath('/:id'),
r.matchType('GET'),
r.useEffect(req$ => {
console.log('Effect bootstrapped: "getUser$"');

return req$.pipe(
use(getUserValidator$),
Expand All @@ -49,7 +47,6 @@ const getUserBuffered$ = r.pipe(
r.matchPath('/:id/buffered'),
r.matchType('GET'),
r.useEffect(req$ => {
console.log('Effect bootstrapped: "getUserBuffered$"');

return req$.pipe(
bufferCount(2),
Expand All @@ -73,7 +70,6 @@ const postUser$ = r.pipe(
r.matchPath('/'),
r.matchType('POST'),
r.useEffect(req$ => {
console.log('Effect bootstrapped: "postUser$"');

return req$.pipe(
use(postUserValidator$),
Expand Down
6 changes: 3 additions & 3 deletions packages/@integration/src/http/http.listener.ts
@@ -1,13 +1,13 @@
import { httpListener } from '@marblejs/core';
import { isTestEnv } from '@marblejs/core/dist/+internal/utils';
import { bodyParser$ } from '@marblejs/middleware-body';
import { loggerDev$, loggerFile$ } from './middlewares/logger.middleware';
import { logger$ } from '@marblejs/middleware-logger';
import { cors$ } from './middlewares/cors.middleware';
import { api$ } from './effects/api.effects';

export default httpListener({
middlewares: [
loggerDev$,
loggerFile$,
logger$({ silent: isTestEnv() }),
bodyParser$(),
cors$,
],
Expand Down
14 changes: 1 addition & 13 deletions packages/@integration/src/http/index.ts
@@ -1,25 +1,13 @@
import { createServer, matchEvent, ServerEvent, HttpServerEffect } from '@marblejs/core';
import { merge } from 'rxjs';
import { tap, map } from 'rxjs/operators';
import { createServer } from '@marblejs/core';
import httpListener from './http.listener';

const port = process.env.PORT
? Number(process.env.PORT)
: undefined;

const listening$: HttpServerEffect = event$ =>
event$.pipe(
matchEvent(ServerEvent.listening),
map(event => event.payload),
tap(({ port, host }) => console.log(`Server running @ http://${host}:${port}/ 🚀`)),
);

export const server = createServer({
port,
httpListener,
event$: (...args) => merge(
listening$(...args),
),
});

export const bootstrap = async () => {
Expand Down
10 changes: 0 additions & 10 deletions packages/@integration/src/http/middlewares/logger.middleware.ts

This file was deleted.

3 changes: 2 additions & 1 deletion packages/@integration/test/http.integration.spec.ts
@@ -1,6 +1,7 @@
import * as request from 'supertest';
import { HttpStatus } from '@marblejs/core';
import { ContentType, createHttpServerTestBed } from '@marblejs/core/dist/+internal';
import { ContentType } from '@marblejs/core/dist/+internal/http';
import { createHttpServerTestBed } from '@marblejs/core/dist/+internal/testing';
import { server } from '../src/http';

describe('API integration', () => {
Expand Down
7 changes: 0 additions & 7 deletions packages/core/src/+internal/index.ts

This file was deleted.

6 changes: 4 additions & 2 deletions packages/core/src/+internal/testing/http.helper.ts
Expand Up @@ -12,8 +12,9 @@ import {
} from '../../http/http.interface';
import { createContext, lookup, registerAll, bindTo } from '../../context/context.factory';
import { createEffectContext } from '../../effects/effectsContext.factory';
import { Server } from '../../http/server/http.server.interface';
import { HttpRequestBusToken, HttpServerClientToken } from '../../http/server/http.server.tokens';
import { ServerIO } from '../../listener/listener.interface';
import { LoggerToken, mockLogger } from '../../logger';

interface HttpRequestMockParams {
url?: string;
Expand Down Expand Up @@ -65,6 +66,7 @@ export const createHttpResponse = (data: HttpResponseMockParams = {}) =>

export const createMockEffectContext = () => {
const dependencies = [
bindTo(LoggerToken)(mockLogger),
bindTo(HttpRequestBusToken)(() => new Subject<HttpRequest>()),
bindTo(HttpServerClientToken)(() => http.createServer()),
];
Expand All @@ -73,7 +75,7 @@ export const createMockEffectContext = () => {
return createEffectContext({ ask: lookup(context), client });
};

export const createHttpServerTestBed = (server: Promise<Server>) => {
export const createHttpServerTestBed = (server: Promise<ServerIO<HttpServer>>) => {
let httpServer: HttpServer;

const getInstance = () => httpServer;
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/+internal/utils/any.util.ts
Expand Up @@ -3,3 +3,6 @@ export const isNonNullable = <T>(value: T): value is NonNullable<T> =>

export const isNullable = <T>(value: T) =>
!isNonNullable(value);

export const isTestEnv = () =>
process.env.NODE_ENV === 'test';
5 changes: 4 additions & 1 deletion packages/core/src/+internal/utils/array.util.ts
Expand Up @@ -15,5 +15,8 @@ export const filterArray = <T>(f: (v: T) => boolean) => (array: T[]) =>
export const mapArray = <T, R>(f: (v: T) => R) => (array: T[]) =>
array.map(f);

export const insertIf = <T>(condition: boolean, ...elements: T[]) =>
export const insertIf = (condition: boolean) => <T>(...elements: T[]) =>
condition ? elements as NonNullable<T>[] : [];

export const insertIfElse = (condition: boolean) => <T>(...elements: T[]) => <U>(...elseElements: U[]) =>
condition ? elements as NonNullable<T>[] : elseElements as NonNullable<U>[];
5 changes: 5 additions & 0 deletions packages/core/src/+internal/utils/string.util.ts
Expand Up @@ -14,6 +14,11 @@ export const trim = (strings: TemplateStringsArray, ...values: any[]) => {
return interpolation.trim();
};

export const trunc = (n: number) => (input: string) =>
(input.length > n)
? input.substr(0, n-1) + '…'
: input;

export const stringify = (value: any): string =>
typeof value === 'function'
? (value.displayName || value.name)
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/context/context.factory.ts
Expand Up @@ -6,8 +6,8 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { contramap, ordString, Ord } from 'fp-ts/lib/Ord';
import { ContextToken } from './context.token.factory';

const ordContextToken: Ord<ContextToken<any>> = contramap((t: ContextToken) => t._id)(ordString);
const setoidContextToken: E.Eq<ContextToken> = { equals: ordContextToken.equals };
export const ordContextToken: Ord<ContextToken<any>> = contramap((t: ContextToken) => t._id)(ordString);
export const setoidContextToken: E.Eq<ContextToken> = { equals: ordContextToken.equals };

export interface Context extends Map<ContextToken, ContextDependency | any> {}

Expand Down
33 changes: 33 additions & 0 deletions packages/core/src/context/context.logger.ts
@@ -0,0 +1,33 @@
import * as M from 'fp-ts/lib/Map';
import * as A from 'fp-ts/lib/Array';
import { io } from 'fp-ts/lib/IO';
import { pipe } from 'fp-ts/lib/pipeable';
import { LoggerToken } from '../logger';
import { Context, lookup, ordContextToken } from './context.factory';
import { useContext } from './context.hook';
import { ContextToken } from './context.token.factory';

export const logContext = (tag: string) => (context: Context): Context => {
const ask = lookup(context);
const logger = useContext(LoggerToken)(ask);

const log = (token: ContextToken) => logger({
tag,
type: 'Context',
message: token.name
? `Registered: "${token.name}"`
: `Registered unnamed token: ${token._id}`,
});

const logDependencies = A.array.sequence(io)(
pipe(
context,
M.keys(ordContextToken),
A.map(log),
)
);

logDependencies();

return context;
}
2 changes: 1 addition & 1 deletion packages/core/src/effects/specs/effects.combiner.spec.ts
Expand Up @@ -2,7 +2,7 @@ import { of } from 'rxjs';
import { tap, mapTo, filter } from 'rxjs/operators';
import { HttpMiddlewareEffect, HttpEffect } from '../../http/effects/http.effects.interface';
import { combineMiddlewares, combineEffects } from '../effects.combiner';
import { Marbles, createHttpRequest, createMockEffectContext } from '../../+internal';
import { Marbles, createHttpRequest, createMockEffectContext } from '../../+internal/testing';

describe('#combineMiddlewares', () => {
test('combines middlewares into one stream', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/http/effects/http.effects.factory.ts
@@ -1,4 +1,4 @@
import { getArrayFromEnum } from '../../+internal';
import { getArrayFromEnum } from '../../+internal/utils';
import { coreErrorFactory, CoreErrorOptions } from '../../error/error.factory';
import { HttpEffect } from '../effects/http.effects.interface';
import { RouteEffect } from '../router/http.router.interface';
Expand Down
@@ -1,4 +1,4 @@
import { Marbles, createHttpResponse, createHttpRequest } from '../../../+internal';
import { Marbles, createHttpResponse, createHttpRequest } from '../../../+internal/testing';
import { defaultError$ } from '../http.error.effect';
import { HttpError } from '../http.error.model';

Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/http/response/http.responseBody.factory.ts
@@ -1,5 +1,6 @@
import { HttpHeaders } from '../http.interface';
import { ContentType, getContentType, isStream } from '../../+internal';
import { ContentType, getContentType } from '../../+internal/http';
import { isStream } from '../../+internal/utils';

export type ResponseBodyFactory = (headers: HttpHeaders) => (body: any) => any;

Expand Down
@@ -1,7 +1,7 @@
import * as fileType from 'file-type';
import * as mime from 'mime';
import { HttpStatus } from '../http.interface';
import { ContentType } from '../../+internal';
import { ContentType } from '../../+internal/http';

export const DEFAULT_CONTENT_TYPE = ContentType.APPLICATION_JSON;

Expand Down
@@ -1,4 +1,4 @@
import { ContentType } from '../../../+internal';
import { ContentType } from '../../../+internal/http';
import { bodyFactory } from '../http.responseBody.factory';

describe('Response body factory', () => {
Expand Down
@@ -1,5 +1,5 @@
import * as fs from 'fs';
import { ContentType } from '../../../+internal';
import { ContentType } from '../../../+internal/http';
import { DEFAULT_CONTENT_TYPE, contentTypeFactory, getMimeType } from '../http.responseContentType.factory';

describe('Response content-type factory', () => {
Expand Down
Expand Up @@ -3,7 +3,8 @@ import * as fs from 'fs';
import { HttpResponse, HttpStatus } from '../../http.interface';
import { handleResponse } from '../http.responseHandler';
import { DEFAULT_HEADERS } from '../http.responseHeaders.factory';
import { ContentType, createMockEffectContext, createHttpResponse, createHttpRequest } from '../../../+internal';
import { createMockEffectContext, createHttpResponse, createHttpRequest } from '../../../+internal/testing';
import { ContentType } from '../../../+internal/http';

describe('Response handler', () => {
const effectContext = createMockEffectContext();
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/http/router/http.router.factory.ts
@@ -1,4 +1,4 @@
import { insertIf } from '../../+internal';
import { insertIf } from '../../+internal/utils';
import { HttpMiddlewareEffect } from '../effects/http.effects.interface';
import { isRouteEffectGroup } from './http.router.helpers';
import {
Expand Down Expand Up @@ -40,7 +40,7 @@ export const factorizeRouting = (
meta: route.meta,
middlewares: [
...middlewares,
...insertIf(!!route.middleware, route.middleware),
...insertIf(!!route.middleware)(route.middleware),
],
};

Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/http/router/http.router.resolver.v2.ts
Expand Up @@ -12,6 +12,7 @@ import {
} from '../error/http.error.model';
import { useContext } from '../../context/context.hook';
import { HttpRequestBusToken } from '../server/http.server.tokens';
import { LoggerToken, LoggerTag } from '../../logger';
import { Routing, BootstrappedRoutingItem } from './http.router.interface';
import { queryParamsFactory } from './http.router.query.factory';
import { matchRoute } from './http.router.matcher';
Expand All @@ -27,6 +28,8 @@ export const resolveRouting = (
error$?: HttpErrorEffect,
) => {
const requestBusSubject = useContext(HttpRequestBusToken)(ctx.ask);
const logger = useContext(LoggerToken)(ctx.ask);

const close$ = fromEvent(ctx.client, 'close').pipe(take(1), share());
const outputSubject = new Subject<{ res: HttpEffectResponse; req: HttpRequest}>();
const errorSubject = new Subject<{ error: Error; req: HttpRequest }>();
Expand Down Expand Up @@ -77,6 +80,12 @@ export const resolveRouting = (
const subject = new Subject<HttpRequest>();
const decorate = !meta?.continuous;

logger({
tag: LoggerTag.HTTP,
type: 'Router',
message: `Effect mapped: ${item.path} ${method}`,
})();

const input$ = subject.asObservable();
const middleware$ = combineRouteMiddlewares(decorate)(...middlewares)(input$, ctx);
const effect$ = decorate ? decorateEffect(middleware$) : middleware$;
Expand Down
Expand Up @@ -2,7 +2,7 @@

import { of } from 'rxjs';
import { mapTo, take, toArray, delay, mergeMap, map } from 'rxjs/operators';
import { createMockEffectContext, createHttpRequest, createHttpResponse } from '../../../+internal';
import { createMockEffectContext, createHttpRequest, createHttpResponse } from '../../../+internal/testing';
import { HttpEffect } from '../../effects/http.effects.interface';
import { Routing, RoutingItem } from '../http.router.interface';
import { resolveRouting } from '../http.router.resolver.v2';
Expand Down
4 changes: 0 additions & 4 deletions packages/core/src/http/server/http.server.interface.ts
@@ -1,8 +1,6 @@
import * as https from 'https';
import { HttpServerEffect } from '../effects/http.effects.interface';
import { BoundDependency } from '../../context/context.factory';
import { ListenerServer } from '../../listener/listener.interface';
import { HttpServer } from '../http.interface';
import { httpListener } from './http.server.listener';

export interface CreateServerConfig {
Expand All @@ -14,8 +12,6 @@ export interface CreateServerConfig {
dependencies?: BoundDependency<any>[];
}

export interface Server extends ListenerServer<HttpServer> {}

export interface ServerOptions {
httpsOptions?: https.ServerOptions;
}