Encapulate datatypes logic into reusable, tested module.

This commit is contained in:
John Chilton
2020-07-04 13:08:12 -04:00
parent fa20f3ead7
commit dc7eee26bc
14 changed files with 97 additions and 43 deletions
@@ -0,0 +1,7 @@
import { DatatypesMapperModel } from "./model.js";
import { getDatatypes } from "./services";
export async function getDatatypesMapper() {
const typesAndMapping = await getDatatypes();
return new DatatypesMapperModel(typesAndMapping);
}
@@ -0,0 +1 @@
export { getDatatypesMapper } from "./factory.js";
@@ -0,0 +1,27 @@
import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import { getDatatypesMapper } from "./index";
import { typesAndMappingResponse } from "./test_fixtures";
describe("Datatypes/index.js", () => {
let axiosMock;
beforeEach(() => {
axiosMock = new MockAdapter(axios);
});
afterEach(() => {
axiosMock.restore();
});
describe("getDatatypesMapper", () => {
it("should fetch logic from API for comparing datatypes in a hierarchy", async () => {
axiosMock.onGet(`/api/datatypes/types_and_mapping`).reply(200, typesAndMappingResponse);
await getDatatypesMapper().then((mapper) => {
expect(mapper.isSubType("txt", "data")).to.equals(true);
expect(mapper.isSubType("data", "txt")).to.equals(false);
});
});
});
});
@@ -0,0 +1,13 @@
export class DatatypesMapperModel {
constructor(typesAndMapping) {
this.datatypes = typesAndMapping.datatypes;
this.datatypesMapping = typesAndMapping.datatypes_mapping;
}
isSubType(child, parent) {
const mapping = this.datatypesMapping;
child = mapping.ext_to_class_name[child];
parent = mapping.ext_to_class_name[parent];
return mapping.class_to_classes[child] && parent in mapping.class_to_classes[child];
}
}
@@ -0,0 +1,12 @@
import axios from "axios";
import { rethrowSimple } from "utils/simple-error";
import { getAppRoot } from "onload/loadConfig";
export async function getDatatypes() {
try {
const request = await axios.get(`${getAppRoot()}api/datatypes/types_and_mapping`);
return request.data;
} catch (e) {
rethrowSimple(e);
}
}
@@ -0,0 +1,10 @@
import DatatypesMappingJson from "qunit/test-data/json/datatypes.mapping.json";
import DatatypesJson from "qunit/test-data/json/datatypes.json";
import { DatatypesMapperModel } from "./model.js";
export const typesAndMappingResponse = {
datatypes: DatatypesJson,
datatypes_mapping: DatatypesMappingJson,
};
export const testDatatypesMapper = new DatatypesMapperModel(typesAndMappingResponse);
@@ -17,7 +17,8 @@
<script>
import * as d3 from "d3";
import { getAppRoot } from "onload/loadConfig";
import { getDatatypeMapping, getToolPredictions } from "components/Workflow/Editor/modules/services";
import { getDatatypesMapper } from "components/Datatypes";
import { getToolPredictions } from "components/Workflow/Editor/modules/services";
export default {
props: {
@@ -53,10 +54,10 @@ export default {
tool_sequence: toolId,
};
getToolPredictions(requestData).then((responsePred) => {
getDatatypeMapping().then((datatypesMapping) => {
getDatatypesMapper().then((datatypesMapper) => {
const predData = responsePred.predicted_data;
const extToType = datatypesMapping.ext_to_class_name;
const typeToType = datatypesMapping.class_to_classes;
const extToType = datatypesMapper.datatypesMapping.ext_to_class_name;
const typeToType = datatypesMapper.datatypesMapping.class_to_classes;
this.deprecated = predData.is_deprecated;
this.deprecatedMessage = predData.message;
if (responsePred !== null && predData.children.length > 0) {
@@ -33,7 +33,7 @@
:content-id="step.content_id"
:step="step"
:key="key"
:datatypes-mapping="datatypesMapping"
:datatypes-mapper="datatypesMapper"
:get-manager="getManager"
:get-canvas-manager="getCanvasManager"
@onAdd="onAdd"
@@ -95,7 +95,8 @@
</template>
<script>
import { getDatatypes, getModule, getVersions, saveWorkflow, loadWorkflow } from "./modules/services";
import { getDatatypesMapper } from "components/Datatypes";
import { getModule, getVersions, saveWorkflow, loadWorkflow } from "./modules/services";
import {
showWarnings,
showUpgradeMessage,
@@ -179,16 +180,16 @@ export default {
hasChanges: false,
nodeIndex: 0,
nodes: {},
datatypesMapping: {},
datatypesMapper: null,
datatypes: [],
report: {},
activeNode: null,
};
},
created() {
getDatatypes().then((response) => {
this.datatypesMapping = response.datatypes_mapping;
this.datatypes = response.datatypes;
getDatatypesMapper().then((mapper) => {
this.datatypesMapper = mapper;
this.datatypes = mapper.datatypes;
// canvas overview management
this.canvasManager = new WorkflowCanvas(this, this.$refs.canvas);
@@ -26,7 +26,7 @@
<Recommendations
:get-node="getNode"
:get-manager="getManager"
:datatypes-mapping="datatypesMapping"
:datatypes-mapper="datatypesMapper"
@onCreate="onCreate"
/>
</b-popover>
@@ -56,7 +56,7 @@
:input="input"
:get-node="getNode"
:get-manager="getManager"
:datatypes-mapping="datatypesMapping"
:datatypes-mapper="datatypesMapper"
@onAdd="onAddInput"
@onChange="onChange"
/>
@@ -124,7 +124,7 @@ export default {
type: Function,
default: null,
},
datatypesMapping: {
datatypesMapper: {
type: Object,
default: null,
},
@@ -25,7 +25,7 @@ export default {
type: Function,
required: true,
},
datatypesMapping: {
datatypesMapper: {
type: Object,
required: true,
},
@@ -66,7 +66,7 @@ export default {
}
const terminal = new terminalClass({
node: this.getNode(),
datatypesMapping: this.datatypesMapping,
datatypesMapper: this.datatypesMapper,
name: input.name,
input: input,
element: this.$refs.terminal,
@@ -36,7 +36,7 @@ export default {
type: Function,
required: true,
},
datatypesMapping: {
datatypesMapper: {
type: Object,
required: true,
},
@@ -146,10 +146,7 @@ export default {
});
},
_isSubType(child, parent) {
const mapping = this.datatypesMapping;
child = mapping.ext_to_class_name[child];
parent = mapping.ext_to_class_name[parent];
return mapping.class_to_classes[child] && parent in mapping.class_to_classes[child];
return this.datatypesMapper.isSubType(child, parent);
},
},
};
@@ -13,18 +13,6 @@ export async function getVersions(id) {
}
}
export async function getDatatypes() {
try {
const datatypesRequest = await axios.get(`${getAppRoot()}api/datatypes`);
const datatypes = datatypesRequest.data;
const mappingRequest = await axios.get(`${getAppRoot()}api/datatypes/mapping`);
const datatypes_mapping = mappingRequest.data;
return { datatypes, datatypes_mapping };
} catch (e) {
rethrowSimple(e);
}
}
export async function getModule(request_data) {
try {
const { data } = await axios.post(`${getAppRoot()}api/workflows/build_module`, request_data);
@@ -219,7 +219,7 @@ class Terminal extends EventEmitter {
class BaseInputTerminal extends Terminal {
constructor(attr) {
super(attr);
this.datatypesMapping = attr.datatypesMapping;
this.datatypesMapper = attr.datatypesMapper;
this.update(attr.input); // subclasses should implement this...
}
canAccept(other) {
@@ -348,10 +348,7 @@ class BaseInputTerminal extends Terminal {
);
}
_isSubType(child, parent) {
const mapping = this.datatypesMapping;
child = mapping.ext_to_class_name[child];
parent = mapping.ext_to_class_name[parent];
return mapping.class_to_classes[child] && parent in mapping.class_to_classes[child];
return this.datatypesMapper.isSubType(child, parent);
}
_producesAcceptableDatatypeAndOptionalness(other) {
if (!this.optional && !this.multiple && other.optional) {
@@ -1,6 +1,6 @@
/* global QUnit */
import $ from "jquery";
import DatatypesMapping from "qunit/test-data/json/datatypes.mapping.json";
import { testDatatypesMapper } from "components/Datatypes/test_fixtures";
import sinon from "sinon";
import WorkflowNode from "components/Workflow/Editor/Node";
import Terminals from "components/Workflow/Editor/modules/terminals";
@@ -49,7 +49,7 @@ function buildNode(propsData) {
propsData.getCanvasManager = () => {
return {};
};
propsData.datatypesMapping = DatatypesMapping;
propsData.datatypesMapper = testDatatypesMapper;
return new component({
propsData: propsData,
el: "#canvas-container",
@@ -63,7 +63,7 @@ QUnit.module("Input terminal model test", {
this.input = { extensions: ["txt"], multiple: false, optional: false };
const inputEl = $("<div>")[0];
this.input_terminal = new Terminals.InputTerminal({
datatypesMapping: DatatypesMapping,
datatypesMapper: testDatatypesMapper,
element: inputEl,
input: this.input,
node: this.node,
@@ -309,7 +309,7 @@ QUnit.module("Input collection terminal model test", {
this.input = { extensions: ["txt"], collection_types: ["list"] };
const inputEl = $("<div>")[0];
this.input_terminal = new Terminals.InputCollectionTerminal({
datatypesMapping: DatatypesMapping,
datatypesMapper: testDatatypesMapper,
element: inputEl,
input: this.input,
node: this.node,
@@ -869,7 +869,7 @@ QUnit.module("terminal mapping logic", {
}
const inputEl = $("<div>")[0];
const inputTerminal = new Terminals.InputTerminal({
datatypesMapping: DatatypesMapping,
datatypesMapper: testDatatypesMapper,
element: inputEl,
input: input,
});
@@ -887,7 +887,7 @@ QUnit.module("terminal mapping logic", {
}
const inputEl = $("<div>")[0];
const inputTerminal = new Terminals.InputCollectionTerminal({
datatypesMapping: DatatypesMapping,
datatypesMapper: testDatatypesMapper,
element: inputEl,
input: input,
node: node,