diff --git a/src/client/action/ClientAction.ts b/src/client/action/ClientAction.ts
new file mode 100644
index 0000000..da57ebb
--- /dev/null
+++ b/src/client/action/ClientAction.ts
@@ -0,0 +1,39 @@
+/**
+ * Representation of actions to be performed by the client
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+
+/**
+ * Action to be performed by the client
+ *
+ * TODO: More specific types
+ */
+export interface ClientAction
+{
+ /** Action to be performed */
+ action: string;
+
+ /** Action arguments */
+ [P: string]: any;
+}
+
+
+/** Set of actions */
+export type ClientActions = ClientAction[];
diff --git a/src/program/Program.d.ts b/src/program/Program.d.ts
new file mode 100644
index 0000000..de3f45a
--- /dev/null
+++ b/src/program/Program.d.ts
@@ -0,0 +1,27 @@
+/**
+ * Contains Program base class
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+export declare abstract class Program
+{
+ readonly ineligibleLockCount: number;
+
+ getId(): string;
+}
diff --git a/src/quote/BaseQuote.d.ts b/src/quote/BaseQuote.d.ts
new file mode 100644
index 0000000..9af47ee
--- /dev/null
+++ b/src/quote/BaseQuote.d.ts
@@ -0,0 +1,93 @@
+/**
+ * Contains program Quote class
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * @todo Use ``document'' terminology in place of ``quote''
+ */
+
+import { Program } from "../program/Program";
+import { Quote, QuoteId } from "./Quote";
+
+
+export declare class BaseQuote implements Quote
+{
+ /**
+ * Retrieve Program associated with quote
+ *
+ * @return quote program
+ */
+ getProgram(): Program;
+
+
+ /**
+ * Returns the program id associated with the quote
+ *
+ * @return program id
+ */
+ getProgramId(): string;
+
+
+ /**
+ * Returns the quote id
+ *
+ * The quote id is immutable. A different quote id would represent a
+ * different quote, therefore a new object should be created with the
+ * desired quote id.
+ *
+ * @return quote id
+ */
+ getId(): QuoteId;
+
+
+ /**
+ * Returns the id of the current step
+ *
+ * @return id of current step
+ */
+ getCurrentStepId(): number;
+
+
+ /**
+ * Sets an explicit lock, providing a reason for doing so
+ *
+ * @param reason - lock reason
+ * @param step - step that user may not navigate prior
+ *
+ * @return self
+ */
+ setExplicitLock( reason: string, step: number ): this;
+
+
+ /**
+ * Set the date that the premium was calculated as a Unix timestamp
+ *
+ * @param timestamp - Unix timestamp representing premium date
+ *
+ * @return self
+ */
+ setLastPremiumDate( timestamp: UnixTimestamp ): this;
+
+
+ /**
+ * Retrieve the last time the premium was calculated
+ *
+ * @return last calculated time or 0
+ */
+ getLastPremiumDate(): UnixTimestamp;
+}
diff --git a/src/quote/Quote.d.ts b/src/quote/Quote.d.ts
new file mode 100644
index 0000000..9324d0f
--- /dev/null
+++ b/src/quote/Quote.d.ts
@@ -0,0 +1,33 @@
+/**
+ * Generic interface to represent quotes
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * @todo Use ``document'' terminology in place of ``quote''
+ */
+
+import { Program } from "../program/Program";
+import { QuoteId } from "../document/Document";
+
+
+export declare interface Quote
+{
+ // TODO: the easejs interface is empty; is this actually needed?
+}
+
+export { QuoteId };
diff --git a/src/server/Server.d.ts b/src/server/Server.d.ts
new file mode 100644
index 0000000..e165d69
--- /dev/null
+++ b/src/server/Server.d.ts
@@ -0,0 +1,66 @@
+/**
+ * General server actions
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import { ClientActions } from "../client/action/ClientAction";
+import { ServerSideQuote } from "./quote/ServerSideQuote";
+import { UserRequest } from "./request/UserRequest";
+
+
+/**
+ * General server actions
+ */
+export declare class Server
+{
+ /**
+ * Send response to user
+ *
+ * @param request - request to respond to
+ * @param quote - quote associated with request
+ * @param data - data with which to reply
+ * @param actions - optional client actions
+ *
+ * @return self
+ */
+ sendResponse(
+ request: UserRequest,
+ quote: ServerSideQuote,
+ data: Record,
+ actions?: ClientActions,
+ ): this;
+
+
+ /**
+ * Send response to user
+ *
+ * @param request - request to respond to
+ * @param message - message to display to user
+ * @param actions - optional client actions
+ * @param btn_caption - optional caption for acknowledgement button
+ *
+ * @return self
+ */
+ sendError(
+ request: UserRequest,
+ message: string,
+ actions?: ClientActions,
+ btn_caption?: string,
+ ): this;
+}
diff --git a/src/server/daemon/controller.js b/src/server/daemon/controller.js
index 5b1b6b0..c027e4e 100644
--- a/src/server/daemon/controller.js
+++ b/src/server/daemon/controller.js
@@ -28,6 +28,8 @@ const {
ReplSetServers: ReplSetServers,
} = require( 'mongodb/lib/mongodb' );
+const easejs = require( 'easejs' );
+
const regex_base = /^\/quote\/([a-z0-9-]+)\/?(?:\/(\d+)\/?(?:\/(.*))?|\/(program.js))?$/;
const regex_step = /^step\/(\d+)\/?(?:\/(post|visit))?$/;
@@ -83,7 +85,7 @@ const {
ExportService,
},
- RatingService,
+ RatingService: { RatingService },
RatingServicePublish,
TokenedService,
},
@@ -136,7 +138,7 @@ exports.init = function( logger, enc_service, conf )
server.init( server_cache, exports.rater );
// TODO: temporary proof-of-concept
- rating_service = RatingService.use(
+ rating_service = easejs( RatingService ).use(
RatingServicePublish( amqplib, exports.post_rate_publish, logger )
)(
logger, dao, server, exports.rater
diff --git a/src/server/db/MongoServerDao.d.ts b/src/server/db/MongoServerDao.d.ts
new file mode 100644
index 0000000..fbf62c0
--- /dev/null
+++ b/src/server/db/MongoServerDao.d.ts
@@ -0,0 +1,146 @@
+/**
+ * Mongo DB DAO for program server
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import { ClassificationData, WorksheetData } from "../rater/Rater";
+import { PositiveInteger } from "../../numeric";
+import { QuoteId } from "../../document/Document";
+import { ServerSideQuote } from "../quote/ServerSideQuote";
+
+/** Success or failure callback */
+type Callback = ( quote: ServerSideQuote ) => void;
+
+
+/**
+ * MongoDB-backed data store
+ */
+export declare class MongoServerDao
+{
+ /**
+ * Saves a quote to the database
+ *
+ * A full save will include all metadata.
+ *
+ * @param quote - the quote to save
+ * @param success - function to call on success
+ * @param failure - function to call if save fails
+ * @param save_data - quote data to save (optional)
+ */
+ saveQuote(
+ quote: ServerSideQuote,
+ success: Callback,
+ failure: Callback,
+ save_data: Record,
+ ): this;
+
+
+ /**
+ * Merges bucket data with the existing bucket (rather than overwriting the
+ * entire bucket)
+ *
+ * @param quote - quote to save
+ * @param data - bucket data
+ * @param success - successful callback
+ * @param failure - failure callback
+ */
+ mergeBucket(
+ quote: ServerSideQuote,
+ data: Record,
+ success: Callback,
+ failure: Callback,
+ ): this;
+
+
+ /**
+ * Save quote classification data
+ *
+ * @param quote - quote to save
+ * @param classes - classification data
+ * @param success - successful callback
+ * @param failure - failure callback
+ */
+ saveQuoteClasses(
+ quote: ServerSideQuote,
+ classes: ClassificationData,
+ success: Callback,
+ failure: Callback,
+ ): this;
+
+
+ /**
+ * Saves the quote state to the database
+ *
+ * The quote state includes the current step, the top visited step and the
+ * explicit lock message.
+ *
+ * @param quote - the quote to save
+ * @param success - function to call on success
+ * @param failure - function to call if save fails
+ */
+ saveQuoteState(
+ quote: ServerSideQuote,
+ success: Callback,
+ failure: Callback,
+ ): this;
+
+
+ /**
+ * Saves the quote lock state to the database
+ *
+ * @param quote - the quote to save
+ * @param success - function to call on success
+ * @param failure - function to call if save fails
+ */
+ saveQuoteLockState(
+ quote: ServerSideQuote,
+ success: Callback,
+ failure: Callback,
+ ): this
+
+
+ /**
+ * Save worksheet data
+ *
+ * @param qid - quote identifier
+ * @param data - worksheet data
+ * @param callback - callback
+ */
+ setWorksheets(
+ qid: QuoteId,
+ data: WorksheetData,
+ callback: NodeCallback,
+ ): this;
+
+
+ /**
+ * Retrieve worksheet data
+ *
+ * @param qid - quote identifier
+ * @param supplier - supplier id
+ * @param index - worksheet index
+ * @param callback - callback
+ */
+ getWorksheet(
+ qid: QuoteId,
+ supplier: string,
+ index: PositiveInteger,
+ callback: ( data: WorksheetData | null ) => void,
+ ): this;
+}
diff --git a/src/server/log/PriorityLog.d.ts b/src/server/log/PriorityLog.d.ts
new file mode 100644
index 0000000..60819a4
--- /dev/null
+++ b/src/server/log/PriorityLog.d.ts
@@ -0,0 +1,45 @@
+/**
+ * Priority log typescript type definitions
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+export declare interface PriorityLog
+{
+ readonly PRIORITY_ERROR: number;
+ readonly PRIORITY_IMPORTANT: number;
+ readonly PRIORITY_DB: number;
+ readonly PRIORITY_INFO: number;
+ readonly PRIORITY_SOCKET: number;
+
+ /**
+ * Write to the log at the given priority
+ *
+ * If the priority is less than or equal to the set priority for this
+ * object, it will be logged. Otherwise, the message will be ignored.
+ *
+ * The first argument should be the priority. The remaining arguments should
+ * be provided in a sprintf()-style fashion
+ *
+ * @param priority - logging priority
+ * @param ...args - sprintf-style aruments
+ *
+ * @return self
+ */
+ log( priority: number, ...args: Array ): this;
+}
diff --git a/src/server/quote/ServerSideQuote.d.ts b/src/server/quote/ServerSideQuote.d.ts
new file mode 100644
index 0000000..98eff98
--- /dev/null
+++ b/src/server/quote/ServerSideQuote.d.ts
@@ -0,0 +1,46 @@
+/**
+ * Augments a quote with additional data for use by the quote server
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * @todo Use ``document'' terminology in place of ``quote''
+ */
+
+import { Program } from "../../program/Program";
+import { BaseQuote } from "../../quote/BaseQuote";
+
+
+export declare class ServerSideQuote extends BaseQuote
+{
+ /**
+ * Last rated date, if any
+ *
+ * @return last rated date
+ */
+ getRatedDate(): UnixTimestamp;
+
+
+ /**
+ * Set the timestamp of the first time quote was rated
+ *
+ * @param timestamp - Unix timestamp representing first rated date
+ *
+ * @return self
+ */
+ setRatedDate( timestamp: UnixTimestamp ): this;
+}
diff --git a/src/server/rater/ProcessManager.d.ts b/src/server/rater/ProcessManager.d.ts
new file mode 100644
index 0000000..23d6705
--- /dev/null
+++ b/src/server/rater/ProcessManager.d.ts
@@ -0,0 +1,35 @@
+/**
+ * Rating process manager
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import { Rater } from "./Rater";
+
+
+export declare class ProcessManager
+{
+ /**
+ * Returns the rater associated with the given id
+ *
+ * @param id - rater id
+ *
+ * @return requested rater
+ */
+ byId( id: string ): Rater;
+}
diff --git a/src/server/rater/ProcessManager.js b/src/server/rater/ProcessManager.js
index a5a67b1..13c7fe2 100644
--- a/src/server/rater/ProcessManager.js
+++ b/src/server/rater/ProcessManager.js
@@ -69,6 +69,9 @@ const _signum = {
*
* Handles formatting and sending requests to the rating process; and
* processing replies.
+ *
+ * TODO: Rename this class and provide a more generic interface. The caller
+ * does not care that this sends data to another process for rating.
*/
module.exports = Class( 'ProcessManager',
{
diff --git a/src/server/rater/Rater.d.ts b/src/server/rater/Rater.d.ts
new file mode 100644
index 0000000..d9dcb1b
--- /dev/null
+++ b/src/server/rater/Rater.d.ts
@@ -0,0 +1,121 @@
+/**
+ * Contains Rater interface
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import { ClientActions } from "../../client/action/ClientAction";
+import { ServerSideQuote } from "../quote/ServerSideQuote";
+import { UserSession } from "../request/UserSession";
+
+
+
+/** Result of rating */
+export interface RateResult
+{
+ /** Whether all suppliers were not able to provide rates */
+ _unavailable_all: '0' | '1';
+
+ /** Result data */
+ [P: string]: any;
+}
+
+
+/**
+ * Result of rating with an individual supplier
+ *
+ * This gets combined into a single RateResult prefixed with each supplier
+ * id and an underscore.
+ */
+export interface SupplierRateResult
+{
+ /** Rating worksheet data */
+ __worksheet?: WorksheetData,
+
+ /** Classification system results */
+ __classes?: ClassificationData,
+
+ /** Basic profiling data */
+ __perf: PerformanceData,
+
+ /** Ineligible message, if any */
+ ineligible: string,
+
+ /** Submit message, if any */
+ submit: string,
+
+ /** Final premium */
+ premium: number,
+
+ /** Rating data */
+ [P: string]: any;
+}
+
+
+/** Basic profiling data */
+export interface PerformanceData
+{
+ /** Timestamp of beginning of rating */
+ start: UnixTimestampMillis;
+
+ /** Timestamp of end of rating */
+ end: UnixTimestampMillis;
+
+ /** Total rating time */
+ total: Milliseconds;
+}
+
+
+/**
+ * Worksheet data from rater
+ *
+ * These data come from the compiled raters.
+ *
+ * TODO: Fill in a schema here
+ */
+export type WorksheetData = Record;
+
+
+/** Classification results */
+export type ClassificationData = Record;
+
+
+/**
+ * Represents a rater that will generate a quote from a given set of values
+ */
+export interface Rater
+{
+ /**
+ * Asynchronously performs rating
+ *
+ * @param quote - quote to perform rating on
+ * @param session - user session
+ * @param indv - individual supplier to rate (otherwise empty)
+ * @param success - continuation when rating is successful
+ * @param failure - continuation when rating fails
+ *
+ * @return self
+ */
+ rate(
+ quote: ServerSideQuote,
+ session: UserSession,
+ indv: string,
+ success: ( rate_data: RateResult, actions: ClientActions ) => void,
+ failure: ( message: string ) => void,
+ ): this;
+}
diff --git a/src/server/request/UserRequest.d.ts b/src/server/request/UserRequest.d.ts
new file mode 100644
index 0000000..3449963
--- /dev/null
+++ b/src/server/request/UserRequest.d.ts
@@ -0,0 +1,36 @@
+/**
+ * User request abstraction
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import { UserSession } from "./UserSession";
+
+
+/**
+ * Representation of request from user
+ */
+export declare class UserRequest
+{
+ /**
+ * Retrieve the current session
+ *
+ * @return current session
+ */
+ getSession(): UserSession;
+}
diff --git a/src/server/request/UserResponse.d.ts b/src/server/request/UserResponse.d.ts
new file mode 100644
index 0000000..7ba776f
--- /dev/null
+++ b/src/server/request/UserResponse.d.ts
@@ -0,0 +1,28 @@
+/**
+ * UserResponse class
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+
+/**
+ * Manipulates response to user request
+ */
+export declare class UserResponse
+{
+}
diff --git a/src/server/request/UserSession.d.ts b/src/server/request/UserSession.d.ts
new file mode 100644
index 0000000..01937d7
--- /dev/null
+++ b/src/server/request/UserSession.d.ts
@@ -0,0 +1,34 @@
+/**
+ * UserSession class
+ *
+ * Copyright (C) 2010-2019 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+
+/**
+ * Session management
+ */
+export declare class UserSession
+{
+ /**
+ * Whether the user is logged in as an internal user
+ *
+ * @return true if internal user, otherwise false
+ */
+ isInternal(): boolean;
+}
diff --git a/src/server/service/RatingService.js b/src/server/service/RatingService.ts
similarity index 57%
rename from src/server/service/RatingService.js
rename to src/server/service/RatingService.ts
index aa9e53f..d0b33dc 100644
--- a/src/server/service/RatingService.js
+++ b/src/server/service/RatingService.ts
@@ -19,33 +19,60 @@
* along with this program. If not, see .
*/
-var Class = require( 'easejs' ).Class;
+import { ClientActions } from "../../client/action/ClientAction";
+import { MongoServerDao } from "../db/MongoServerDao";
+import { PositiveInteger } from "../../numeric";
+import { PriorityLog } from "../log/PriorityLog";
+import { ProcessManager } from "../rater/ProcessManager";
+import { Program } from "../../program/Program";
+import { QuoteId } from "../../quote/Quote";
+import { ClassificationData, RateResult, WorksheetData } from "../rater/Rater";
+import { Server } from "../Server";
+import { ServerSideQuote } from "../quote/ServerSideQuote";
+import { UserRequest } from "../request/UserRequest";
+import { UserResponse } from "../request/UserResponse";
+
+type RequestCallback = () => void;
/**
- * XXX: Half-assed, quick refactoring to extract from Server class; this is not
- * yet complete!
+ * Handle rating requests
+ *
+ * XXX: This class was extracted from Server and needs additional
+ * refactoring, testing, and cleanup.
*
* TODO: Logging should be implemented by observers
*/
-module.exports = Class( 'RatingService',
+export class RatingService
{
- logger: null,
-
- dao: null,
-
- _server: null,
-
- _raters: null,
+ /**
+ * Initialize rating service
+ *
+ * @param _logger - logging system
+ * @param _dao - database connection
+ * @param _server - server actions
+ * @param _rater_manager - rating manager
+ */
+ constructor(
+ private readonly _logger: PriorityLog,
+ private readonly _dao: MongoServerDao,
+ private readonly _server: Server,
+ private readonly _rater_manager: ProcessManager,
+ ) {}
- __construct: function( logger, dao, server, raters )
+ /**
+ * TODO: Remove once traits subtypes are converted to TS
+ *
+ * This works around an easejs bug where prototype constructors are not
+ * properly invoked. Note that technically the constructor above is
+ * invoked twice by easejs: once with no arguments, and again when
+ * calling this method with the proper arguments.
+ */
+ __construct()
{
- this._logger = logger;
- this._dao = dao;
- this._server = server;
- this._raters = raters;
- },
+ (RatingService ).apply( this, arguments );
+ }
/**
@@ -54,15 +81,21 @@ module.exports = Class( 'RatingService',
* Note that the continuation will be called after all data saving is
* complete; the request will be sent back to the client before then.
*
- * @param {UserRequest} request user request to satisfy
- * @param {UserResponse} response pending response
- * @param {Quote} quote quote to export
- * @param {string} cmd applicable of command request
- * @param {Function} callback continuation after saving is complete
+ * @param request - user request to satisfy
+ * @param _response - pending response
+ * @param quote - quote to export
+ * @param cmd - applicable of command request
+ * @param callback - continuation after saving is complete
*
* @return Server self to allow for method chaining
*/
- 'public request': function( request, response, quote, cmd, callback )
+ request(
+ request: UserRequest,
+ _response: UserResponse,
+ quote: ServerSideQuote,
+ cmd: string,
+ callback: RequestCallback
+ )
{
// cmd represents a request for a single rater
if ( !cmd && this._isQuoteValid( quote ) )
@@ -89,12 +122,12 @@ module.exports = Class( 'RatingService',
}
return this;
- },
+ }
- _getProgramRater: function( program, quote )
+ private _getProgramRater( program: Program, quote: ServerSideQuote )
{
- var rater = this._raters.byId( program.getId() );
+ var rater = this._rater_manager.byId( program.getId() );
// if a rater could not be found, we can't do any rating
if ( rater === null )
@@ -107,10 +140,20 @@ module.exports = Class( 'RatingService',
}
return rater;
- },
+ }
- _isQuoteValid: function( quote )
+ /**
+ * Whether quote is still valid
+ *
+ * TODO: This class shouldn't be making this determination, and this
+ * method is nondeterministic.
+ *
+ * @param quote - quote to check
+ *
+ * @return whether quote is still valid
+ */
+ private _isQuoteValid( quote: ServerSideQuote ): boolean
{
// quotes are valid for 30 days
var re_date = Math.round( ( ( new Date() ).getTime() / 1000 ) -
@@ -129,14 +172,19 @@ module.exports = Class( 'RatingService',
}
return false;
- },
+ }
- _performRating: function( request, program, quote, indv, c )
+ private _performRating(
+ request: UserRequest,
+ program: Program,
+ quote: ServerSideQuote,
+ indv: string,
+ c: RequestCallback,
+ )
{
- var _self = this;
+ var rater = this._getProgramRater( program, quote );
- var rater = this._getProgramRater( program );
if ( !rater )
{
this._server.sendError( request, 'Unable to perform rating.' );
@@ -150,49 +198,51 @@ module.exports = Class( 'RatingService',
);
rater.rate( quote, request.getSession(), indv,
- function( rate_data, actions )
+ ( rate_data: RateResult, actions: ClientActions ) =>
{
actions = actions || [];
- _self.postProcessRaterData(
+ this.postProcessRaterData(
request, rate_data, actions, program, quote
);
const class_dest = {};
- const cleaned = _self._cleanRateData(
+ const cleaned = this._cleanRateData(
rate_data,
class_dest
);
// TODO: move me during refactoring
- _self._dao.saveQuoteClasses( quote, class_dest );
+ this._dao.saveQuoteClasses(
+ quote, class_dest, () => {}, () => {}
+ );
// save all data server-side (important: do after
// post-processing); async
- _self._saveRatingData( quote, rate_data, indv, function()
+ this._saveRatingData( quote, rate_data, indv, function()
{
// we're done
c();
} );
// no need to wait for the save; send the response
- _self._server.sendResponse( request, quote, {
+ this._server.sendResponse( request, quote, {
data: cleaned,
initialRatedDate: quote.getRatedDate(),
lastRatedDate: quote.getLastPremiumDate()
}, actions );
},
- function( message )
+ ( message: string ) =>
{
- _self._sendRatingError( request, quote, program,
+ this._sendRatingError( request, quote, program,
Error( message )
);
c();
}
);
- },
+ }
/**
@@ -202,32 +252,32 @@ module.exports = Class( 'RatingService',
* this is to allow us to reference the data (e.g. for reporting) even if
* the client does not save it.
*
- * @param {Quote} quote quote to save data to
- * @param {Object} data rating data
- *
- * @return {undefined}
+ * @param quote - quote to save data to
+ * @param data - rating data
+ * @param indv - individual supplier, or empty
+ * @param c - callback
*/
- _saveRatingData: function( quote, data, indv, c )
+ private _saveRatingData(
+ quote: ServerSideQuote,
+ data: RateResult,
+ indv: string,
+ c: RequestCallback
+ ): void
{
// only update the last premium calc date on the initial request
if ( !indv )
{
- var cur_date = Math.round(
+ var cur_date = Math.round(
( new Date() ).getTime() / 1000
);
quote.setLastPremiumDate( cur_date );
quote.setRatedDate( cur_date );
- function done()
- {
- c();
- }
-
// save the last prem status (we pass an empty object as the save
// data argument to ensure that we do not save the actual bucket
// data, which may cause a race condition with the below merge call)
- this._dao.saveQuote( quote, done, done, {} );
+ this._dao.saveQuote( quote, c, c, {} );
}
else
{
@@ -239,24 +289,26 @@ module.exports = Class( 'RatingService',
// user a rate (if this save fails, it's likely we have bigger problems
// anyway); this can also be done concurrently with the above request
// since it only modifies a portion of the bucket
- this._dao.mergeBucket( quote, data );
- },
+ this._dao.mergeBucket( quote, data, () => {}, () => {} );
+ }
/**
* Process rater data returned from a rater
*
- * @param {UserRequest} request user request to satisfy
- * @param {Object} data rating data returned
- * @param {Array} actions actions to send to client
- * @param {Program} program program used to perform rating
- * @param {Quote} quote quote used for rating
- *
- * @return {undefined}
+ * @param _request - user request to satisfy
+ * @param data - rating data returned
+ * @param actions - actions to send to client
+ * @param program - program used to perform rating
+ * @param quote - quote used for rating
*/
- 'virtual protected postProcessRaterData': function(
- request, data, actions, program, quote
- )
+ protected postProcessRaterData(
+ _request: UserRequest,
+ data: RateResult,
+ actions: ClientActions,
+ program: Program,
+ quote: ServerSideQuote,
+ ): void
{
var meta = data._cmpdata || {};
@@ -284,18 +336,18 @@ module.exports = Class( 'RatingService',
// have a race condition with async. rating (the /visit request may
// be made while we're rating, and when we come back we would then
// update the step id with a prior, incorrect step)
- this._dao.saveQuoteLockState( quote );
+ this._dao.saveQuoteLockState( quote, () => {}, () => {} );
}
// if any have been deferred, instruct the client to request them
// individually
if ( Array.isArray( meta.deferred ) && ( meta.deferred.length > 0 ) )
{
- var torate = [];
+ var torate: string[] = [];
- meta.deferred.forEach( function( alias )
+ meta.deferred.forEach( ( alias: string ) =>
{
- actions.push( { action: 'indvRate', id: alias } );
+ actions.push( { action: 'indvRate', after: alias } );
torate.push( alias );
} );
@@ -308,17 +360,29 @@ module.exports = Class( 'RatingService',
torate.join( ',' )
);
}
- },
+ }
- _sendRatingError: function( request, quote, program, err )
+ /**
+ * Send rating error to user and log
+ *
+ * @param request - user request to satisfy
+ * @param quote - problem quote
+ * @param err - error
+ */
+ private _sendRatingError(
+ request: UserRequest,
+ quote: ServerSideQuote,
+ program: Program,
+ err: Error,
+ ): void
{
// well that's no good
this._logger.log( this._logger.PRIORITY_ERROR,
"Rating for quote %d (program %s) failed: %s",
quote.getId(),
program.getId(),
- err.message + '\n-!' + err.stack.replace( /\n/g, '\n-!' )
+ err.message + '\n-!' + ( err.stack || "" ).replace( /\n/g, '\n-!' )
);
this._server.sendError( request,
@@ -328,18 +392,25 @@ module.exports = Class( 'RatingService',
// show details for internal users
( ( request.getSession().isInternal() )
? '
[Internal] ' + err.message + '
' +
- '
' + err.stack.replace( /\n/g, '
' )
+ '
' + ( err.stack || "" ).replace( /\n/g, '
' )
: ''
)
);
- },
+ }
- _processWorksheetData: function( qid, data )
+ /**
+ * Process and save worksheet data from rating results
+ *
+ * @param qid - quote id
+ * @param data - rating result
+ */
+ private _processWorksheetData( qid: QuoteId, data: RateResult ): void
{
// TODO: this should be done earlier on, so that this is not necessary
- var wre = /^(.+)___worksheet$/,
- worksheets = {};
+ const wre = /^(.+)___worksheet$/;
+
+ const worksheets: Record = {};
// extract worksheets for each supplier
for ( var field in data )
@@ -354,33 +425,44 @@ module.exports = Class( 'RatingService',
}
}
- var _self = this;
- this._dao.setWorksheets( qid, worksheets, function( err )
+ this._dao.setWorksheets( qid, worksheets, ( err: Error | null ) =>
{
if ( err )
{
- _self._logger.log( this._logger.PRIORITY_ERROR,
+ this._logger.log( this._logger.PRIORITY_ERROR,
"Failed to save rating worksheets for quote %d",
- quote.getId(),
- err.message + '\n-!' + err.stack.replace( /\n/g, '\n-!' )
+ qid,
+ err.message + '\n-!' + ( err.stack || "" ).replace( /\n/g, '\n-!' )
);
}
} );
- },
+ }
- serveWorksheet: function( request, quote, supplier, index )
+ /**
+ * Serve worksheet data to user
+ *
+ * @param request - user request to satisfy
+ * @param quote - quote from which to look up worksheet data
+ * @param supplier - supplier name
+ * @param index - worksheet index
+ */
+ serveWorksheet(
+ request: UserRequest,
+ quote: ServerSideQuote,
+ supplier: string,
+ index: PositiveInteger,
+ ): void
{
- var qid = quote.getId(),
- _self = this;
+ var qid = quote.getId();
- this._dao.getWorksheet( qid, supplier, index, function( data )
+ this._dao.getWorksheet( qid, supplier, index, data =>
{
- _self._server.sendResponse( request, quote, {
+ this._server.sendResponse( request, quote, {
data: data
} );
} );
- },
+ }
/**
@@ -389,15 +471,18 @@ module.exports = Class( 'RatingService',
* There are certain data saved server-side that there is no use serving to
* the client.
*
- * @param {Object} data rate data
+ * @param data - rate data
+ * @param classes - classification data
*
- * @return {Object} modified rate data
+ * @return modified rate data
*/
- 'private _cleanRateData': function( data, classes )
+ private _cleanRateData(
+ data: RateResult,
+ classes: ClassificationData
+ ): RateResult
{
- classes = classes || {};
-
- var result = {};
+ // forceful cast because the below loop will copy everything
+ const result = {};
// clear class data
for ( var key in data )
@@ -415,6 +500,5 @@ module.exports = Class( 'RatingService',
}
return result;
- },
-} );
-
+ }
+}
diff --git a/src/server/service/RatingServicePublish.js b/src/server/service/RatingServicePublish.js
index fd5a539..ce843f0 100644
--- a/src/server/service/RatingServicePublish.js
+++ b/src/server/service/RatingServicePublish.js
@@ -21,8 +21,8 @@
'use strict';
-const { Trait } = require( 'easejs' );
-const RatingService = require( './RatingService' );
+const { Interface, Trait } = require( 'easejs' );
+const { RatingService } = require( './RatingService' );
/**
@@ -51,7 +51,8 @@ const RatingService = require( './RatingService' );
* See the body of `#_sendMessage' for their values.
*/
module.exports = Trait( 'RatingServicePublish' )
- .extend( RatingService,
+ .implement( Interface( { 'postProcessRaterData': [] } ) )
+ .extend(
{
/**
* AMQP library (amqplib API)
@@ -75,7 +76,7 @@ module.exports = Trait( 'RatingServicePublish' )
*
* @type {DebugLog}
*/
- 'private _logger': null,
+ 'private _log': null,
/**
@@ -87,9 +88,9 @@ module.exports = Trait( 'RatingServicePublish' )
*/
__mixin( amqp, conf, logger )
{
- this._amqp = amqp;
- this._conf = conf;
- this._logger = logger;
+ this._amqp = amqp;
+ this._conf = conf;
+ this._log = logger;
},
@@ -104,7 +105,7 @@ module.exports = Trait( 'RatingServicePublish' )
*
* @return {undefined}
*/
- 'override protected postProcessRaterData'(
+ 'abstract override postProcessRaterData'(
request, data, actions, program, quote
)
{
@@ -127,13 +128,13 @@ module.exports = Trait( 'RatingServicePublish' )
quote
);
} )
- .then( () => this._logger.log(
- this._logger.PRIORITY_INFO,
+ .then( () => this._log.log(
+ this._log.PRIORITY_INFO,
"Published quote " + quote.getId() +
" to post-rate exchange '" + exchange + "'"
) )
- .catch( e => this._logger.log(
- this._logger.PRIORITY_ERROR,
+ .catch( e => this._log.log(
+ this._log.PRIORITY_ERROR,
"Post-rate exchange publish failure for quote " +
quote.getId() + ": " + e.message
) );
diff --git a/test/server/service/RatingServiceTest.js b/test/server/service/RatingServiceTest.ts
similarity index 82%
rename from test/server/service/RatingServiceTest.js
rename to test/server/service/RatingServiceTest.ts
index b04c354..8330bea 100644
--- a/test/server/service/RatingServiceTest.js
+++ b/test/server/service/RatingServiceTest.ts
@@ -19,10 +19,9 @@
* along with this program. If not, see .
*/
-'use strict'
+import { expect } from 'chai';
+import { RatingService as Sut } from "../../../src/server/service/RatingService";
-const { expect } = require( 'chai' );
-const Sut = require( '../../../' ).server.service.RatingService;
const RatingServiceStub = require( '../../../' ).test.server.service.RatingServiceStub;
describe( 'RatingService', () =>
@@ -49,15 +48,13 @@ describe( 'RatingService', () =>
done();
};
- const sut = Sut.extend(
+ const sut = new class extends Sut
{
- 'override postProcessRaterData'(
- request, data, actions, program, quote
- )
+ postProcessRaterData()
{
processed = true;
}
- } )( logger, dao, server, raters );
+ }( logger, dao, server, raters );
sut.request( request, response, quote, 'something', () => {} );
} );
@@ -87,9 +84,9 @@ describe( 'RatingService', () =>
quote.getRatedDate = () => initial_date;
- const sut = Sut( logger, dao, server, raters );
+ const sut = new Sut( logger, dao, server, raters );
- server.sendResponse = ( request, quote, resp, actions ) =>
+ server.sendResponse = ( _request: any, _quote: any, resp: any, _actions: any ) =>
{
expect( getLastPremiumDateCallCount ).to.equal( 2 );
expect( resp.initialRatedDate ).to.equal( initial_date );
@@ -98,7 +95,7 @@ describe( 'RatingService', () =>
done();
};
- sut.request( request, response, quote, null, () => {} );
+ sut.request( request, response, quote, "", () => {} );
} );
} );