Browse Source

create SyncDish and wrap results in it.

d98762625 7 years ago
parent
commit
45d2fbc5fc
4 changed files with 177 additions and 52 deletions
  1. 1 1
      Gruntfile.js
  2. 22 47
      src/node/apiUtils.mjs
  3. 3 4
      src/node/config/scripts/generateNodeIndex.mjs
  4. 151 0
      src/node/syncDish.mjs

+ 1 - 1
Gruntfile.js

@@ -393,7 +393,7 @@ module.exports = function (grunt) {
                     "mkdir -p src/core/config/modules",
                     "echo 'export default {};\n' > src/core/config/modules/OpModules.mjs",
                     "echo '[]\n' > src/core/config/OperationConfig.json",
-                    "node --experimental-modules src/core/config/scripts/generateNodeIndex.mjs",
+                    "node --experimental-modules src/node/config/scripts/generateNodeIndex.mjs",
                     "echo '--- Node index finished. ---\n'"
                 ].join(";"),
             },

+ 22 - 47
src/node/apiUtils.mjs

@@ -1,12 +1,12 @@
 /**
- * Wrap operations for consumption in Node
+ * Wrap operations for consumption in Node.
  *
  * @author d98762625 [d98762625@gmail.com]
  * @copyright Crown Copyright 2018
  * @license Apache-2.0
  */
 
-import Dish from "../core/Dish";
+import SyncDish from "./SyncDish";
 
 /**
  * Extract default arg value from operation argument
@@ -59,61 +59,36 @@ function transformArgs(originalArgs, newArgs) {
  * @returns {Function} The operation's run function, wrapped in
  * some type conversion logic
  */
-export function wrap(Operation) {
+export function wrap(opClass) {
     /**
      * Wrapped operation run function
+     * @param {*} input
+     * @param {Object[]} args
+     * @returns {SyncDish} operation's output, on a Dish.
+     * @throws {OperationError} if the operation throws one.
      */
-    return async (input, args=null, callback) => {
+    return (input, args=null) => {
+        const operation = new opClass();
 
-        if (callback && typeof callback !== "function") {
-            throw TypeError("Expected callback to be a function");
-        }
-
-        if (!callback && typeof args === "function") {
-            callback = args;
-            args = null;
+        let dish;
+        if (input instanceof SyncDish) {
+            dish = input;
+        } else {
+            dish = new SyncDish();
+            const type = SyncDish.typeEnum(input.constructor.name);
+            dish.set(input, type);
         }
 
-        const operation = new Operation();
-        const dish = new Dish();
-
-        const type = Dish.typeEnum(input.constructor.name);
-        dish.set(input, type);
-
         args = transformArgs(operation.args, args);
-        const transformedInput = await dish.get(operation.inputType);
-
-        // Allow callback or promsise / async-await
-        if (callback) {
-            try {
-                const out = operation.run(transformedInput, args);
-                callback(null, out);
-            } catch (e) {
-                callback(e);
-            }
-        } else {
-            return operation.run(transformedInput, args);
-        }
+        const transformedInput = dish.get(operation.inputType);
+        const result = operation.run(transformedInput, args);
+        return new SyncDish({
+            value: result,
+            type: operation.outputType
+        });
     };
 }
 
-
-/**
- * First draft
- * @namespace Api
- * @param input
- * @param type
- */
-export async function translateTo(input, type) {
-    const dish = new Dish();
-
-    const initialType = Dish.typeEnum(input.constructor.name);
-
-    dish.set(input, initialType);
-    return await dish.get(type);
-}
-
-
 /**
  * Extract properties from an operation by instantiating it and
  * returning some of its properties for reference.

+ 3 - 4
src/core/config/scripts/generateNodeIndex.mjs → src/node/config/scripts/generateNodeIndex.mjs

@@ -13,8 +13,8 @@
 
 import fs from "fs";
 import path from "path";
-import * as operations from "../../operations/index";
-import { decapitalise } from "../../../node/apiUtils";
+import * as operations from "../../../core/operations/index";
+import { decapitalise } from "../../apiUtils";
 
 const dir = path.join(`${process.cwd()}/src/node`);
 if (!fs.existsSync(dir)) {
@@ -36,7 +36,7 @@ let code = `/**
 
 
 import "babel-polyfill";
-import { wrap, translateTo } from "./apiUtils";
+import { wrap } from "./apiUtils";
 import {
 `;
 
@@ -76,7 +76,6 @@ code += `    };
 }
 
 const chef = generateChef();
-chef.translateTo = translateTo;
 `;
 
 Object.keys(operations).forEach((op) => {

+ 151 - 0
src/node/syncDish.mjs

@@ -0,0 +1,151 @@
+/**
+ * @author d98762625 [d98762625@gmail.com]
+ * @copyright Crown Copyright 2018
+ * @license Apache-2.0
+ */
+
+import Utils from "../core/Utils";
+import Dish from "../core/Dish";
+import BigNumber from "bignumber.js";
+import log from "loglevel";
+
+/**
+ * Subclass of Dish where `get` and `_translate` are synchronous.
+ * Also define functions to improve coercion behaviour.
+ */
+class SyncDish extends Dish {
+
+    /**
+     * Synchronously returns the value of the data in the type format specified.
+     *
+     * @param {number} type - The data type of value, see Dish enums.
+     * @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
+     * @returns {*} - The value of the output data.
+     */
+    get(type, notUTF8=false) {
+        if (typeof type === "string") {
+            type = Dish.typeEnum(type);
+        }
+        if (this.type !== type) {
+            this._translate(type, notUTF8);
+        }
+        return this.value;
+    }
+
+    /**
+     * alias for get
+     * @param args see get args
+     */
+    to(...args) {
+        return this.get(...args);
+    }
+
+    /**
+     * Avoid coercion to a String primitive.
+     */
+    toString() {
+        return this.get(Dish.typeEnum("string"));
+    }
+
+    /**
+     * Log only the value to the console in node.
+     */
+    inspect() {
+        return this.get(Dish.typeEnum("string"));
+    }
+
+    /**
+     * Avoid coercion to a Number primitive.
+     */
+    valueOf() {
+        return this.get(Dish.typeEnum("number"));
+    }
+
+    /**
+     * Synchronously translates the data to the given type format.
+     *
+     * @param {number} toType - The data type of value, see Dish enums.
+     * @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
+     */
+    _translate(toType, notUTF8=false) {
+        log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`);
+        const byteArrayToStr = notUTF8 ? Utils.byteArrayToChars : Utils.byteArrayToUtf8;
+
+        // Convert data to intermediate byteArray type
+        switch (this.type) {
+            case Dish.STRING:
+                this.value = this.value ? Utils.strToByteArray(this.value) : [];
+                break;
+            case Dish.NUMBER:
+                this.value = typeof this.value === "number" ? Utils.strToByteArray(this.value.toString()) : [];
+                break;
+            case Dish.HTML:
+                this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : [];
+                break;
+            case Dish.ARRAY_BUFFER:
+                // Array.from() would be nicer here, but it's slightly slower
+                this.value = Array.prototype.slice.call(new Uint8Array(this.value));
+                break;
+            case Dish.BIG_NUMBER:
+                this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : [];
+                break;
+            case Dish.JSON:
+                this.value = this.value ? Utils.strToByteArray(JSON.stringify(this.value)) : [];
+                break;
+            // case Dish.FILE:
+            //     this.value = Utils.readFileSync(this.value);
+            //     this.value = Array.prototype.slice.call(this.value);
+            //     break;
+            // case Dish.LIST_FILE:
+            //     this.value = this.value.map(f => Utils.readFileSync(f));
+            //     this.value = this.value.map(b => Array.prototype.slice.call(b));
+            //     this.value = [].concat.apply([], this.value);
+            //     break;
+            default:
+                break;
+        }
+
+        this.type = Dish.BYTE_ARRAY;
+
+        // Convert from byteArray to toType
+        switch (toType) {
+            case Dish.STRING:
+            case Dish.HTML:
+                this.value = this.value ? byteArrayToStr(this.value) : "";
+                this.type = Dish.STRING;
+                break;
+            case Dish.NUMBER:
+                this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0;
+                this.type = Dish.NUMBER;
+                break;
+            case Dish.ARRAY_BUFFER:
+                this.value = new Uint8Array(this.value).buffer;
+                this.type = Dish.ARRAY_BUFFER;
+                break;
+            case Dish.BIG_NUMBER:
+                try {
+                    this.value = new BigNumber(byteArrayToStr(this.value));
+                } catch (err) {
+                    this.value = new BigNumber(NaN);
+                }
+                this.type = Dish.BIG_NUMBER;
+                break;
+            case Dish.JSON:
+                this.value = JSON.parse(byteArrayToStr(this.value));
+                this.type = Dish.JSON;
+                break;
+            // case Dish.FILE:
+            //     this.value = new File(this.value, "unknown");
+            //     break;
+            // case Dish.LIST_FILE:
+            //     this.value = [new File(this.value, "unknown")];
+            //     this.type = Dish.LIST_FILE;
+            //     break;
+            default:
+                break;
+        }
+    }
+
+}
+
+export default SyncDish;