Skip to content
This repository has been archived by the owner on Jun 16, 2021. It is now read-only.

Commit

Permalink
feature(DynamicSchema): added dynamic gateway capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
Stradivario committed Sep 27, 2019
1 parent 5542f29 commit 540df30
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 17 deletions.
9 changes: 9 additions & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ModuleWithServices } from '@rxdi/core';
import { MicroserviceInterface } from './microservice.interface';
export declare class MicroserviceModule {
static forRoot(microservices: MicroserviceInterface[], config?: {
authorization?: Function;
}): ModuleWithServices;
}
export * from './microservice.interface';
export * from './proxy.service';
57 changes: 57 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
var MicroserviceModule_1;
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@rxdi/core");
const proxy_service_1 = require("./proxy.service");
let MicroserviceModule = MicroserviceModule_1 = class MicroserviceModule {
static forRoot(microservices, config) {
if (!config) {
config = { authorization: null };
}
return {
module: MicroserviceModule_1,
services: [
proxy_service_1.ProxyService,
{
provide: 'gapi-microservice-config-auth',
useValue: config
},
{
provide: 'gapi-microservice-config',
useValue: microservices
},
{
provide: 'gapi-custom-schema-definition',
lazy: true,
deps: [proxy_service_1.ProxyService],
useFactory: (proxyService) => __awaiter(this, void 0, void 0, function* () {
return yield proxyService.getSchemaIntrospection();
})
}
]
};
}
};
MicroserviceModule = MicroserviceModule_1 = __decorate([
core_1.Module()
], MicroserviceModule);
exports.MicroserviceModule = MicroserviceModule;
__export(require("./microservice.interface"));
__export(require("./proxy.service"));
22 changes: 22 additions & 0 deletions dist/microservice.interface.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { InjectionToken } from '@rxdi/core';
export interface MicroserviceInterface {
name: string;
link: string;
}
export declare type AFTER_MIDDLEWARE = Middleware;
export declare type BEFORE_MIDDLEWARE = Middleware;
export interface MiddlewareOptions {
context: Object;
operationName: string;
variables: Object;
query: string;
microservice: MicroserviceInterface;
method: string | 'POST' | 'GET';
headers: {
[key: string]: string;
};
}
export declare type Middleware = (options: MiddlewareOptions) => Promise<MiddlewareOptions>;
export declare type MiddlewareAfter = (res: any, options: MiddlewareOptions) => Promise<Object>;
export declare const BEFORE_MIDDLEWARE: InjectionToken<Middleware>;
export declare const AFTER_MIDDLEWARE: InjectionToken<Middleware>;
5 changes: 5 additions & 0 deletions dist/microservice.interface.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@rxdi/core");
exports.BEFORE_MIDDLEWARE = new core_1.InjectionToken('graph-gateway-before-middleware-function-token');
exports.AFTER_MIDDLEWARE = new core_1.InjectionToken('graph-gateway-after-middleware-function-token');
8 changes: 8 additions & 0 deletions dist/proxy.service.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { MicroserviceInterface } from './microservice.interface';
import { GraphQLSchema } from 'graphql';
export declare class ProxyService {
private microservices;
constructor(microservices: MicroserviceInterface[]);
getSchemaIntrospection(microservices?: MicroserviceInterface[]): Promise<GraphQLSchema>;
private getIntrospectSchema;
}
101 changes: 101 additions & 0 deletions dist/proxy.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@rxdi/core");
const graphql_tools_1 = require("graphql-tools");
const apollo_link_http_1 = require("apollo-link-http");
const microservice_interface_1 = require("./microservice.interface");
const fetch = require("node-fetch");
const graphql_1 = require("graphql");
let ProxyService = class ProxyService {
constructor(microservices) {
this.microservices = microservices;
}
getSchemaIntrospection(microservices) {
return __awaiter(this, void 0, void 0, function* () {
const schemas = yield Promise.all((microservices || this.microservices).map((ep) => __awaiter(this, void 0, void 0, function* () {
console.log(`Microservice: ${ep.name} loaded!`);
return Object.assign({}, ep, { schema: yield this.getIntrospectSchema(ep) });
})));
core_1.Container.reset('schemas');
core_1.Container.remove('schemas');
core_1.Container.set('schemas', schemas);
return graphql_tools_1.mergeSchemas({
schemas: schemas.map(res => res.schema)
});
});
}
getIntrospectSchema(microservice) {
return __awaiter(this, void 0, void 0, function* () {
const link = apollo_link_http_1.createHttpLink({ uri: microservice.link, fetch });
return graphql_tools_1.makeRemoteExecutableSchema({
schema: yield graphql_tools_1.introspectSchema(link),
link,
fetcher({ query: queryDocument, variables, operationName, context: { graphqlContext } }) {
return __awaiter(this, void 0, void 0, function* () {
let beforeMiddleware = r => Promise.resolve(r);
let afterMiddlewares = [];
try {
beforeMiddleware = core_1.Container.get(microservice_interface_1.BEFORE_MIDDLEWARE);
}
catch (e) { }
try {
afterMiddlewares = core_1.Container.getMany(microservice_interface_1.AFTER_MIDDLEWARE);
}
catch (e) { }
const query = graphql_1.print(queryDocument);
let middlewareOptions = {
context: graphqlContext,
operationName,
variables,
method: 'POST',
query,
headers: graphqlContext['headers'] || {
'Content-Type': 'application/json'
},
microservice
};
middlewareOptions = yield beforeMiddleware(middlewareOptions);
const fetchResult = yield fetch(middlewareOptions.microservice.link, {
method: middlewareOptions.method,
headers: middlewareOptions.headers,
body: JSON.stringify({ query, variables, operationName })
});
let res = yield fetchResult.json();
if (afterMiddlewares.length) {
for (const middleware of afterMiddlewares) {
res = yield middleware(res, middlewareOptions);
}
}
return res;
});
}
});
});
}
};
ProxyService = __decorate([
core_1.Service(),
__param(0, core_1.Inject('gapi-microservice-config')),
__metadata("design:paramtypes", [Array])
], ProxyService);
exports.ProxyService = ProxyService;
4 changes: 2 additions & 2 deletions microservice.interface.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface MiddlewareOptions {
[key: string]: string;
};
}
export declare type Middleware = (options: MiddlewareOptions) => MiddlewareOptions;
export declare type MiddlewareAfter = (res: any, options: MiddlewareOptions) => Object;
export declare type Middleware = (options: MiddlewareOptions) => Promise<MiddlewareOptions>;
export declare type MiddlewareAfter = (res: any, options: MiddlewareOptions) => Promise<Object>;
export declare const BEFORE_MIDDLEWARE: InjectionToken<Middleware>;
export declare const AFTER_MIDDLEWARE: InjectionToken<Middleware>;
6 changes: 3 additions & 3 deletions proxy.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ let ProxyService = class ProxyService {
link,
fetcher({ query: queryDocument, variables, operationName, context: { graphqlContext } }) {
return __awaiter(this, void 0, void 0, function* () {
let beforeMiddleware = r => r;
let beforeMiddleware = r => Promise.resolve(r);
let afterMiddlewares = [];
try {
beforeMiddleware = core_1.Container.get(microservice_interface_1.BEFORE_MIDDLEWARE);
Expand All @@ -70,7 +70,7 @@ let ProxyService = class ProxyService {
},
microservice
};
middlewareOptions = beforeMiddleware(middlewareOptions);
middlewareOptions = yield beforeMiddleware(middlewareOptions);
const fetchResult = yield fetch(middlewareOptions.microservice.link, {
method: middlewareOptions.method,
headers: middlewareOptions.headers,
Expand All @@ -79,7 +79,7 @@ let ProxyService = class ProxyService {
let res = yield fetchResult.json();
if (afterMiddlewares.length) {
for (const middleware of afterMiddlewares) {
res = middleware(res, middlewareOptions);
res = yield middleware(res, middlewareOptions);
}
}
return res;
Expand Down
4 changes: 2 additions & 2 deletions src/microservice.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export interface MiddlewareOptions {
method: string | 'POST' | 'GET';
headers: { [key: string]: string };
}
export type Middleware = (options: MiddlewareOptions) => MiddlewareOptions;
export type MiddlewareAfter = (res, options: MiddlewareOptions) => Object;
export type Middleware = (options: MiddlewareOptions) => Promise<MiddlewareOptions>;
export type MiddlewareAfter = (res, options: MiddlewareOptions) => Promise<Object>;

export const BEFORE_MIDDLEWARE = new InjectionToken<Middleware>(
'graph-gateway-before-middleware-function-token'
Expand Down
24 changes: 14 additions & 10 deletions src/proxy.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ export class ProxyService {
private microservices: MicroserviceInterface[]
) {}

public async getSchemaIntrospection(): Promise<GraphQLSchema> {
public async getSchemaIntrospection(microservices?: MicroserviceInterface[]): Promise<GraphQLSchema> {
const schemas = await Promise.all(
(microservices || this.microservices).map(async ep => {
console.log(`Microservice: ${ep.name} loaded!`);
return {
...ep,
schema: await this.getIntrospectSchema(ep)
};
})
);
return mergeSchemas({
schemas: await Promise.all(
this.microservices.map(ep => {
console.log(`Microservice: ${ep.name} loaded!`);
return this.getIntrospectSchema(ep);
})
)
schemas: schemas.map(res => res.schema)
});
}

Expand All @@ -47,7 +51,7 @@ export class ProxyService {
operationName,
context: { graphqlContext }
}) {
let beforeMiddleware: Middleware = r => r;
let beforeMiddleware: Middleware = r => Promise.resolve(r);
let afterMiddlewares: MiddlewareAfter[] = [];
try {
beforeMiddleware = Container.get(BEFORE_MIDDLEWARE);
Expand All @@ -68,7 +72,7 @@ export class ProxyService {
microservice
};

middlewareOptions = beforeMiddleware(middlewareOptions);
middlewareOptions = await beforeMiddleware(middlewareOptions);
const fetchResult = await fetch(middlewareOptions.microservice.link, {
method: middlewareOptions.method,
headers: middlewareOptions.headers,
Expand All @@ -77,7 +81,7 @@ export class ProxyService {
let res = await fetchResult.json();
if (afterMiddlewares.length) {
for (const middleware of afterMiddlewares) {
res = middleware(res, middlewareOptions);
res = await middleware(res, middlewareOptions);
}
}
return res;
Expand Down

0 comments on commit 540df30

Please sign in to comment.