From 9c76ba00b759fa7f0cda9a824f7ae39273c36417 Mon Sep 17 00:00:00 2001 From: METGE Alain Date: Mon, 12 Mar 2018 14:47:29 +0100 Subject: [PATCH 01/10] add log --- package.json | 2 +- src/commands/command.ts | 2 ++ src/configurations/properties/chainedPropertyValue.ts | 4 ++++ src/configurations/properties/dynamicProperty.ts | 4 ++++ src/instrumentations/metrics/prometheusMetrics.ts | 1 + src/instrumentations/trackers/JaegerInstrumentation.ts | 2 ++ src/instrumentations/trackers/zipkinInstrumentation.ts | 3 +++ 7 files changed, 17 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 60596b3..8655fec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta939", + "version": "2.0.0-beta940", "description": "Vulcain micro-service framework", "main": "lib/src/index.js", "scripts": { diff --git a/src/commands/command.ts b/src/commands/command.ts index b3579fd..dfa3f80 100644 --- a/src/commands/command.ts +++ b/src/commands/command.ts @@ -60,6 +60,8 @@ export class HystrixCommand { let result; + this.command.context.logInfo(() => `Executing command ${this.properties.commandName} with properties ${JSON.stringify(this.properties)}`); + // Execution this.hystrixMetrics.incrementExecutionCount(); let recordTotalTime = true; diff --git a/src/configurations/properties/chainedPropertyValue.ts b/src/configurations/properties/chainedPropertyValue.ts index fc9ef92..7f4ed85 100644 --- a/src/configurations/properties/chainedPropertyValue.ts +++ b/src/configurations/properties/chainedPropertyValue.ts @@ -67,4 +67,8 @@ export class ChainedDynamicProperty extends DynamicProperty { v = this._activeProperty.value; return v || this.defaultValue; } + + public toString() { + return this.value; + } } diff --git a/src/configurations/properties/dynamicProperty.ts b/src/configurations/properties/dynamicProperty.ts index b0ba630..a44cc31 100644 --- a/src/configurations/properties/dynamicProperty.ts +++ b/src/configurations/properties/dynamicProperty.ts @@ -74,4 +74,8 @@ export class DynamicProperty implements IDynamicProperty, IUpdatableProper this._propertyChanged = null; this.removed = true; } + + public toString() { + return this.value; + } } diff --git a/src/instrumentations/metrics/prometheusMetrics.ts b/src/instrumentations/metrics/prometheusMetrics.ts index 45f222f..844cb2d 100644 --- a/src/instrumentations/metrics/prometheusMetrics.ts +++ b/src/instrumentations/metrics/prometheusMetrics.ts @@ -17,6 +17,7 @@ export class PrometheusMetrics implements IMetrics { private ignoredProperties = ["hystrixProperties", "params"]; constructor(private container: IContainer) { + Service.log.info(null, () => `Providing prometheus metrics from '/metrics'`); container.registerHTTPEndpoint("GET", '/metrics', (req: http.IncomingMessage, resp: http.ServerResponse) => { const chunk = new Buffer(Prometheus.register.metrics(), 'utf8'); diff --git a/src/instrumentations/trackers/JaegerInstrumentation.ts b/src/instrumentations/trackers/JaegerInstrumentation.ts index 64c6c9f..421bc75 100644 --- a/src/instrumentations/trackers/JaegerInstrumentation.ts +++ b/src/instrumentations/trackers/JaegerInstrumentation.ts @@ -19,6 +19,8 @@ export class JaegerInstrumentation implements IRequestTrackerFactory { jaegerAddress = jaegerAddress + ':9411'; } + Service.log.info(null, () => `Enabling Jaeger instrumentation at ${jaegerAddress}`); + const sender = new UDPSender(); const tracer = new jaeger.Tracer(Service.fullServiceName, new jaeger.RemoteReporter(sender), diff --git a/src/instrumentations/trackers/zipkinInstrumentation.ts b/src/instrumentations/trackers/zipkinInstrumentation.ts index ba20655..b39af5c 100644 --- a/src/instrumentations/trackers/zipkinInstrumentation.ts +++ b/src/instrumentations/trackers/zipkinInstrumentation.ts @@ -29,6 +29,9 @@ export class ZipkinInstrumentation implements IRequestTrackerFactory { if (!/:[0-9]+/.test(zipkinAddress)) { zipkinAddress = zipkinAddress + ':9411'; } + + Service.log.info(null, () => `Enabling Zipkin instrumentation at ${zipkinAddress}`); + const recorder = new BatchRecorder({ logger: new HttpLogger({ endpoint: `${zipkinAddress}/api/v1/spans`, From 1a44485ba0102a4a8a6d9b6fe6612aef4ca7d5bb Mon Sep 17 00:00:00 2001 From: METGE Alain Date: Mon, 12 Mar 2018 17:32:34 +0100 Subject: [PATCH 02/10] OPTIONS is only valid in test mode --- package.json | 2 +- src/configurations/properties/chainedPropertyValue.ts | 4 ++-- src/configurations/properties/dynamicProperty.ts | 4 ++-- src/pipeline/serverAdapter.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 8655fec..e8afd6a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta940", + "version": "2.0.0-beta942", "description": "Vulcain micro-service framework", "main": "lib/src/index.js", "scripts": { diff --git a/src/configurations/properties/chainedPropertyValue.ts b/src/configurations/properties/chainedPropertyValue.ts index 7f4ed85..e8d2b71 100644 --- a/src/configurations/properties/chainedPropertyValue.ts +++ b/src/configurations/properties/chainedPropertyValue.ts @@ -68,7 +68,7 @@ export class ChainedDynamicProperty extends DynamicProperty { return v || this.defaultValue; } - public toString() { - return this.value; + public toJSON(key: string) { + return key ? String(this.value) : this.name + "=" + String(this.value); } } diff --git a/src/configurations/properties/dynamicProperty.ts b/src/configurations/properties/dynamicProperty.ts index a44cc31..59e7fb3 100644 --- a/src/configurations/properties/dynamicProperty.ts +++ b/src/configurations/properties/dynamicProperty.ts @@ -75,7 +75,7 @@ export class DynamicProperty implements IDynamicProperty, IUpdatableProper this.removed = true; } - public toString() { - return this.value; + public toJSON(key: string) { + return key ? String(this.value) : this.name + "=" + String(this.value); } } diff --git a/src/pipeline/serverAdapter.ts b/src/pipeline/serverAdapter.ts index eeafa98..86571b8 100644 --- a/src/pipeline/serverAdapter.ts +++ b/src/pipeline/serverAdapter.ts @@ -139,7 +139,7 @@ export class HttpAdapter extends ServerAdapter { return; } - if (req.method === "OPTIONS" && Service.isTestEnvironment) { + if (req.method === "OPTIONS") { resp.setHeader("Access-Control-Allow-Origin", "*"); resp.setHeader("Access-Control-Allow-Methods", "GET,POST"); resp.setHeader("Access-Control-Allow-Headers", "origin, content-type, accept"); From 8ec0c86200226dd8a4298408898f9dc336af615f Mon Sep 17 00:00:00 2001 From: METGE Alain Date: Mon, 12 Mar 2018 17:58:05 +0100 Subject: [PATCH 03/10] always allow CORS --- package.json | 2 +- src/pipeline/middlewares/normalizeDataMiddleware.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index e8afd6a..1efa5bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta942", + "version": "2.0.0-beta943", "description": "Vulcain micro-service framework", "main": "lib/src/index.js", "scripts": { diff --git a/src/pipeline/middlewares/normalizeDataMiddleware.ts b/src/pipeline/middlewares/normalizeDataMiddleware.ts index 168d902..fe1b891 100644 --- a/src/pipeline/middlewares/normalizeDataMiddleware.ts +++ b/src/pipeline/middlewares/normalizeDataMiddleware.ts @@ -44,8 +44,7 @@ export class NormalizeDataMiddleware extends VulcainMiddleware { } // Inject request context in response - if( Service.isTestEnvironment) - ctx.response.addHeader('Access-Control-Allow-Origin', '*'); // CORS + ctx.response.addHeader('Access-Control-Allow-Origin', '*'); // CORS if (Object.getOwnPropertyDescriptor(ctx.response.content, "value") || Object.getOwnPropertyDescriptor(ctx.response.content, "error")) { ctx.response.content.meta = ctx.response.content.meta || {}; From a69aae2a5888b168f3fd6ec2d252e261163a5b07 Mon Sep 17 00:00:00 2001 From: METGE Alain Date: Tue, 13 Mar 2018 18:18:41 +0100 Subject: [PATCH 04/10] fix jaeger error --- package.json | 2 +- src/application.ts | 2 +- src/globals/system.ts | 7 ++++-- src/instrumentations/common.ts | 2 +- src/instrumentations/span.ts | 25 ++++++++++--------- .../trackers/JaegerInstrumentation.ts | 14 ++++++----- src/pipeline/requestContext.ts | 9 ++++--- 7 files changed, 34 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 1efa5bf..82c4f8b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta943", + "version": "2.0.0-beta945", "description": "Vulcain micro-service framework", "main": "lib/src/index.js", "scripts": { diff --git a/src/application.ts b/src/application.ts index f72c940..30086eb 100644 --- a/src/application.ts +++ b/src/application.ts @@ -93,7 +93,7 @@ export class Application { throw new Error("Domain name is required."); } - Service.defaultDomainName = this.domainName; + Service.setDomainName(this.domainName); this._container = this._container || new Container(); this.container.registerHTTPEndpoint("GET", Conventions.instance.defaultHystrixPath, hystrixStream.getHandler()); } diff --git a/src/globals/system.ts b/src/globals/system.ts index 2351d8d..733533c 100644 --- a/src/globals/system.ts +++ b/src/globals/system.ts @@ -37,10 +37,13 @@ export class Service { private static crypter: CryptoHelper; private static _manifest: VulcainManifest; private static _stubManager: IStubManager; - static defaultDomainName: string; private static _serviceStatus: ServiceStatus = ServiceStatus.Starting; private static _statusTimer: NodeJS.Timer; + public static setDomainName(name: string) { + if(name) + Service._domainName = name; + } /** * Settings properties from vulcain.json file */ @@ -326,7 +329,7 @@ export class Service { if (env) Service._domainName = env; else - Service._domainName = Service.defaultDomainName; + Service._domainName = "vulcain"; } return Service._domainName; } diff --git a/src/instrumentations/common.ts b/src/instrumentations/common.ts index 82e417c..21b822c 100644 --- a/src/instrumentations/common.ts +++ b/src/instrumentations/common.ts @@ -12,7 +12,7 @@ export enum SpanKind { export interface TrackerId { correlationId?: string; parentId?: string; - spanId: string; + spanId?: string; } export interface ITracker { diff --git a/src/instrumentations/span.ts b/src/instrumentations/span.ts index 2a6bc66..5a66791 100644 --- a/src/instrumentations/span.ts +++ b/src/instrumentations/span.ts @@ -31,17 +31,12 @@ export class Span implements ISpanTracker { return this._tracker; } - private constructor(public context: RequestContext, public kind: SpanKind, private name: string, parent: TrackerId | ISpanTracker) { + private constructor(public context: RequestContext, public kind: SpanKind, private name: string, parentId: TrackerId) { this._logger = context.container.get(DefaultServiceNames.Logger); this.startTime = Date.now(); this.startTick = process.hrtime(); - let parentId = parent; - if ((parent).id) { - parentId = (parent).id; - } - this._id = { correlationId: parentId.correlationId, spanId: !parentId.spanId ? parentId.correlationId : this.randomTraceId(), @@ -50,9 +45,10 @@ export class Span implements ISpanTracker { this.metrics = context.container.get(DefaultServiceNames.Metrics); - this.addTag("name",name); + this.addTag("name", name); this.addTag("domain", Service.domainName); this.addTag("host", os.hostname()); + this.addTag("correlationId", parentId.correlationId); this.convertKind(); } @@ -62,11 +58,11 @@ export class Span implements ISpanTracker { } createCommandTracker(context: RequestContext, commandName: string) { - return new Span(context, SpanKind.Command, commandName, this); + return new Span(context, SpanKind.Command, commandName, this._id); } createCustomTracker(context: RequestContext, name: string, tags?: {[index:string]:string}): ITracker { - let span = new Span(context, SpanKind.Custom, name, this); + let span = new Span(context, SpanKind.Custom, name, this._id); span.trackAction(name, tags); return span; } @@ -144,6 +140,8 @@ export class Span implements ISpanTracker { this.endRequest(); if (this._tracker) { + Object.keys(this.tags).forEach(key => this._tracker.addTag(key, this.tags[key])); + this._tracker.finish(); this._tracker = null; } @@ -164,8 +162,12 @@ export class Span implements ISpanTracker { private endRequest() { if (this.action) { // for ignored requests like _servicedependency - let hasError = !!this.error || !this.context.response || this.context.response.statusCode && this.context.response.statusCode >= 400; - this.addTag("error", hasError ? "true" : "false"); + if (!this.error && this.kind === SpanKind.Request && this.context.response && this.context.response.statusCode && this.context.response.statusCode >= 400) { + this.error = new Error("Http error " + this.context.response.statusCode); + } + if(this.error) + this.addTag("error", this.error.toString()); + this.metrics.timing("vulcain_service_duration_ms", this.durationInMs, this.tags); } @@ -213,7 +215,6 @@ export class Span implements ISpanTracker { value = JSON.stringify(value); } this.tags[name] = value; - this._tracker && this._tracker.addTag(name, value); } catch (e) { this.context.logError(e); diff --git a/src/instrumentations/trackers/JaegerInstrumentation.ts b/src/instrumentations/trackers/JaegerInstrumentation.ts index 421bc75..efde8a8 100644 --- a/src/instrumentations/trackers/JaegerInstrumentation.ts +++ b/src/instrumentations/trackers/JaegerInstrumentation.ts @@ -6,6 +6,7 @@ import { ITrackerAdapter, IRequestTrackerFactory } from './index'; import { IRequestContext } from "../../pipeline/common"; import { TrackerId, SpanKind, ISpanTracker } from '../../instrumentations/common'; import { Service } from '../../globals/system'; +import * as URL from 'url'; export class JaegerInstrumentation implements IRequestTrackerFactory { @@ -16,16 +17,17 @@ export class JaegerInstrumentation implements IRequestTrackerFactory { jaegerAddress = "http://" + jaegerAddress; } if (!/:[0-9]+/.test(jaegerAddress)) { - jaegerAddress = jaegerAddress + ':9411'; + jaegerAddress = jaegerAddress + ':6832'; } - - Service.log.info(null, () => `Enabling Jaeger instrumentation at ${jaegerAddress}`); - - const sender = new UDPSender(); + + let url = URL.parse(jaegerAddress); + const sender = new UDPSender({host:url.hostname, port: url.port}); const tracer = new jaeger.Tracer(Service.fullServiceName, new jaeger.RemoteReporter(sender), new jaeger.RateLimitingSampler(1)); - + + Service.log.info(null, () => `Enabling Jaeger instrumentation at ${jaegerAddress}`); + return new JaegerInstrumentation(tracer); } return null; diff --git a/src/pipeline/requestContext.ts b/src/pipeline/requestContext.ts index 5e528b1..45463ec 100644 --- a/src/pipeline/requestContext.ts +++ b/src/pipeline/requestContext.ts @@ -163,13 +163,14 @@ export class RequestContext implements IRequestContext { }; } + const parentId = this.pipeline !== Pipeline.Event + ? (this.request && this.request.headers[VulcainHeaderNames.X_VULCAIN_PARENT_ID]) || null + : this.requestData.correlationId; + if(!this.requestData.correlationId) this.requestData.correlationId = (this.request && this.request.headers[VulcainHeaderNames.X_VULCAIN_CORRELATION_ID]) || Conventions.getRandomId(); - + if (this.pipeline !== Pipeline.Test) { - // For event we do not use parentId to chain traces. - // However all traces can be aggregated with the correlationId tag. - const parentId = (this.pipeline !== Pipeline.Event && this.request && this.request.headers[VulcainHeaderNames.X_VULCAIN_PARENT_ID]) || null; const trackerId: TrackerId = { spanId: parentId, correlationId: this.requestData.correlationId }; this._tracker = Span.createRequestTracker(this, trackerId); } From 008cb4839419b3fd60f293c669751718489e69a9 Mon Sep 17 00:00:00 2001 From: METGE Alain Date: Wed, 14 Mar 2018 16:43:02 +0100 Subject: [PATCH 05/10] update localadapter --- package.json | 2 +- src/bus/localAdapter.ts | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 82c4f8b..1b3e340 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta945", + "version": "2.0.0-beta946", "description": "Vulcain micro-service framework", "main": "lib/src/index.js", "scripts": { diff --git a/src/bus/localAdapter.ts b/src/bus/localAdapter.ts index 4d1e283..6679f81 100644 --- a/src/bus/localAdapter.ts +++ b/src/bus/localAdapter.ts @@ -1,39 +1,37 @@ import { IActionBusAdapter, IEventBusAdapter } from '../bus/busAdapter'; import { EventData } from "./messageBus"; import { RequestData } from "../pipeline/common"; +import * as RX from 'rxjs'; export class LocalAdapter implements IActionBusAdapter, IEventBusAdapter { - private eventHandler: (event: EventData) => void; - private commandHandler: (event: RequestData) => void; + private eventQueue: RX.Subject; + private taskQueue: RX.Subject; open() { + this.eventQueue = new RX.Subject(); + this.taskQueue = new RX.Subject(); return Promise.resolve(); } - stopReception() { } + stopReception() { + this.eventQueue = null; + this.taskQueue = null; + } sendEvent(domain: string, event: EventData) { - // console.log("Event: %j", event); - let self = this; - self.eventHandler && setTimeout(function () { - self.eventHandler(event); - }, (1)); + this.eventQueue && this.eventQueue.next(event); } - consumeEvents(domain: string, handler: (event: EventData) => void, queueName?:string) { - this.eventHandler = handler; + consumeEvents(domain: string, handler: (event: EventData) => void, queueName?: string) { + this.eventQueue && this.eventQueue.subscribe(handler); } publishTask(domain: string, serviceId: string, command: RequestData) { - let self = this; - self.commandHandler && setTimeout(function () { - // console.log("Running task: %j", command); - self.commandHandler(command); - }, (1)); + this.taskQueue && this.taskQueue.next(command); } consumeTask(domain: string, serviceId: string, handler: (event: RequestData) => void) { - this.commandHandler = handler; + this.taskQueue && this.taskQueue.subscribe(handler); } } \ No newline at end of file From 0274cae821d8b183b7a76f3cb5ac7ac8e15435fb Mon Sep 17 00:00:00 2001 From: METGE Alain Date: Wed, 14 Mar 2018 17:19:58 +0100 Subject: [PATCH 06/10] optimize send local events --- package.json | 2 +- src/pipeline/handlers/action/actionManager.ts | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 1b3e340..a808350 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta946", + "version": "2.0.0-beta947", "description": "Vulcain micro-service framework", "main": "lib/src/index.js", "scripts": { diff --git a/src/pipeline/handlers/action/actionManager.ts b/src/pipeline/handlers/action/actionManager.ts index 2b0454f..c090cde 100644 --- a/src/pipeline/handlers/action/actionManager.ts +++ b/src/pipeline/handlers/action/actionManager.ts @@ -198,9 +198,12 @@ export class CommandManager implements IManager { if (eventDef.factory) event = eventDef.factory(ctx, event); + if(eventDef.schema) + event.schema = eventDef.schema; + // Redispatch event in EVENT mode only if this is a new event schema - if (event && (source !== "EVENT" || (eventDef.schema && eventDef.schema !== (def).subscribeToSchema))) { - ctx.logInfo(() => `Sending event ${eventDef.schema || def.outputSchema}`); + if (event && (source !== "EVENT" || (event.schema !== (def).subscribeToSchema))) { + ctx.logInfo(() => `Sending event ${event.schema}`); this.messageBus.sendEvent(event); } @@ -272,16 +275,18 @@ export class CommandManager implements IManager { // Subscribe to events for a domain, a schema and an action // Get event stream for a domain let events = this.messageBus.getOrCreateEventQueue(def.subscribeToDomain || this.domain.name, def.distributionMode === "once" ? def.distributionKey: null); - events = events.filter(e => !e[MessageBus.LocalEventSymbol]); // already sent ? + events = events.filter(e => !e[MessageBus.LocalEventSymbol] && !!e.schema); // already sent ? // Filtered by schema if (def.subscribeToSchema !== '*') { events = events.filter(e => e.schema === def.subscribeToSchema); } + // Filtered by action if (def.subscribeToAction !== '*') { events = events.filter(e => !e.action || (e.action.toLowerCase() === def.subscribeToAction)); } + // And by custom filter if (def.filter) events = def.filter(events); From 9a386a2a272b25d0eedc9883b9a4e570a18a9a7d Mon Sep 17 00:00:00 2001 From: METGE Alain Date: Wed, 14 Mar 2018 18:11:27 +0100 Subject: [PATCH 07/10] fix event handler --- package.json | 2 +- src/pipeline/handlers/action/actionManager.ts | 11 ++++++----- src/pipeline/middlewares/normalizeDataMiddleware.ts | 7 +++++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index a808350..9f022e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta947", + "version": "2.0.0-beta948", "description": "Vulcain micro-service framework", "main": "lib/src/index.js", "scripts": { diff --git a/src/pipeline/handlers/action/actionManager.ts b/src/pipeline/handlers/action/actionManager.ts index c090cde..3aab2bd 100644 --- a/src/pipeline/handlers/action/actionManager.ts +++ b/src/pipeline/handlers/action/actionManager.ts @@ -286,7 +286,7 @@ export class CommandManager implements IManager { if (def.subscribeToAction !== '*') { events = events.filter(e => !e.action || (e.action.toLowerCase() === def.subscribeToAction)); } - + // And by custom filter if (def.filter) events = def.filter(events); @@ -312,16 +312,17 @@ export class CommandManager implements IManager { let error; try { let res = await handler[info.methodName](evt.value); - if (res !== undefined) - evt.value = res; + evt.value = res; } catch (e) { error = (e instanceof CommandRuntimeError && e.error) ? e.error : e; ctx.logError(error, () => `Error with event handler ${info.handler.name} event : ${evt}`); } - let e = this.emitEvent("EVENT", ctx, def, evt.value, error); - MessageBus.emitLocalEvent(def.name, e); + if (evt.value) { + let e = this.emitEvent("EVENT", ctx, def, evt.value, error); + MessageBus.emitLocalEvent(def.name, e); + } } finally { ctx.dispose(); diff --git a/src/pipeline/middlewares/normalizeDataMiddleware.ts b/src/pipeline/middlewares/normalizeDataMiddleware.ts index fe1b891..4ef7489 100644 --- a/src/pipeline/middlewares/normalizeDataMiddleware.ts +++ b/src/pipeline/middlewares/normalizeDataMiddleware.ts @@ -29,7 +29,7 @@ export class NormalizeDataMiddleware extends VulcainMiddleware { return; } - try { + try { await super.invoke(ctx); if (!ctx.response) { ctx.response = new HttpResponse({}); @@ -39,7 +39,10 @@ export class NormalizeDataMiddleware extends VulcainMiddleware { if (!(e instanceof ApplicationError)) { e = new ApplicationError(e.message, 500); } - ctx.logError(e, () => "Request has error"); + if (!(e instanceof ApplicationError) || e.statusCode !== 405) { + // Don't pollute logs with incorect request + ctx.logError(e, () => "Request has error"); + } ctx.response = HttpResponse.createFromError(e); } From 3177722a30444b7679783ca78cc4eaa37125adcc Mon Sep 17 00:00:00 2001 From: METGE Alain Date: Thu, 15 Mar 2018 19:44:57 +0100 Subject: [PATCH 08/10] optimize --- package-lock.json | 221 ++++++++---------- package.json | 32 +-- src/application.ts | 70 +++--- src/bus/messageBus.ts | 4 +- .../sources/httpConfigurationSource.ts | 3 +- .../sources/vulcainConfigurationSource.ts | 1 - src/di/containers.ts | 9 +- src/globals/settings.ts | 1 - src/globals/system.ts | 21 +- src/pipeline/handlerProcessor.ts | 1 - src/pipeline/handlers/action/actionManager.ts | 30 +-- src/pipeline/handlers/query/queryManager.ts | 8 +- .../middlewares/authenticationMiddleware.ts | 2 +- .../middlewares/normalizeDataMiddleware.ts | 86 +------ src/pipeline/requestContext.ts | 87 ++++++- src/pipeline/testContext.ts | 17 +- src/schemas/validator.ts | 24 +- src/security/securityContext.ts | 4 +- src/security/services/apiKeyService.ts | 4 +- test/schema/validateData.spec.ts | 34 +-- 20 files changed, 320 insertions(+), 339 deletions(-) diff --git a/package-lock.json b/package-lock.json index ab7405f..db75e9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta937", + "version": "2.0.0-beta948", "lockfileVersion": 1, "requires": true, "dependencies": { "@types/amqplib": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.5.6.tgz", - "integrity": "sha512-lR3b0F+OJYxkU41ETDXUXYPuIQy6HT2mwGi0h8sYfTupbzsxR280ixfkgv6z3/BEhB+heuGpG124dHwZhpU61Q==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.5.7.tgz", + "integrity": "sha512-1PIJiT+6yRq8eJG3F5bqilBtwihv9b0+FRNaWq4br+RrxVogHRJoWpkh+DTYtLVN+q+WgSN98L97Yk4DEjINWA==", "dev": true, "requires": { "@types/bluebird": "3.5.20", @@ -22,9 +22,9 @@ "dev": true }, "@types/bson": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-1.0.6.tgz", - "integrity": "sha512-v7N8qcTGiYhLRyi+Y69R3tPC4GLqByCg3NC2EO6PciC166O9dNhjFPoXeMePtZ+0f+/O2xLDWXs5BLnRfcBaBA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-1.0.7.tgz", + "integrity": "sha512-RehFYAhF6h+dO8112VwjM8zq63Fh9zukvqC3NlWWGfVa+0StuSMu4xwa54M2CRFrNGvgKnqHLTQLC7P20AlMYg==", "dev": true, "requires": { "@types/node": "9.4.0" @@ -64,12 +64,12 @@ "dev": true }, "@types/mongodb": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-2.2.20.tgz", - "integrity": "sha512-EcsDWWppvtZ/C70+oDxYmbMNtckQ4dD4v60vI6HBamWuDNrblUEMvI6LDicpbE6Mpt6nhq/T7qzUOsy9cRgvYw==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.0.7.tgz", + "integrity": "sha512-A2MKmbS+HyO+S/q8otc+Y0ekK5n85ZNjNz3XVYOMPf3hxwS+N00fY8Wz4LMjAy/oPHRy7TMnkzUQWGME53d8sg==", "dev": true, "requires": { - "@types/bson": "1.0.6", + "@types/bson": "1.0.7", "@types/events": "1.1.0", "@types/node": "9.4.0" } @@ -134,8 +134,7 @@ "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" }, "async": { "version": "0.9.2", @@ -170,9 +169,9 @@ "dev": true }, "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.3.tgz", + "integrity": "sha512-MsAhsUW1GxCdgYSO6tAfZrNapmUKk7mWx/k5mFY/A1gBtkaCaNapTg+FExCw1r9yeaZhqx/xPg43xgTFH6KL5w==" }, "base64url": { "version": "2.0.0", @@ -253,15 +252,15 @@ } }, "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "bson": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz", - "integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw=" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.6.tgz", + "integrity": "sha512-D8zmlb46xfuK2gGvKmUjIklQEouN2nQ0LEHHeZ/NoHM2LDiMk2EYzZ5Ntw/Urk+bgMDosOZxaRzXxvhI5TcAVQ==" }, "buffer-equal-constant-time": { "version": "1.0.1", @@ -273,11 +272,6 @@ "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz", "integrity": "sha1-JrOIXRD6E9t/wBquOquHAZngEkw=" }, - "buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" - }, "bufrw": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/bufrw/-/bufrw-1.2.1.tgz", @@ -303,7 +297,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", - "dev": true, "requires": { "assertion-error": "1.1.0", "check-error": "1.0.2", @@ -328,8 +321,7 @@ "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" }, "color-convert": { "version": "1.9.1", @@ -412,7 +404,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, "requires": { "type-detect": "4.0.8" } @@ -463,11 +454,6 @@ "xtend": "4.0.1" } }, - "es6-promise": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", - "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -537,8 +523,7 @@ "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" }, "getpass": { "version": "0.1.7", @@ -570,11 +555,11 @@ } }, "graphql": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.12.3.tgz", - "integrity": "sha512-Hn9rdu4zacplKXNrLCvR8YFiTGnbM4Zw/UH8FDmzBDsH7ou40lSNH4tIlsxcYnz2TGNVJCpu1WxCM23yd6kzhA==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.13.1.tgz", + "integrity": "sha512-awNp3LTrQ7dJDSX3p3PBuxNDC+WFSOrWeV6+l4Xeh2PQJVOFyQ9SZPonXRz2WZc7aIxLZsf2nDZuuuc0qyEq/A==", "requires": { - "iterall": "1.1.3" + "iterall": "1.2.2" } }, "growl": { @@ -671,6 +656,11 @@ "xtend": "4.0.1" } }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, "is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", @@ -697,18 +687,18 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "iterall": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", - "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", + "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" }, "jaeger-client": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/jaeger-client/-/jaeger-client-3.8.0.tgz", - "integrity": "sha1-JVkvlCIeLyXT1ZlzU1Qxrif2zhc=", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/jaeger-client/-/jaeger-client-3.10.0.tgz", + "integrity": "sha1-rQ6DDq16q60mAaUqfkY/DZQrAJE=", "requires": { "node-int64": "0.4.0", "opentracing": "0.13.0", - "thriftrw": "3.11.1", + "thriftrw": "3.11.2", "xorshift": "0.2.1" } }, @@ -750,9 +740,9 @@ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" }, "jsonwebtoken": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.1.1.tgz", - "integrity": "sha512-+ijVOtfLMlCII8LJkvabaKX3+8tGrGjiCTfzoed2D1b/ebKTO1hIYBQUJHbd9dJ9Fa4kH+dhYEd1qDwyzDLUUw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.2.0.tgz", + "integrity": "sha512-1Wxh8ADP3cNyPl8tZ95WtraHXCAyXupgc0AhMHjU9er98BV+UcKsO7OJUjfhIu0Uba9A40n1oSx8dbJYrm+EoQ==", "requires": { "jws": "3.1.4", "lodash.includes": "4.3.0", @@ -898,15 +888,15 @@ } }, "mocha": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.0.tgz", - "integrity": "sha512-ukB2dF+u4aeJjc6IGtPNnJXfeby5d4ZqySlIBT0OEyva/DrMjVm5HkQxKnHDLKEfEQBsEnwTg9HHhtPHJdTd8w==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.4.tgz", + "integrity": "sha512-nMOpAPFosU1B4Ix1jdhx5e3q7XO55ic5a8cgYvW27CequcEY+BabS0kUVL1Cw1V5PuVHZWeNRWFLmEPexo79VA==", "dev": true, "requires": { - "browser-stdout": "1.3.0", + "browser-stdout": "1.3.1", "commander": "2.11.0", "debug": "3.1.0", - "diff": "3.3.1", + "diff": "3.5.0", "escape-string-regexp": "1.0.5", "glob": "7.1.2", "growl": "1.10.3", @@ -930,6 +920,12 @@ "ms": "2.0.0" } }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -948,55 +944,24 @@ } }, "moment": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", - "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz", + "integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ==" }, "mongodb": { - "version": "2.2.34", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.34.tgz", - "integrity": "sha1-o09Zu+thdUrsQy3nLD/iFSakTBo=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.4.tgz", + "integrity": "sha512-90YIIs7A4ko4kCGafxxXj3foexCAlJBC0YLwwIKgSLoE7Vni2IqUMz6HSsZ3zbXOfR1KWtxfnc0RyAMAY/ViLg==", "requires": { - "es6-promise": "3.2.1", - "mongodb-core": "2.1.18", - "readable-stream": "2.2.7" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", - "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "5.1.1" - } - } + "mongodb-core": "3.0.4" } }, "mongodb-core": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.18.tgz", - "integrity": "sha1-TEYTm986HwMt7ZHbSfOO7AFlkFA=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.0.4.tgz", + "integrity": "sha512-OTH267FjfwBdEufSnrgd+u8HuLWRuQ6p8DR0XirPl2BdlLEMh4XwjJf1RTlruILp5p2m1w8dDC8rCxibC3W8qQ==", "requires": { - "bson": "1.0.4", + "bson": "1.0.6", "require_optional": "1.0.1" } }, @@ -1068,8 +1033,7 @@ "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" }, "pinkie": { "version": "2.0.4", @@ -1090,9 +1054,9 @@ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "prom-client": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-10.2.2.tgz", - "integrity": "sha512-d3qCBK41qZx00/WVzWOX4tau9FinCztqaECZiGuMI5vGYD//5VSdKMOZPRQKjVh5RkI4Ex98DI0YPsoFnEo1QQ==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.0.0.tgz", + "integrity": "sha512-UM4uYDwmA7x9yTq+AZcL4lU/XF11RkbQWbIouFaVMLxdV4qBB5CEmEosQlR1lGvduBuS1IWonHFh1WBtFSoZ3A==", "requires": { "tdigest": "0.1.1" } @@ -1240,9 +1204,9 @@ } }, "rxjs": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz", - "integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==", + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.7.tgz", + "integrity": "sha512-Hxo2ac8gRQjwjtKgukMIwBRbq5+KAeEV5hXM4obYBOAghev41bDQWgFH4svYiU9UnQ5kNww2LgfyBdevCd2HXA==", "requires": { "symbol-observable": "1.0.1" } @@ -1327,9 +1291,9 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "swagger-ui-dist": { - "version": "3.9.3", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.9.3.tgz", - "integrity": "sha1-yrqR6FUNfSRkoIRWvZNtfEMPDdM=" + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.12.1.tgz", + "integrity": "sha1-uIeIiPAM8BPvlf9xfluRK9SoTjo=" }, "symbol-observable": { "version": "1.0.1", @@ -1345,9 +1309,9 @@ } }, "thriftrw": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/thriftrw/-/thriftrw-3.11.1.tgz", - "integrity": "sha1-Wi9RZdZluxleZl5bW5+Il9rCOsw=", + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/thriftrw/-/thriftrw-3.11.2.tgz", + "integrity": "sha512-3iCowlHgCEXjabCkurHTaECb2+U0V+NzqBmwfKHn8fzJJwXd/oDo7Wh6Vs0kaESN7YNJMRPC8ObL3AfQ1gxKmQ==", "requires": { "bufrw": "1.2.1", "error": "7.0.2", @@ -1442,13 +1406,12 @@ "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" }, "typescript": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.1.tgz", - "integrity": "sha512-bqB1yS6o9TNA9ZC/MJxM0FZzPnZdtHj0xWK/IZ5khzVqdpGul/R/EIiHRgFXlwTD7PSIaYVnGKq1QgMCu2mnqw==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz", + "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", "dev": true }, "unirest": { @@ -1477,9 +1440,9 @@ "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" }, "validator": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.0.tgz", - "integrity": "sha512-ftkCYp/7HrGdybVCuwSje07POAd93ksZJpb5GVDBzm8SLKIm3QMJcZugb5dOJsONBoWhIXl0jtoGHTyou3DAgA==" + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==" }, "verror": { "version": "1.10.0", @@ -1515,19 +1478,21 @@ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "zipkin": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/zipkin/-/zipkin-0.10.1.tgz", - "integrity": "sha512-iOGibktOLzLVyiaJQ73O3bn+J0vfoM4oeABqBKkdWntsI9eK+h+/MGaGuelJnk9vMW8Pc6bHEOyQwEz1bBbTLQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/zipkin/-/zipkin-0.12.0.tgz", + "integrity": "sha512-QguH/UQt/ofUVcTJLW7qdyi7iEo71MJJbKg/6d/m3ptvxhm5gDVf/KVuCEfzxxzzWGSKtX+c+qACvowsPkNjuQ==", "requires": { - "base64-js": "1.2.1", + "base64-js": "1.2.3", + "is-promise": "2.1.0", "network-address": "1.1.2" } }, "zipkin-transport-http": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/zipkin-transport-http/-/zipkin-transport-http-0.10.1.tgz", - "integrity": "sha512-lfjp1+0EZ+WnTq6SiyasrK0lFGhgV4xtt4OCWievOON+7eQny7zeVzC6nYaBYH4Jx0Nif3475dpNxKUCOy8SpQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/zipkin-transport-http/-/zipkin-transport-http-0.12.0.tgz", + "integrity": "sha512-65v7qSgWfThsFn/cY8+Tc4ed2ZChM5DM3MFj8Y00naFpptl+kgm1e42uU/6DlyyCyX4CtNGn4G3rk4GGSrNORA==", "requires": { + "chai": "4.1.2", "node-fetch": "1.7.3" } } diff --git a/package.json b/package.json index 9f022e8..e685e19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta948", + "version": "2.0.0-beta949", "description": "Vulcain micro-service framework", "main": "lib/src/index.js", "scripts": { @@ -23,34 +23,34 @@ "license": "Apache-2.0", "typings": "lib/src/index.d.ts", "devDependencies": { - "@types/amqplib": "^0.5.6", + "@types/amqplib": "^0.5.7", "@types/chai": "^4.1.0", "@types/fast-stats": "0.0.29", "@types/jsonwebtoken": "^7.2.5", "@types/mocha": "^2.2.46", - "@types/mongodb": "^2.2.18", + "@types/mongodb": "^3.0.7", "chai": "^4.1.2", - "mocha": "^5.0.0", + "mocha": "^5.0.4", "tslint": "^5.9.1", - "typescript": "^2.6.2" + "typescript": "^2.7.2" }, "dependencies": { "amqplib": "^0.5.2", "fast-stats": "0.0.3", - "graphql": "^0.12.3", - "jaeger-client": "^3.7.0", - "jsonwebtoken": "^8.1.0", - "moment": "^2.20.1", - "mongodb": "^2.2.34", - "prom-client": "^10.2.2", + "graphql": "^0.13.1", + "jaeger-client": "^3.10.0", + "jsonwebtoken": "^8.2.0", + "moment": "^2.21.0", + "mongodb": "^3.0.4", + "prom-client": "^11.0.0", "reflect-metadata": "^0.1.3", "router": "^1.3.2", - "rxjs": "^5.5.6", - "swagger-ui-dist": "^3.9.0", + "rxjs": "^5.5.7", + "swagger-ui-dist": "^3.12.1", "unirest": "^0.5.0", "uuid": "^3.0.1", - "validator": "^9.2.0", - "zipkin": "^0.10.1", - "zipkin-transport-http": "^0.10.1" + "validator": "^9.4.1", + "zipkin": "^0.12.0", + "zipkin-transport-http": "^0.12.0" } } diff --git a/src/application.ts b/src/application.ts index 30086eb..2e24f74 100644 --- a/src/application.ts +++ b/src/application.ts @@ -25,6 +25,7 @@ import { ActionHandler } from './pipeline/handlers/action/annotations'; import { GraphQLActionHandler } from './graphql/graphQLHandler'; import { GraphQLAdapter } from "./graphql/graphQLAdapter"; import { HystrixSSEStream as hystrixStream } from './commands/http/hystrixSSEStream'; +import { HandlerProcessor } from '.'; const vulcainExecutablePath = __dirname; const applicationPath = Path.dirname(module.parent.parent.filename); @@ -38,6 +39,7 @@ const applicationPath = Path.dirname(module.parent.parent.filename); */ export class Application { private _domain: Domain; + private _initialized = false; public useMongoProvider(address: string) { this.container.useMongoProvider(address); @@ -95,18 +97,26 @@ export class Application { Service.setDomainName(this.domainName); this._container = this._container || new Container(); - this.container.registerHTTPEndpoint("GET", Conventions.instance.defaultHystrixPath, hystrixStream.getHandler()); } - - private async init() { + + /** + * Only use it for testing. Used start instead + */ + public async init() { + if (this._initialized) + return; + + this._initialized = true; await DynamicConfiguration.init().startPolling(); - + Service.log.info(null, () => "Starting application"); - + this._container.injectInstance(this, DefaultServiceNames.Application); this._domain = new Domain(this.domainName, this._container); this._container.injectInstance(this._domain, DefaultServiceNames.Domain); + this.container.registerHTTPEndpoint("GET", Conventions.instance.defaultHystrixPath, hystrixStream.getHandler()); + process.on('unhandledRejection', (reason, p) => { Service.log.info(null, () => `Unhandled Rejection at ${p} reason ${reason}")`); }); @@ -124,6 +134,32 @@ export class Application { commandBus.stopReception(); } }); + + let local = new LocalAdapter(); + let eventBus = this.container.get(DefaultServiceNames.EventBusAdapter, true); + if (!eventBus) { + this.container.injectInstance(local, DefaultServiceNames.EventBusAdapter); + eventBus = local; + } + let commandBus = this.container.get(DefaultServiceNames.ActionBusAdapter, true); + if (!commandBus) { + this.container.injectInstance(local, DefaultServiceNames.ActionBusAdapter); + commandBus = local; + } + + this.registerComponents(); + Preloader.instance.runPreloads(this.container, this._domain); + + await eventBus.open(); + await commandBus.open(); + + let scopes = this.container.get(DefaultServiceNames.ScopesDescriptor); + this.defineScopeDescriptions(scopes); + + let descriptors = this.container.get(DefaultServiceNames.ServiceDescriptors); + descriptors.getDescriptions(); // ensures handlers table is created + + this.container.injectSingleton(HandlerProcessor, DefaultServiceNames.HandlerProcessor ); } /** @@ -151,30 +187,6 @@ export class Application { try { await this.init(); - let local = new LocalAdapter(); - let eventBus = this.container.get(DefaultServiceNames.EventBusAdapter, true); - if (!eventBus) { - this.container.injectInstance(local, DefaultServiceNames.EventBusAdapter); - eventBus = local; - } - let commandBus = this.container.get(DefaultServiceNames.ActionBusAdapter, true); - if (!commandBus) { - this.container.injectInstance(local, DefaultServiceNames.ActionBusAdapter); - commandBus = local; - } - - this.registerComponents(); - Preloader.instance.runPreloads(this.container, this._domain); - - await eventBus.open(); - await commandBus.open(); - - let scopes = this.container.get(DefaultServiceNames.ScopesDescriptor); - this.defineScopeDescriptions(scopes); - - let descriptors = this.container.get(DefaultServiceNames.ServiceDescriptors); - descriptors.getDescriptions(); // ensures handlers table is created - let server = new VulcainServer(this.domain.name, this._container); server.start(port); } diff --git a/src/bus/messageBus.ts b/src/bus/messageBus.ts index 146bba7..832418d 100644 --- a/src/bus/messageBus.ts +++ b/src/bus/messageBus.ts @@ -15,8 +15,8 @@ export interface EventData extends RequestData { source: string; error?: string; userContext: UserContextData; - startedAt: string; - completedAt?: string; + startedAt: number; + completedAt?: number; status: string; } diff --git a/src/configurations/sources/httpConfigurationSource.ts b/src/configurations/sources/httpConfigurationSource.ts index 5078183..b39cafa 100644 --- a/src/configurations/sources/httpConfigurationSource.ts +++ b/src/configurations/sources/httpConfigurationSource.ts @@ -3,7 +3,6 @@ import { AbstractRemoteSource } from "./abstractRemoteSource"; import { Service } from "../../globals/system"; const rest = require('unirest'); -const moment = require('moment'); export class HttpConfigurationSource extends AbstractRemoteSource { protected lastUpdate: string; @@ -49,7 +48,7 @@ export class HttpConfigurationSource extends AbstractRemoteSource { values = new Map(); let data = response.body; data.value && data.value.forEach(cfg => values.set(cfg.key, cfg)); - self.lastUpdate = moment.utc().format(); + self.lastUpdate = Service.nowAsString(); self.mergeChanges(values); } } diff --git a/src/configurations/sources/vulcainConfigurationSource.ts b/src/configurations/sources/vulcainConfigurationSource.ts index 95212aa..33e8aad 100644 --- a/src/configurations/sources/vulcainConfigurationSource.ts +++ b/src/configurations/sources/vulcainConfigurationSource.ts @@ -2,7 +2,6 @@ import { HttpConfigurationSource } from './httpConfigurationSource'; import { DataSource } from '../abstractions'; import { Service } from '../../globals/system'; const rest = require('unirest'); -const moment = require('moment'); export class VulcainConfigurationSource extends HttpConfigurationSource { diff --git a/src/di/containers.ts b/src/di/containers.ts index a54ebee..2fc2b9a 100644 --- a/src/di/containers.ts +++ b/src/di/containers.ts @@ -169,8 +169,13 @@ export class Container implements IContainer { // Insert always in first position // so 'get' take the last inserted private addResolver(name: string, resolver: IResolver) { - let list = this.resolvers.get(name) || []; - list.unshift(resolver); + let list = this.resolvers.get(name); + if (!list) { + list = [resolver]; + } + else { + list.unshift(resolver); + } this.resolvers.set(name, list); } diff --git a/src/globals/settings.ts b/src/globals/settings.ts index 8c2f72a..ab29595 100644 --- a/src/globals/settings.ts +++ b/src/globals/settings.ts @@ -1,5 +1,4 @@ import { VulcainLogger } from './../log/vulcainLogger'; -import * as moment from 'moment'; import * as fs from 'fs'; import { Conventions } from '../utils/conventions'; import { DefaultServiceNames } from '../di/annotations'; diff --git a/src/globals/system.ts b/src/globals/system.ts index 733533c..16fc599 100644 --- a/src/globals/system.ts +++ b/src/globals/system.ts @@ -1,6 +1,5 @@ import { CryptoHelper } from '../utils/crypto'; import { VulcainLogger } from './../log/vulcainLogger'; -import * as moment from 'moment'; import * as fs from 'fs'; import { VulcainManifest } from './manifest'; import { Conventions } from '../utils/conventions'; @@ -39,7 +38,8 @@ export class Service { private static _stubManager: IStubManager; private static _serviceStatus: ServiceStatus = ServiceStatus.Starting; private static _statusTimer: NodeJS.Timer; - + private static _fullServiceName: string; + public static setDomainName(name: string) { if(name) Service._domainName = name; @@ -123,7 +123,7 @@ export class Service { * @memberOf System */ static nowAsString() { - return moment.utc().format(); + return new Date(Date.now()).toUTCString(); } /** @@ -284,10 +284,7 @@ export class Service { static get serviceName() { if (!Service._serviceName) { let env = process.env[Conventions.instance.ENV_SERVICE_NAME]; - if (env) - Service._serviceName = env; - else - return null; + Service._serviceName = env || "no-name"; } return Service._serviceName; } @@ -303,16 +300,16 @@ export class Service { static get serviceVersion() { if (!Service._serviceVersion) { let env = process.env[Conventions.instance.ENV_SERVICE_VERSION]; - if (env) - Service._serviceVersion = env; - else - return null; + Service._serviceVersion = env || "1.0"; } return Service._serviceVersion; } static get fullServiceName() { - return this.serviceName + "-" + this.serviceVersion; + if (!this._fullServiceName) { + this._fullServiceName = this.serviceName + "-" + this.serviceVersion; + } + return this._fullServiceName; } /** diff --git a/src/pipeline/handlerProcessor.ts b/src/pipeline/handlerProcessor.ts index f3670cd..9662a82 100644 --- a/src/pipeline/handlerProcessor.ts +++ b/src/pipeline/handlerProcessor.ts @@ -13,7 +13,6 @@ import { QueryManager } from "./handlers/query/queryManager"; import { IManager } from "./handlers/definitions"; import { ActionDefinition } from "./handlers/action/definitions"; -@Injectable(LifeTime.Singleton, DefaultServiceNames.HandlerProcessor) export class HandlerProcessor { private actionManager: CommandManager; private queryManager: QueryManager; diff --git a/src/pipeline/handlers/action/actionManager.ts b/src/pipeline/handlers/action/actionManager.ts index 3aab2bd..a41c1bc 100644 --- a/src/pipeline/handlers/action/actionManager.ts +++ b/src/pipeline/handlers/action/actionManager.ts @@ -22,10 +22,10 @@ import { Utils } from '../utils'; export interface AsyncTaskData extends RequestData { status?: "Error" | "Success" | "Pending" | "Running"; taskId?: string; - submitAt?: string; - startedAt?: string; + submitAt?: number; + startedAt?: number; userContext?: UserContextData; - completedAt?: string; + completedAt?: number; } export interface ActionResult { @@ -63,7 +63,7 @@ export class CommandManager implements IManager { this.subscribeToEvents(); } - private async validateRequestData(ctx: RequestContext, info:Handler, command: RequestData, skipValidation: boolean) { + private validateRequestData(ctx: RequestContext, info:Handler, command: RequestData, skipValidation: boolean) { let errors; let inputSchema = info.definition.inputSchema; if (inputSchema && inputSchema !== "none") { @@ -81,14 +81,14 @@ export class CommandManager implements IManager { } } if (!skipValidation) { - errors = await schema.validate(ctx, command.params); + errors = schema.validate(ctx, command.params); } } if (!skipValidation && !errors) { // Search if a method naming validate exists let methodName = 'validate' + inputSchema; - errors = info.handler[methodName] && await info.handler[methodName](command.params, command.action); + errors = info.handler[methodName] && info.handler[methodName](command.params, command.action); } } @@ -102,7 +102,7 @@ export class CommandManager implements IManager { action: ctx.requestData.action, schema: ctx.requestData.schema, source: Service.fullServiceName, - startedAt: Service.nowAsString(), + startedAt: Date.now(), value: result && Utils.obfuscateSensibleData(this.domain, ctx.container, result), error: error && error.message, userContext: ctx.user.getUserContext(), @@ -118,7 +118,7 @@ export class CommandManager implements IManager { try { let skipValidation = def.skipDataValidation || (def.name === "delete" && def.skipDataValidation === undefined); - let errors = await this.validateRequestData(ctx, info, command, skipValidation); + let errors = this.validateRequestData(ctx, info, command, skipValidation); if (errors && Object.keys(errors).length > 0) { throw new BadRequestError("Validation errors", errors); } @@ -156,7 +156,7 @@ export class CommandManager implements IManager { else { // Asynchronous task let pendingTask: AsyncTaskData = Object.assign({}, ctx.getRequestDataObject(), { - submitAt: Service.nowAsString(), + submitAt: Date.now(), status: "Pending" }); @@ -193,7 +193,7 @@ export class CommandManager implements IManager { if ((eventDef.mode === EventNotificationMode.successOnly && !error) || eventDef.mode === EventNotificationMode.always) { let event = this.createEvent(ctx, error ? "Error" : "Success", result, error); - event.completedAt = Service.nowAsString(); + event.completedAt = Date.now(); if (eventDef.factory) event = eventDef.factory(ctx, event); @@ -213,7 +213,7 @@ export class CommandManager implements IManager { async processAsyncTask(command: AsyncTaskData) { let ctx = new RequestContext(this.container, Pipeline.AsyncTask, command); - ctx.setSecurityManager(command.userContext); + ctx.setSecurityContext(command.userContext); let processor = this.container.get(DefaultServiceNames.HandlerProcessor); let info = processor.getHandlerInfo(ctx.container, command.schema, command.action); @@ -229,7 +229,7 @@ export class CommandManager implements IManager { try { let res; command.status = "Running"; - command.startedAt = Service.nowAsString(); + command.startedAt = Date.now(); if (taskManager) await taskManager.updateTask(command); @@ -252,7 +252,7 @@ export class CommandManager implements IManager { command.status = "Error"; } finally { - command.completedAt = Service.nowAsString(); + command.completedAt = Date.now(); if (taskManager) await taskManager.updateTask(command); ctx.dispose(); @@ -299,7 +299,7 @@ export class CommandManager implements IManager { try { try { ctx.requestTracker.trackAction(evt.vulcainVerb); - ctx.setSecurityManager(evt.userContext); + ctx.setSecurityContext(evt.userContext); handler = ctx.container.resolve(info.handler); handler.context = ctx; handler.event = evt; @@ -316,7 +316,7 @@ export class CommandManager implements IManager { } catch (e) { error = (e instanceof CommandRuntimeError && e.error) ? e.error : e; - ctx.logError(error, () => `Error with event handler ${info.handler.name} event : ${evt}`); + ctx.logError(error, () => `Error with event handler ${info.handler.name} event : ${JSON.stringify(evt)}`); } if (evt.value) { diff --git a/src/pipeline/handlers/query/queryManager.ts b/src/pipeline/handlers/query/queryManager.ts index d8c7e45..711de5f 100644 --- a/src/pipeline/handlers/query/queryManager.ts +++ b/src/pipeline/handlers/query/queryManager.ts @@ -29,7 +29,7 @@ export class QueryManager implements IManager { constructor(public container: IContainer) { } - private async validateRequestData(ctx: RequestContext, info: Handler, query) { + private validateRequestData(ctx: RequestContext, info: Handler, query) { let errors; let inputSchema = info.definition.inputSchema; if (inputSchema && inputSchema !== "none") { @@ -40,13 +40,13 @@ export class QueryManager implements IManager { // Custom binding if any query.params = schema.coerce(query.params); - errors = await schema.validate(ctx, query.params); + errors = schema.validate(ctx, query.params); } if (!errors) { // Search if a method naming validate[Async] exists let methodName = 'validate' + inputSchema; - errors = info.handler[methodName] && await info.handler[methodName](query.params, query.action); + errors = info.handler[methodName] && info.handler[methodName](query.params, query.action); } } return errors; @@ -57,7 +57,7 @@ export class QueryManager implements IManager { let logger = this.container.get(DefaultServiceNames.Logger); try { - let errors = await this.validateRequestData(ctx, info, query); + let errors = this.validateRequestData(ctx, info, query); if (errors && Object.keys(errors).length > 0) { throw new BadRequestError("Validation errors", errors); } diff --git a/src/pipeline/middlewares/authenticationMiddleware.ts b/src/pipeline/middlewares/authenticationMiddleware.ts index 006307b..3f2e5a5 100644 --- a/src/pipeline/middlewares/authenticationMiddleware.ts +++ b/src/pipeline/middlewares/authenticationMiddleware.ts @@ -7,7 +7,7 @@ export class AuthenticationMiddleware extends VulcainMiddleware { async invoke(ctx: RequestContext) { let tenantPolicy = ctx.container.get(DefaultServiceNames.TenantPolicy); - ctx.setSecurityManager(tenantPolicy.resolveTenant(ctx)); + ctx.setSecurityContext(tenantPolicy.resolveTenant(ctx)); await ctx.user.process(ctx); diff --git a/src/pipeline/middlewares/normalizeDataMiddleware.ts b/src/pipeline/middlewares/normalizeDataMiddleware.ts index 4ef7489..7a4b5d1 100644 --- a/src/pipeline/middlewares/normalizeDataMiddleware.ts +++ b/src/pipeline/middlewares/normalizeDataMiddleware.ts @@ -21,7 +21,7 @@ export class NormalizeDataMiddleware extends VulcainMiddleware { // $pageSize, $page async invoke(ctx: RequestContext) { try { - this.populateData(ctx); + ctx.normalize(); } catch (e) { ctx.logError(e, () => "Bad request format for " + ctx.request.url.pathname); @@ -54,88 +54,4 @@ export class NormalizeDataMiddleware extends VulcainMiddleware { ctx.response.content.meta.correlationId = ctx.requestData.correlationId; } } - - private populateData(ctx: RequestContext) { - let action: string; - let schema: string; - - const url = ctx.request.url; - const body = ctx.request.body; - - ctx.requestData.body = body; - - // Try to get schema and action from path - let schemaAction = url.pathname.substr(Conventions.instance.defaultUrlprefix.length + 1); - if (schemaAction) { - if (schemaAction[schemaAction.length - 1] === '/') - schemaAction = schemaAction.substr(0, schemaAction.length - 1); - } - - // Split schema and action (schema is optional) - if (schemaAction) { - if (schemaAction.indexOf('.') >= 0) { - let parts = schemaAction.split('.'); - schema = parts[0]; - action = parts[1]; - } - else { - action = schemaAction; - } - } - // Schema and action can be in the body - if (body) { - // Body contains only data -> create a new action object - if (!ctx.requestData.body.action && !ctx.requestData.body.params && !ctx.requestData.body.schema) { - ctx.requestData.params = ctx.requestData.body; - } - else { - action = ctx.requestData.body.action || action; - schema = ctx.requestData.body.schema || schema; - } - } - else { - url.query && Object.keys(url.query).forEach(k => { - if (k[0] !== "$") { - ctx.requestData.params = ctx.requestData.params || {}; - ctx.requestData.params[k] = url.query[k]; - } - }); - } - - ctx.requestData.params = ctx.requestData.params || {}; - - // Or can be forced in the url query - if (url.query["$action"]) - action = url.query["$action"]; - if (url.query["_schema"]) - schema = url.query["_schema"]; - - ctx.requestData.action = action || (!body && "all") || null; - ctx.requestData.schema = schema; - - // if (ctx.request.verb === "GET" && ctx.requestData.action !== "get") { - ctx.requestData.page = 0; - ctx.requestData.pageSize = 20; - //} - // Normalize option values - Object.keys(url.query).forEach(name => { - try { - switch (name.toLowerCase()) { - case "$page": - ctx.requestData.page = (url.query["$page"] && parseInt(url.query["$page"])) || ctx.requestData.page; - break; - case "$pagesize": - ctx.requestData.pageSize = (url.query[name] && parseInt(url.query[name])) || ctx.requestData.pageSize; - break; - case "$query": - ctx.requestData.params = url.query["$query"] && JSON.parse(url.query["$query"]); - break; - } - } - catch (ex) {/*ignore*/ } - }); - - ctx.requestData.vulcainVerb = ctx.requestData.schema ? `${ctx.requestData.schema}.${ctx.requestData.action}` : ctx.requestData.action; - ctx.requestTracker.trackAction(ctx.requestData.vulcainVerb); - } } \ No newline at end of file diff --git a/src/pipeline/requestContext.ts b/src/pipeline/requestContext.ts index 45463ec..9b1c814 100644 --- a/src/pipeline/requestContext.ts +++ b/src/pipeline/requestContext.ts @@ -12,6 +12,7 @@ import { ISpanRequestTracker, DummySpanTracker, TrackerId } from '../instrumenta import { Span } from '../instrumentations/span'; import { Conventions } from '../utils/conventions'; import { Service, ServiceStatus } from '../globals/system'; +import { URL } from 'url'; export class VulcainHeaderNames { static X_VULCAIN_TENANT = "x-vulcain-tenant"; @@ -220,7 +221,7 @@ export class RequestContext implements IRequestContext { return this._securityManager; } - setSecurityManager(tenant: string|UserContextData) { + setSecurityContext(tenant: string|UserContextData) { if (!tenant) throw new Error("Tenant can not be null"); let manager = this.container.get(DefaultServiceNames.SecurityManager, true); @@ -311,4 +312,88 @@ export class RequestContext implements IRequestContext { this._tracker.dispose(); this.container.dispose(); } + + normalize() { + let action: string; + let schema: string; + + const url = this.request.url; + const body = this.request.body; + + this.requestData.body = body; + + // Try to get schema and action from path + let schemaAction = url && url.pathname.substr(Conventions.instance.defaultUrlprefix.length + 1); + if (schemaAction) { + if (schemaAction[schemaAction.length - 1] === '/') + schemaAction = schemaAction.substr(0, schemaAction.length - 1); + } + + // Split schema and action (schema is optional) + if (schemaAction) { + if (schemaAction.indexOf('.') >= 0) { + let parts = schemaAction.split('.'); + schema = parts[0]; + action = parts[1]; + } + else { + action = schemaAction; + } + } + // Schema and action can be in the body + if (body) { + // Body contains only data -> create a new action object + if (!this.requestData.body.action && !this.requestData.body.params && !this.requestData.body.schema) { + this.requestData.params = this.requestData.body; + } + else { + action = this.requestData.body.action || action; + schema = this.requestData.body.schema || schema; + } + } + else { + url && url.query && Object.keys(url.query).forEach(k => { + if (k[0] !== "$") { + this.requestData.params = this.requestData.params || {}; + this.requestData.params[k] = url.query[k]; + } + }); + } + + this.requestData.params = this.requestData.params || {}; + this.requestData.page = 0; + this.requestData.pageSize = 20; + + // Or can be forced in the url query + if (url && url.query) { + if (url.query["$action"]) + action = url.query["$action"]; + if (url.query["_schema"]) + schema = url.query["_schema"]; + + // Normalize option values + Object.keys(url.query).forEach(name => { + try { + switch (name.toLowerCase()) { + case "$page": + this.requestData.page = (url.query["$page"] && parseInt(url.query["$page"])) || this.requestData.page; + break; + case "$pagesize": + this.requestData.pageSize = (url.query[name] && parseInt(url.query[name])) || this.requestData.pageSize; + break; + case "$query": + this.requestData.params = url.query["$query"] && JSON.parse(url.query["$query"]); + break; + } + } + catch (ex) {/*ignore*/ } + }); + } + + this.requestData.action = action || (!body && "all") || null; + this.requestData.schema = schema; + + this.requestData.vulcainVerb = this.requestData.schema ? `${this.requestData.schema}.${this.requestData.action}` : this.requestData.action; + this.requestTracker.trackAction(this.requestData.vulcainVerb); + } } \ No newline at end of file diff --git a/src/pipeline/testContext.ts b/src/pipeline/testContext.ts index 3625188..2b2d11b 100644 --- a/src/pipeline/testContext.ts +++ b/src/pipeline/testContext.ts @@ -6,7 +6,7 @@ import { IContainer } from "../di/resolvers"; import { UserContext } from "../security/securityContext"; import { Container } from "../di/containers"; import { RequestContext } from "./requestContext"; -import { Pipeline } from "./common"; +import { Pipeline, IRequestContext } from "./common"; import { AbstractHandler } from "./handlers/abstractHandlers"; import { DefaultServiceNames } from '../di/annotations'; @@ -15,7 +15,7 @@ export class TestContext extends RequestContext { return this.container; } - constructor(...components: Function[]) { + constructor() { super(new Container(), Pipeline.Test); let domain = new Domain(Service.domainName, this.container); this.container.injectInstance(domain, DefaultServiceNames.Domain); @@ -24,7 +24,7 @@ export class TestContext extends RequestContext { } setUser(user: UserContext) { - this.setSecurityManager(user); + this.setSecurityContext(user); return this; } @@ -33,13 +33,18 @@ export class TestContext extends RequestContext { } get context() { - let ctx = new RequestContext(this.container, Pipeline.Test); - ctx.setSecurityManager("test"); + return TestContext.newContext(this.container); + } + + static newContext(container: IContainer, data?: any): IRequestContext { + let ctx = new RequestContext(container, Pipeline.Test, data); + ctx.setSecurityContext("test"); + ctx.normalize(); return ctx; } createHandler(handler: Function): T { - let ctx = this.context; + let ctx = this.context; let scopedContainer = new Container(this.container, ctx); let h = new (<(container: IContainer) => void>handler)(scopedContainer); h.context = ctx; diff --git a/src/schemas/validator.ts b/src/schemas/validator.ts index 9ee34cf..6fada50 100644 --- a/src/schemas/validator.ts +++ b/src/schemas/validator.ts @@ -9,13 +9,13 @@ export class Validator { constructor(private domain: Domain) { } - async validate(ctx: IRequestContext, schema: Schema, val:any, parentName:string=""): Promise<{ [propertyName: string]: string }> { + validate(ctx: IRequestContext, schema: Schema, val:any, parentName:string=""): { [propertyName: string]: string } { let errors: { [propertyName: string]: string } = {}; if (!schema || !val) return errors; if (schema.extends) { if (schema.extends) { - let errorList = (await this.validate(ctx, schema.extends, val)); + let errorList = this.validate(ctx, schema.extends, val); errors = Object.assign(errors, errorList); } } @@ -38,12 +38,12 @@ export class Validator { if (prop.type === "any" && formatContext.propertyValue && formatContext.propertyValue._schema) { propertyTypeName = formatContext.propertyValue._schema; } - let errors2 = await this.validateReference(ctx, formatContext, prop.type, val); + let errors2 = this.validateReference(ctx, formatContext, prop.type, val); if (errors2) errors = Object.assign(errors, errors2); } else { - let err = await this.validateProperty(ctx, formatContext, val); + let err = this.validateProperty(ctx, formatContext, val); if (err) { errors[propertyName] = err; } @@ -58,7 +58,7 @@ export class Validator { if (schema.info.validate) { formatContext.propertyName = formatContext.propertySchema = formatContext.propertyValue = null; try { - let err = await schema.info.validate(val, ctx); + let err = schema.info.validate(val, ctx); if (err) errors["_"] = this.__formatMessage(err, formatContext, schema); } @@ -69,7 +69,7 @@ export class Validator { return errors; } - private async validateReference(ctx: IRequestContext, formatContext: FormatContext, propertyTypeName: string, entity): Promise<{ [propertyName: string]: string }> { + private validateReference(ctx: IRequestContext, formatContext: FormatContext, propertyTypeName: string, entity): { [propertyName: string]: string } { let errors = {}; const { propertySchema, propertyValue } = formatContext; @@ -90,7 +90,7 @@ export class Validator { if (propertySchema.validators) { for (let validator of propertySchema.validators) { - let msg = validator.validate && await validator.validate(propertyValue, ctx); + let msg = validator.validate && validator.validate(propertyValue, ctx); if (msg) { errors[formatContext.propertyName] = this.__formatMessage(msg, formatContext, propertySchema); return errors; @@ -98,7 +98,7 @@ export class Validator { } } - let err = propertySchema.validate && await propertySchema.validate(propertyValue, ctx); + let err = propertySchema.validate && propertySchema.validate(propertyValue, ctx); if (err) { errors[formatContext.propertyName] = err; return errors; @@ -117,14 +117,14 @@ export class Validator { baseItemSchema = currentItemSchema; } if (currentItemSchema) { - errors = Object.assign(errors, await this.validate(ctx, currentItemSchema, val, formatContext.propertyName + ".")); + errors = Object.assign(errors, this.validate(ctx, currentItemSchema, val, formatContext.propertyName + ".")); } } } return errors; } - private async validateProperty(ctx: IRequestContext, formatContext: FormatContext, entity): Promise { + private validateProperty(ctx: IRequestContext, formatContext: FormatContext, entity): string { const { propertySchema, propertyValue } = formatContext; if (propertySchema.dependsOn && !propertySchema.dependsOn(entity)) return; @@ -137,13 +137,13 @@ export class Validator { } if (propertySchema.validators) { for (let validator of propertySchema.validators) { - let err = validator.validate && await validator.validate(propertyValue, ctx); + let err = validator.validate && validator.validate(propertyValue, ctx); if (err) return this.__formatMessage(err, formatContext, validator); } } if (propertySchema.validate) { - let err = await propertySchema.validate(propertyValue, ctx); + let err = propertySchema.validate(propertyValue, ctx); if (err) return this.__formatMessage(err, formatContext, propertySchema); } } diff --git a/src/security/securityContext.ts b/src/security/securityContext.ts index c487098..e6dc650 100644 --- a/src/security/securityContext.ts +++ b/src/security/securityContext.ts @@ -75,8 +75,8 @@ export class SecurityContext implements UserContext { constructor(container: IContainer, private scopePolicy: IAuthorizationPolicy) { // Default - this.addOrReplaceStrategy(new TokenService()); - this.addOrReplaceStrategy(new ApiKeyService()); + this.addOrReplaceStrategy(container.get(DefaultServiceNames.BearerTokenService)); + //this.addOrReplaceStrategy(new ApiKeyService()); let strategies = container.getList(DefaultServiceNames.AuthenticationStrategy); for(let strategy of strategies) { diff --git a/src/security/services/apiKeyService.ts b/src/security/services/apiKeyService.ts index 19021d2..d61f102 100644 --- a/src/security/services/apiKeyService.ts +++ b/src/security/services/apiKeyService.ts @@ -11,8 +11,8 @@ import { CommandFactory } from "../../commands/commandFactory"; import { Metadata } from "../../utils/reflector"; // TODO add enableApiKeyAuthentication -@Injectable(LifeTime.Singleton, DefaultServiceNames.AuthenticationStrategy) -@Metadata("system", true) +//@Injectable(LifeTime.Singleton, DefaultServiceNames.AuthenticationStrategy) +//@Metadata("system", true) export class ApiKeyService implements IAuthenticationStrategy { public readonly name = "apiKey"; diff --git a/test/schema/validateData.spec.ts b/test/schema/validateData.spec.ts index 79dcfb1..a6db028 100644 --- a/test/schema/validateData.spec.ts +++ b/test/schema/validateData.spec.ts @@ -103,7 +103,7 @@ describe("Validate data", function () { let schema = domain.getSchema("SimpleModel"); let model: SimpleModel = { text: "text", number: 1, baseText: "" }; - let errors = await schema.validate(null, model); + let errors = schema.validate(null, model); expect(errors.baseText); }); @@ -112,7 +112,7 @@ describe("Validate data", function () { let schema = domain.getSchema("SimpleModel"); let model: SimpleModel = { text: "text", number: 1, baseText: "a" }; - let errors = await schema.validate(null, model); + let errors = schema.validate(null, model); expect(errors.baseText); }); @@ -121,7 +121,7 @@ describe("Validate data", function () { let schema = domain.getSchema("SimpleModel"); let model = schema.coerce({ text: "text", number: "1w1", baseText: "text" }); - let errors = await schema.validate(null, model); + let errors = schema.validate(null, model); expect(errors.number); }); @@ -129,7 +129,7 @@ describe("Validate data", function () { let model: SimpleModel = { text: "text", number: 1, baseText: "text" }; let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("SimpleModel"); - let errors = await schema.validate(null, model); + let errors = schema.validate(null, model); expect(Object.keys(errors).length).equals(0); }); @@ -139,7 +139,7 @@ describe("Validate data", function () { let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("ReferenceModel"); - let errors = await schema.validate(null, refs); + let errors = schema.validate(null, refs); expect(Object.keys(errors).length).equals(1); }); @@ -150,7 +150,7 @@ describe("Validate data", function () { let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("ReferenceModel"); - let errors = await schema.validate(null, refs); + let errors = schema.validate(null, refs); expect(Object.keys(errors).length).equals(2); }); @@ -161,7 +161,7 @@ describe("Validate data", function () { let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("ReferenceModel"); - let errors = await schema.validate(null, refs); + let errors = schema.validate(null, refs); expect(Object.keys(errors).length).equals(1); // TODO really expected ? }); @@ -171,7 +171,7 @@ describe("Validate data", function () { let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("ReferenceModel"); - let errors = await schema.validate(null, refs); + let errors = schema.validate(null, refs); expect(Object.keys(errors).length).equals(1); }); @@ -183,7 +183,7 @@ describe("Validate data", function () { let model: EmailModel = { email: "first.name@email.com" }; let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("EmailModel"); - let errors = await schema.validate(null, model); + let errors = schema.validate(null, model); expect(!errors.email); }); @@ -192,7 +192,7 @@ describe("Validate data", function () { let model: EmailModel = { email: "first.name@email" }; let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("EmailModel"); - let errors = await schema.validate(null, model); + let errors = schema.validate(null, model); expect(errors.email); }); @@ -205,7 +205,7 @@ describe("Validate data", function () { let model: UrlModel = { url: "https://myWebsite.com/#ancre/1" }; let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("UrlModel"); - let errors = await schema.validate(null, model); + let errors = schema.validate(null, model); expect(Object.keys(errors).length).equals(0); }); @@ -214,7 +214,7 @@ describe("Validate data", function () { let model: UrlModel = { url: "http://site.r" }; let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("UrlModel"); - let errors = await schema.validate(null, model); + let errors = schema.validate(null, model); expect(Object.keys(errors).length).equals(1); }); @@ -226,7 +226,7 @@ describe("Validate data", function () { let model: AlphanumericModel = { value: "abcde1345fghik6789" }; let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("AlphanumericModel"); - let errors = await schema.validate(undefined, model); + let errors = schema.validate(undefined, model); expect(Object.keys(errors).length).equals(0); }); @@ -235,7 +235,7 @@ describe("Validate data", function () { let model: AlphanumericModel = { value: "abc123!" }; let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("AlphanumericModel"); - let errors = await schema.validate(undefined, model); + let errors = schema.validate(undefined, model); expect(Object.keys(errors).length).equals(1); }); @@ -247,7 +247,7 @@ describe("Validate data", function () { let model: DateIsoModel = { date: new Date().toISOString() }; let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("DateIsoModel"); - let errors = await schema.validate(undefined, model); + let errors = schema.validate(undefined, model); expect(Object.keys(errors).length).equals(0); }); @@ -258,7 +258,7 @@ describe("Validate data", function () { let model: DateIsoModel = { date: new Date().toDateString() }; let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("DateIsoModel"); - let errors = await schema.validate(undefined, model); + let errors = schema.validate(undefined, model); expect(Object.keys(errors).length).equals(1); }); @@ -270,7 +270,7 @@ describe("Validate data", function () { let model: ArrayOfModel = { enums: ["a", "bb"] }; let domain = context.rootContainer.get("Domain"); let schema = domain.getSchema("ArrayOfModel"); - let errors = await schema.validate(undefined, model); + let errors = schema.validate(undefined, model); expect(Object.keys(errors).length).equals(1); }); From 09e85418820a21dce3bfc0b08d29905522a44e07 Mon Sep 17 00:00:00 2001 From: METGE Alain Date: Fri, 16 Mar 2018 10:56:39 +0100 Subject: [PATCH 09/10] fix mongo update error --- package.json | 2 +- src/providers/mongo/provider.ts | 65 +++++++++++++++++---------------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index e685e19..de4b1af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta949", + "version": "2.0.0-beta951", "description": "Vulcain micro-service framework", "main": "lib/src/index.js", "scripts": { diff --git a/src/providers/mongo/provider.ts b/src/providers/mongo/provider.ts index 22e68bf..ec2cd7a 100644 --- a/src/providers/mongo/provider.ts +++ b/src/providers/mongo/provider.ts @@ -74,14 +74,14 @@ export class MongoProviderFactory implements IProviderFactory { */ class MongoProvider implements IProvider { - private tenant: string; - private _logger: Logger; + private databaseName: string; + private logger: Logger; public state: { keyPropertyNameBySchemas: Map; uri: string; dispose?: () => void; - _mongo?; + mongoClient?: MongoClient; }; get address() { @@ -103,22 +103,23 @@ class MongoProvider implements IProvider throw new Error("Uri is required for mongodb provider."); } this.state = { uri: uri, keyPropertyNameBySchemas: new Map() }; - this._logger = ctx.container.get(DefaultServiceNames.Logger); + this.logger = ctx.container.get(DefaultServiceNames.Logger); } - initialize( ctx: IRequestContext, tenant: string): () => any { - if (!tenant) - throw new Error("tenant is required"); + initialize( ctx: IRequestContext, tenant?: string): () => any { + // By default, there is a database base by tenant + this.databaseName = tenant; - this.tenant = tenant; - // Insert tenant into connection string + // If database is provided use it as database name and ignore tenant let url = URL.parse(this.state.uri); - // If no database is provided just use the tenant as database name - if( !url.pathname || url.pathname === "/") - url.pathname = tenant; - else - // else suffix the database name with the tenant - url.pathname += "_" + tenant; + if (url.pathname && url.pathname !== "/") { + this.databaseName = url.pathname.replace('/', ''); + url.pathname = "/"; + } + else if (!tenant) { + throw new Error("tenant is required"); + } + this.state.uri = URL.format(url); Service.log.verbose(ctx, () => `MONGODB: Creating provider ${Service.removePasswordFromUrl(this.state.uri)} for tenant ${tenant}`); @@ -126,13 +127,13 @@ class MongoProvider implements IProvider } dispose() { - this.state._mongo.close(); - this.state._mongo = null; + this.state.mongoClient.close(); + this.state.mongoClient = null; this.state.dispose = null; } private async ensureSchemaReady(ctx: IRequestContext, schema: Schema) { - if(!this.state._mongo) + if(!this.state.mongoClient) await this.openDatabase(ctx); let keyPropertyName = this.state.keyPropertyNameBySchemas.get(schema.name); @@ -158,8 +159,9 @@ class MongoProvider implements IProvider return Promise.resolve(); } - const db = this.state._mongo; - let self = this; + const db = this.state.mongoClient.db(this.databaseName); + let uri = this.state.uri; + return new Promise((resolve, reject) => { // Don't use 'this' here to avoid memory leaks // Open connection @@ -167,10 +169,10 @@ class MongoProvider implements IProvider db.createIndex(schema.info.storageName, keys, { w: 1, background: true, name: indexName, unique: true }, (err) => { if (err) { - ctx.logError( err, ()=>`MONGODB: Error when creating index for ${Service.removePasswordFromUrl(self.state.uri)} for schema ${schema.name}`); + ctx.logError( err, ()=>`MONGODB: Error when creating index for ${Service.removePasswordFromUrl(uri)} for schema ${schema.name}`); } else { - ctx.logInfo(()=>`MONGODB: Unique index created for ${Service.removePasswordFromUrl(self.state.uri)} for schema ${schema.name}`); + ctx.logInfo(()=>`MONGODB: Unique index created for ${Service.removePasswordFromUrl(uri)} for schema ${schema.name}`); } resolve(); }); @@ -181,18 +183,19 @@ class MongoProvider implements IProvider return new Promise((resolve, reject) => { // Don't use 'this' here to avoid memory leaks // Open connection - MongoClient.connect(this.state.uri, this.options, (err, db) => { + MongoClient.connect(this.state.uri, this.options, (err, client) => { if (err) { reject(err); - ctx.logError(err, ()=>`MONGODB: Error when opening database ${Service.removePasswordFromUrl(this.state.uri)} for tenant ${this.tenant}`); + ctx.logError(err, ()=>`MONGODB: Error when opening database ${Service.removePasswordFromUrl(this.state.uri)} for tenant ${this.databaseName}`); return; } - this.state._mongo = db; + this.state.mongoClient = client; resolve(); }); }); } + /** * Return a list of entities * @param options @@ -215,7 +218,7 @@ class MongoProvider implements IProvider let self = this; return new Promise(async (resolve, reject) => { try { - let db = self.state._mongo; + let db = self.state.mongoClient.db(this.databaseName); // TODO try with aggregate let total = await db.collection(schema.info.storageName).find(query).count(); let cursor = db.collection(schema.info.storageName).find(query, proj) @@ -253,7 +256,7 @@ class MongoProvider implements IProvider let self = this; return new Promise(async (resolve, reject) => { try { - let db = self.state._mongo; + let db = self.state.mongoClient.db(this.databaseName); db.collection(schema.info.storageName).findOne(filter, (err, res) => { if (err) { ctx.logError(err, ()=>`MONGODB ERROR: Get query on ${Service.removePasswordFromUrl(self.state.uri)} for schema ${schema.name} with id: ${id}`); @@ -296,7 +299,7 @@ class MongoProvider implements IProvider reject(new Error("MONGODB DELETE ERROR: Entity must exists")); return; } - let db = self.state._mongo; + let db = self.state.mongoClient.db(this.databaseName); db.collection(schema.info.storageName).remove(filter, (err, res) => { if (err) { let e = self.normalizeErrors(id, err); @@ -350,7 +353,7 @@ class MongoProvider implements IProvider let self = this; return new Promise(async (resolve, reject) => { try { - let db = self.state._mongo; + let db = self.state.mongoClient.db(this.databaseName); db.collection(schema.info.storageName).insertOne(entity, (err) => { if (err) { let e = self.normalizeErrors(entity[keyPropertyName], err); @@ -390,7 +393,7 @@ class MongoProvider implements IProvider return new Promise(async (resolve, reject) => { try { - let db = self.state._mongo; + let db = self.state.mongoClient.db(this.databaseName); let collection = db.collection(schema.info.storageName); collection.findOne(filter, (err, initial) => { @@ -412,7 +415,7 @@ class MongoProvider implements IProvider initial._updated = new Date().toUTCString(); initial._id = _id; - collection.updateOne(filter, initial, err => { + collection.updateOne(filter, { $set: initial }, err => { if (err) { let e = self.normalizeErrors(id, err); ctx.logError(e, ()=>`MONGODB ERROR : Updating entity on ${Service.removePasswordFromUrl(self.state.uri)} for schema ${schema.name} with id: ${id}`); From e2ffb306a045ab516ab2e3ebc6dd3ebeeb19bf82 Mon Sep 17 00:00:00 2001 From: METGE Alain Date: Mon, 26 Mar 2018 16:40:17 +0200 Subject: [PATCH 10/10] fix error tag in tracker --- package-lock.json | 2 +- package.json | 2 +- src/instrumentations/span.ts | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index db75e9b..4ec372e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta948", + "version": "2.0.0-beta951", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index de4b1af..bdaea67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vulcain-corejs", - "version": "2.0.0-beta951", + "version": "2.0.0-beta952", "description": "Vulcain micro-service framework", "main": "lib/src/index.js", "scripts": { diff --git a/src/instrumentations/span.ts b/src/instrumentations/span.ts index 5a66791..42c6c7d 100644 --- a/src/instrumentations/span.ts +++ b/src/instrumentations/span.ts @@ -151,7 +151,7 @@ export class Span implements ISpanTracker { endCommand() { if (this.action) { // for ignored requests like _servicedependency - this.addTag("error", this.error ? "true" : "false"); + this.addTag("error", this.error ? this.error.toString() : "false"); let metricsName = `vulcain_${this.commandType.toLowerCase()}command_duration_ms`; this.metrics.timing(metricsName, this.durationInMs, this.tags); } @@ -165,8 +165,7 @@ export class Span implements ISpanTracker { if (!this.error && this.kind === SpanKind.Request && this.context.response && this.context.response.statusCode && this.context.response.statusCode >= 400) { this.error = new Error("Http error " + this.context.response.statusCode); } - if(this.error) - this.addTag("error", this.error.toString()); + this.addTag("error", this.error ? this.error.toString() : "false"); this.metrics.timing("vulcain_service_duration_ms", this.durationInMs, this.tags); }