actionhero
Version:
The reusable, scalable, and quick node.js API server for stateless and stateful applications
196 lines (170 loc) • 5.15 kB
text/typescript
import { EventEmitter } from "stream";
import * as uuid from "uuid";
import { ActionProcessor } from "../classes/actionProcessor";
import { connectionVerbs } from "../classes/connection";
import {
api,
config,
log,
env,
Initializer,
Server,
Connection,
} from "../index";
import { SpecHelperConnection } from "../modules/specHelper";
export interface SpecHelperApi {
returnMetadata?: boolean;
// Server?: Server;
Server?: any;
Connection?: any;
}
/**
* A special "mock" server which enables you to test actions and tasks in a simple way. Only available in the TEST environment.
*/
export class SpecHelper extends Initializer {
enabled: boolean;
constructor() {
super();
this.name = "specHelper";
this.loadPriority = 900;
this.startPriority = 901;
this.enabled = false;
}
async initialize() {
if (env === "test") this.enabled = true;
if (!this.enabled) return;
class TestServer extends Server {
constructor() {
super();
this.type = "testServer";
this.attributes = {
canChat: true,
logConnections: false,
logExits: false,
sendWelcomeMessage: true,
verbs: connectionVerbs,
};
}
async initialize() {}
async start() {
log("loading the testServer", "info");
this.on("connection", (connection) => {
this.handleConnection(connection);
});
this.on("actionComplete", (data) => {
this.actionComplete(data);
});
}
async stop() {}
async sendMessage(
connection: SpecHelperConnection,
message: any,
messageId: string | number,
) {
process.nextTick(() => {
connection.messages.push(message);
if (typeof connection.actionCallbacks[messageId] === "function") {
connection.actionCallbacks[messageId](message, connection);
delete connection.actionCallbacks[messageId];
}
});
}
async sendFile(
connection: Connection,
error: NodeJS.ErrnoException,
fileStream: EventEmitter,
mime: string,
length: number,
) {
let content = "";
const messageId = connection.messageId;
const response = {
content: null as string,
mime: mime,
length: length,
error: undefined as Error,
};
if (error) {
response.error = error;
}
try {
if (!error) {
fileStream.on("data", (d) => (content += d));
fileStream.on("end", () => {
response.content = content;
this.sendMessage(connection, response, messageId);
});
} else {
this.sendMessage(connection, response, messageId);
}
} catch (e) {
this.log(e, "warning");
this.sendMessage(connection, response, messageId);
}
}
handleConnection(connection: SpecHelperConnection) {
connection.messages = [];
connection.actionCallbacks = {};
}
async actionComplete(data: ActionProcessor<any>) {
data.response.error;
if (typeof data.response === "string" || Array.isArray(data.response)) {
// nothing to do...
} else {
if (data.response.error) {
data.response.error =
await config.errors.serializers.servers.specHelper(
data.response.error,
);
}
if (api.specHelper.returnMetadata) {
data.response.messageId = data.messageId;
data.response.serverInformation = {
serverName: config.general.serverName,
apiVersion: config.general.apiVersion,
};
data.response.requesterInformation = {
id: data.connection.id,
remoteIP: data.connection.remoteIP,
receivedParams: {},
};
for (const k in data.params) {
data.response.requesterInformation.receivedParams[k] =
data.params[k];
}
}
}
if (data.toRender === true) {
this.sendMessage(data.connection, data.response, data.messageId);
}
}
}
api.specHelper = {
returnMetadata: true,
Server: TestServer,
};
/**
* A special connection usable in tests. Create via `await api.specHelper.Connection.createAsync()`
*/
api.specHelper.Connection = class {
static async createAsync() {
const id = uuid.v4();
await api.servers.servers.testServer.buildConnection({
id: id,
fingerprint: id,
rawConnection: {},
remoteAddress: "testServer",
remotePort: 0,
});
return api.connections.connections[id];
}
};
}
async start() {
if (!this.enabled) return;
const server = new api.specHelper.Server();
server.config = { enabled: true };
await server.start(api);
api.servers.servers.testServer = server;
}
}