mono/packages/vfs/ref/services/External.ts

205 lines
5.1 KiB
TypeScript

import { BaseService, Config } from '../services/Base';
import { ChildProcess } from 'child_process';
import * as _ from 'lodash';
const os = require('os');
const path = require('path');
const fs = require('fs');
const NOIMPL = function (fn: string) {
throw new Error(fn + " not implemented in sub class ");
};
// @TODO: vfs-ext#exists
function exists(_path: string): any {
try {
const stat = fs.statSync(_path);
if (stat.isFile() === true) {
return true;
}
} catch (e) {
}
return null;
}
// @TODO:@-> TS aspect signal extension?
export enum EStatus {
INACTIVE,
STARTED,
STARTING,
RUNNING,
ERROR,
INVALID
}
export enum EFlags {
// No particual behaviour
None = 0,
// This service is needed to run the application, otherwise allow failed
REQUIRED,
// This service can be retrieved existing instance
SHARED,
// This service can be retrieved from a system daemon
SYSTEM_SHARED = EFlags.SHARED & 1
}
// @TODO: TS garbage
export interface IExternalServiceDesriptor {
label: string;
status: EStatus;
}
// ─── Minimum implementation for an external service
export interface IExternalService {
label(): string; // debugging
filename(): string; // binary name, ie: 'mongod'. Need for search.
path(): string; // full path to binary
init(): Promise<any>; // std call
start(): Promise<any>; // std call
stop(): Promise<any>; // std call
pause?(): Promise<any>; // optional std call
resume?(): Promise<any>; // optional std call
validate(): Promise<any>; // std call
dependencies(): string[]; // optional std call
status: EStatus;
}
// ─── Base class to express an external service binary
export class ExternalService extends BaseService implements IExternalService {
config: Config;
process: ChildProcess = null;
flags: EFlags = EFlags.None;
status: EStatus = EStatus.INVALID;
searchPaths: Array<string> = [];
// @TODO:decorator on Ctor ?
constructor(config: Config) {
super(config, null, null);
if (_.isString(config)) {
this.configPath = config;
} else if (_.isObject(config)) {
this.config = config;
}
}
//
// ─── DESCRIPTORS ────────────────────────────────────────────────────────────────
//
/**
* Return an unique label for this service.
* - This is being used to help finding the binary.
* @returns {string}
*
* @memberOf ExternalService
*/
label(): string {
return "No Name";
}
/**
* Return the filename of the binary.
* - This is being used to help finding the binary.
*/
filename(): string {
return "No File Name";
}
/**
* Return the fully resolved path to the application's binary
*
* @returns {string}
*
* @memberOf ExternalService
*/
path(): string {
return this.find(this.filename());
}
/**
* Return a list of depending services, using unique service['label']
*
* @returns {string[]}
*
* @memberOf ExternalService
*/
dependencies(): string[] {
return [];
}
//
// ─── API ────────────────────────────────────────────────────────────────
//
// TODO: macros?
async start(): Promise<any> {
NOIMPL('start');
}
async stop(): Promise<any> {
NOIMPL('stop');
}
async pause(): Promise<any> {
NOIMPL('pause');
}
async resume(): Promise<any> {
NOIMPL('resume');
}
async validate(): Promise<any> {
return Promise.resolve(true);
}
async init(): Promise<any> {
Promise.resolve(true);
}
async run(): Promise<void> {
if (await this.init()) {
if (await this.validate()) {
await this.start();
}
}
}
info(): IExternalServiceDesriptor {
return {
label: this.label(),
status: this.status
} as IExternalServiceDesriptor;
}
//
// ─── UTILS ──────────────────────────────────────────────────────────────────────
//
private _find(filename: string): string {
// 1. try search paths
let result: string = null;
for (let index = 0; index < this.searchPaths.length; index++) {
let test = this.searchPaths[index];
// try path as is
if (exists(test)) {
result = test;
break;
}
// try with file Name
if (exists(path.join(test, filename))) {
result = path.join(test, filename);
break;
}
}
return result;
}
private _findInWindows(filename: string): string {
return this._find(filename);
}
private _findInLinux(filename: string): string {
let result: string = this._find(filename);
const which = require('which');
if (!result && which) {
try {
const whichResult: string = which.sync(filename);
if (whichResult) {
result = whichResult;
}
} catch (e) {
}
}
return result;
}
find(filename: string = this.filename()): string {
switch (os.platform()) {
case 'win32': {
return this._findInWindows(filename);
}
case 'darwin':
case 'linux': {
return this._findInLinux(filename);
}
}
}
}