Skip to content

Commit

Permalink
Merge pull request #14 from onixjs/development
Browse files Browse the repository at this point in the history
Release 1.0.0-alpha.9
  • Loading branch information
jonathan-casarrubias committed Mar 27, 2018
2 parents 85ba436 + 52b8c4d commit 6dd5a44
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 96 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onixjs/core",
"version": "1.0.0-alpha.8",
"version": "1.0.0-alpha.9",
"description": "The High-Performance SOA Real-Time Framework for Node.JS",
"main": "dist/src/index.js",
"scripts": {
Expand Down Expand Up @@ -47,8 +47,8 @@
"node": ">=8.10.0"
},
"dependencies": {
"@onixjs/sdk": "^1.0.0-alpha.4.4",
"reflect-metadata": "^0.1.12",
"@onixjs/sdk": "^1.0.0-alpha.4.1",
"uws": "^9.14.0"
},
"devDependencies": {
Expand Down
3 changes: 3 additions & 0 deletions src/core/app.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ export class AppServer {
switch (operation.type) {
// Event sent from broker when loading a project
case OperationType.APP_CREATE:
// Use Host Level configurations, like custom ports
Object.assign(this.config, operation.message);
// Setup factory, responser and streamer
this.factory = new AppFactory(this.AppClass, this.config);
this.responser = new CallResponser(this.factory, this.AppClass);
this.streamer = new CallStreamer(this.factory, this.AppClass);
Expand Down
136 changes: 101 additions & 35 deletions src/core/http.server.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import 'reflect-metadata';
import {OnixConfig} from '../index';
import {EndpointDirectory, HttpRequestHandler} from '../interfaces';
import {
HttpRequestHandler,
HTTPMethodsDirectory,
HTTPMethods,
} from '../interfaces';
import * as fs from 'fs';
import * as url from 'url';
import * as http from 'http';
import * as https from 'https';
import * as querystring from 'querystring';
import {Utils} from '@onixjs/sdk/dist/utils';
/**
* @function HTTPServer
* @author Jonathan Casarrubias
Expand All @@ -26,7 +31,14 @@ export class HTTPServer {
* @description Will contain a directory of handlers
* for specific http request calls.
*/
private endpoints: EndpointDirectory = {};
private endpoints: HTTPMethodsDirectory = {
get: {},
post: {},
patch: {},
put: {},
update: {},
delete: {},
};
/**
* @constructor
* @param config
Expand Down Expand Up @@ -91,55 +103,109 @@ export class HTTPServer {
stop(): void {
this.server.close();
}

/**
* @method listener
* @param req
* @param res
* @description This method wraps endpoints. This provides framework level
* functionalities to each of the calls.
*
* Example, parse POST or Query.
*/
private async listener(req: http.IncomingMessage, res: http.ServerResponse) {
if (req.url) {
// TODO Create HTTP Request that extends from http.IncomingMessage
if (req.method === 'POST') {
req['post'] = await this.processPost(req, res);
}
// Get Query String
req['query'] = querystring.parse(url.parse(req.url).query || '');
// Define Endpoint
const endpoint: string | undefined = url.parse(req.url).pathname;
//const query: string | null = url.parse(req.url).query;
if (endpoint && this.endpoints[endpoint]) {
this.endpoints[endpoint](req, res);
} else if (this.endpoints['*']) {
this.endpoints['*'](req, res);
} else {
res.end(
JSON.stringify({
error: {
code: 404,
message: `Unable to process endpoint, missing listener ${endpoint}`,
},
}),
);
if (req.url && req.method) {
// Get directory of endpoints for this method
const directory = this.endpoints[req.method.toLowerCase()];
// Verify we actually got a directory
if (directory) {
// TODO Create HTTP Request that extends from http.IncomingMessage
if (req.method === 'POST') {
req['post'] = await this.processPost(req, res);
req['post'] = Utils.IsJsonString(req['post'])
? JSON.parse(req['post'])
: req['post'];
}
// Get Query String
req['query'] = querystring.parse(url.parse(req.url).query || '');
// Define Endpoint
const endpoint: string | undefined = url.parse(req.url).pathname;
//const query: string | null = url.parse(req.url).query;
if (endpoint && directory[endpoint]) {
directory[endpoint](req, res);
} else if (directory['*']) {
directory['*'](req, res);
} else {
res.end(
JSON.stringify({
error: {
code: 404,
message: `Unable to process endpoint, missing listener ${endpoint}`,
},
}),
);
}
}
} else {
throw new Error('Missing request.url from http.server');
}
}

register(endpoint: string, handler: HttpRequestHandler): void {
this.endpoints[endpoint] = handler;
/**
* @method register
* @param method
* @param endpoint
* @param handler
* @description This method will register middleware endpoints
* It should be used before the server is started.
*/
register(
method:
| HTTPMethods.GET
| HTTPMethods.POST
| HTTPMethods.PUT
| HTTPMethods.PATCH
| HTTPMethods.DELETE,
endpoint: string,
handler: HttpRequestHandler,
): void {
switch (method) {
// Using enum instead of string to make this a strict feature
case HTTPMethods.GET:
this.endpoints.get[endpoint] = handler;
break;
case HTTPMethods.POST:
this.endpoints.post[endpoint] = handler;
break;
case HTTPMethods.PUT:
this.endpoints.put[endpoint] = handler;
break;
case HTTPMethods.PATCH:
this.endpoints.patch[endpoint] = handler;
break;
case HTTPMethods.DELETE:
this.endpoints.delete[endpoint] = handler;
break;
}
}

/**
* @method processPost
* @param req
* @param res
* @description Built-in middleware that parses a post data
* and returns a parsed object.
*/
private async processPost(req, res): Promise<any> {
return new Promise((resolve, reject) => {
let data: string = '';
req.on('data', d => {
data += d;
// Oops way to large body, kill this guy now
/* Oops way to large body, kill this guy now
Temporally disabled since it is not currently public feature
if (data.length > 1e6) {
data = '';
res.writeHead(413, {'Content-Type': 'text/plain'}).end();
req.connection.destroy();
}
}*/
});
req.on('end', function() {
resolve(querystring.parse(data));
resolve(data);
});
});
}
Expand Down
6 changes: 4 additions & 2 deletions src/core/schema.provider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'reflect-metadata';
import {OnixJS, IAppDirectory, IAppConfig} from '../index';
import {OnixJS, IAppDirectory, IAppConfig, HTTPMethods} from '../index';
import * as http from 'http';
import {HTTPServer} from './http.server';
/**
Expand Down Expand Up @@ -33,7 +33,9 @@ export class SchemaProvider {
start(): void {
// Setup server
this.server = new HTTPServer(this.onix.config);
this.server.register('/', (req, res) => this.listener(req, res));
this.server.register(HTTPMethods.GET, '/', (req, res) =>
this.listener(req, res),
);
this.server.start();
// Indicate the ONIX SERVER is now listening on the given port
console.log(
Expand Down
21 changes: 19 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class OnixJS {
* @description Current Onix Version.
*/
get version(): string {
return '1.0.0-alpha.8';
return '1.0.0-alpha.9';
}
/**
* @property server
Expand Down Expand Up @@ -131,7 +131,19 @@ export class OnixJS {
return new Promise<ChildProcess | Error>((resolve, reject) => {
const parts: string[] = namespace.split('@');
const name: string = parts.shift() || '';
const directory: string = parts.shift() || '';
let directory: string = parts.shift() || '';
let port: number = 0;
let disableNetwork;
if (directory.match(/:[\d]{2,5}/)) {
const p = directory.split(':');
directory = p.shift() || '';
port = parseInt(p.shift() || '') || port;
}
if (directory.match(/:disabled/)) {
const p = directory.split(':');
directory = p.shift() || '';
disableNetwork = true;
}
// Verify for duplicated applications
if (this._apps[name]) {
reject(new Error('OnixJS Error: Trying to add duplicated application'));
Expand All @@ -155,6 +167,11 @@ export class OnixJS {
// Must Follow App Operation
this._apps[name].process.send(<IAppOperation>{
type: OperationType.APP_CREATE,
// Send host level parameters here
message: {
port,
disableNetwork,
},
});
}
});
Expand Down
18 changes: 15 additions & 3 deletions src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,13 @@ export interface BootConfig {
apps: string[];
identityProvider?: DomainConfig;
}

export interface EndpointDirectory {
[key: string]: HttpRequestHandler;
export interface HTTPMethodsDirectory {
get: {[key: string]: HttpRequestHandler};
post: {[key: string]: HttpRequestHandler};
patch: {[key: string]: HttpRequestHandler};
put: {[key: string]: HttpRequestHandler};
update: {[key: string]: HttpRequestHandler};
delete: {[key: string]: HttpRequestHandler};
}
export interface HttpRequestHandler {
(req: http.IncomingMessage, res: http.ServerResponse): void;
Expand Down Expand Up @@ -363,3 +367,11 @@ export enum ReflectionKeys {
/*15*/ INJECTABLE_SERVICE,
/*16*/ INJECTABLE_DATASOURCE,
}

export enum HTTPMethods {
GET,
POST,
PATCH,
PUT,
DELETE,
}
10 changes: 6 additions & 4 deletions test/onix.acceptance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ test('Onix version', t => {
**/
test('Onix app starter', async t => {
const onix: OnixJS = new OnixJS({cwd, port: 8083});
await onix.load('TodoApp@todo2.app');
await onix.load('TodoApp@todo.app:disabled');
const results: OperationType.APP_START_RESPONSE[] = await onix.start();
t.deepEqual(results, [
// One for the server
Expand All @@ -36,7 +36,7 @@ test('Onix app starter', async t => {
*/
test('Onix app pinger', async t => {
const onix: OnixJS = new OnixJS({cwd, port: 8084});
await onix.load('TodoApp@todo2.app');
await onix.load('TodoApp@todo.app:disabled');
const config: IAppConfig = await onix.ping('TodoApp');
t.true(config.disableNetwork);
});
Expand All @@ -60,7 +60,7 @@ test('Onix app greeter', async t => {
**/
test('Onix rpc component methods from server', async t => {
const onix: OnixJS = new OnixJS({cwd, port: 8085});
await onix.load('TodoApp@todo2.app');
await onix.load('TodoApp@todo.app:disabled');
await onix.start();
const todo: TodoModel = new TodoModel();
todo.text = 'Hello World';
Expand Down Expand Up @@ -125,8 +125,10 @@ test('Onix rpc component methods from client', async t => {
***/
test('Onix rpc component stream', async t => {
const text: string = 'Hello SDK World';
// Host Port 8087
const onix: OnixJS = new OnixJS({cwd, port: 8087});
await onix.load('TodoApp@todo3.app');
// SOA Service Port 8078
await onix.load('TodoApp@todo.app:8078');
await onix.start();
// Websocket should be available now
const client: OnixClient = new OnixClient({
Expand Down
Loading

0 comments on commit 6dd5a44

Please sign in to comment.