Skip to content

SOF-7640: new ade + fix TaggableMixin usage #95

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: chore/SOF-7644
Choose a base branch
from
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@
"underscore.string": "^3.3.4"
},
"devDependencies": {
"@exabyte-io/ade.js": "git+https://github.com/Exabyte-io/ade.js.git#bbb62dc12f4ae7c4ba32edc60e459b8bbbcd232d",
"@exabyte-io/ade.js": "git+https://github.com/Exabyte-io/ade.js.git#51864363d0d9525a2481b733068cb00396806370",
"@exabyte-io/application-flavors.js": "2025.5.10-0",
"@exabyte-io/eslint-config": "2025.5.13-0",
"@exabyte-io/ide.js": "2024.3.26-0",
"@exabyte-io/mode.js": "2024.4.28-0",
"@mat3ra/code": "git+https://github.com/Exabyte-io/code.git#3fbf30e5318c9f1ab7a3a5b967fa57d2b70cad3e",
"@mat3ra/code": "git+https://github.com/Exabyte-io/code.git#72a4dd05240da180f09fcb04db9f469aea4077af",
"@mat3ra/esse": "2025.4.26-0",
"@mat3ra/made": "git+https://github.com/Exabyte-io/made.git#84807b83b15dfd632fae9495be301aa7df503b11",
"chai": "^4.3.4",
Expand Down
25 changes: 7 additions & 18 deletions src/subworkflows/create.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Application } from "@exabyte-io/ade.js";
import AdeFactoryDefault from "@exabyte-io/ade.js/dist/js/AdeFactory";
import {
default_methods as MethodConfigs,
default_models as ModelConfigs,
Expand All @@ -14,17 +14,6 @@ import { workflowData as allWorkflowData } from "../workflows/workflows";
import { dynamicSubworkflowsByApp, getSurfaceEnergySubworkflowUnits } from "./dynamic";
import { Subworkflow } from "./subworkflow";

/**
* @summary Thin wrapper around Application.createFromStored for extensibility
* @param config {Object} application config
* @param applicationCls {any} application class
* @returns {Application} the application
*/
function createApplication({ config, applicationCls }) {
const { name, version, build = "Default" } = config;
return applicationCls.create({ name, version, build });
}

// NOTE: DFTModel => DFTModelConfig, configs should have the same name as the model/method class + "Config" at the end
function _getConfigFromModelOrMethodName(name, kind) {
const configs = kind === "Model" ? ModelConfigs : MethodConfigs;
Expand Down Expand Up @@ -63,14 +52,14 @@ function createMethod({ config, methodFactoryCls }) {
/**
* @summary Create top-level objects used in subworkflow initialization
* @param subworkflowData {Object} subworkflow data
* @param applicationCls {any} application class
* @param AdeFactory
* @param modelFactoryCls {any} model factory class
* @param methodFactoryCls {any} method factory class
* @returns {{application: *, method: *, model: (DFTModel|Model), setSearchText: String|null}}
*/
function createTopLevel({ subworkflowData, applicationCls, modelFactoryCls, methodFactoryCls }) {
function createTopLevel({ subworkflowData, modelFactoryCls, methodFactoryCls, AdeFactory }) {
const { application: appConfig, model: modelConfig, method: methodConfig } = subworkflowData;
const application = createApplication({ config: appConfig, applicationCls });
const application = AdeFactory.createApplication(appConfig);
const model = createModel({ config: modelConfig, modelFactoryCls });
const { method, setSearchText } = createMethod({ config: methodConfig, methodFactoryCls });
return {
Expand All @@ -91,7 +80,7 @@ function createTopLevel({ subworkflowData, applicationCls, modelFactoryCls, meth
* @param unitFactoryCls {*} workflow unit class factory
* @returns {*|{head: boolean, preProcessors: [], postProcessors: [], name: *, flowchartId: *, type: *, results: [], monitors: []}}
*/
function createUnit({ config, application, unitBuilders, unitFactoryCls }) {
export function createUnit({ config, application, unitBuilders, unitFactoryCls }) {
const { type, config: unitConfig } = config;
if (type === "executionBuilder") {
const { name, execName, flavorName, flowchartId } = unitConfig;
Expand Down Expand Up @@ -143,7 +132,7 @@ function createDynamicUnits({

function createSubworkflow({
subworkflowData,
applicationCls = Application,
AdeFactory = AdeFactoryDefault,
modelFactoryCls = ModelFactory,
methodFactoryCls = MethodFactory,
subworkflowCls = Subworkflow,
Expand All @@ -152,7 +141,7 @@ function createSubworkflow({
}) {
const { application, model, method, setSearchText } = createTopLevel({
subworkflowData,
applicationCls,
AdeFactory,
modelFactoryCls,
methodFactoryCls,
});
Expand Down
13 changes: 5 additions & 8 deletions src/units/base.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import {
NamedDefaultableRepetitionRuntimeItemsImportantSettingsContextAndRenderHashedInMemoryEntity,
TaggableMixin,
} from "@mat3ra/code/dist/js/entity";
import { NamedDefaultableRepetitionRuntimeItemsImportantSettingsContextAndRenderHashedInMemoryEntity } from "@mat3ra/code/dist/js/entity";
import { taggableMixin } from "@mat3ra/code/dist/js/entity/mixins/TaggableMixin";
import { getUUID } from "@mat3ra/code/dist/js/utils";
import lodash from "lodash";
import { mix } from "mixwith";

import { UNIT_STATUSES } from "../enums";

// eslint-disable-next-line max-len
export class BaseUnit extends mix(
NamedDefaultableRepetitionRuntimeItemsImportantSettingsContextAndRenderHashedInMemoryEntity,
).with(TaggableMixin) {
export class BaseUnit extends NamedDefaultableRepetitionRuntimeItemsImportantSettingsContextAndRenderHashedInMemoryEntity {
constructor(config) {
super({
...config,
Expand Down Expand Up @@ -92,3 +87,5 @@ export class BaseUnit extends mix(
return super.clone(flowchartIDOverrideConfigAsExtraContext);
}
}

taggableMixin(BaseUnit.prototype);
39 changes: 24 additions & 15 deletions src/units/builders/ExecutionUnitConfigBuilder.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { Application, Executable, Flavor } from "@exabyte-io/ade.js";
/* eslint-disable class-methods-use-this */
import AdeFactory from "@exabyte-io/ade.js/dist/js/AdeFactory";

import { UNIT_TYPES } from "../../enums";
import { UnitConfigBuilder } from "./UnitConfigBuilder";

export class ExecutionUnitConfigBuilder extends UnitConfigBuilder {
static Application = Application;

static Executable = Executable;

static Flavor = Flavor;

constructor(name, application, execName, flavorName, flowchartId) {
super({ name, type: UNIT_TYPES.execution, flowchartId });

Expand All @@ -29,14 +24,8 @@ export class ExecutionUnitConfigBuilder extends UnitConfigBuilder {

initialize(application, execName, flavorName) {
this.application = application;
this.executable = this.constructor.Executable.create({
name: execName,
application: this.application,
});
this.flavor = this.constructor.Flavor.create({
name: flavorName,
executable: this.executable,
});
this.executable = this._createExecutable(this.application, execName);
this.flavor = this._createFlavor(this.executable, flavorName);
}

build() {
Expand All @@ -47,4 +36,24 @@ export class ExecutionUnitConfigBuilder extends UnitConfigBuilder {
flavor: this.flavor.toJSON(),
};
}

/**
* Creates an executable instance. This method is intended to be overridden in subclasses.
* @param {Application} application - The application object
* @param {string} execName - The name of the executable
* @returns {Executable} The created executable instance
*/
_createExecutable(application, execName) {
return AdeFactory.getExecutableByName(application.name, execName);
}

/**
* Creates a flavor instance. This method is intended to be overridden in subclasses.
* @param {Executable} executable - The executable object
* @param {string} flavorName - The name of the flavor
* @returns {Flavor} The created flavor instance
*/
_createFlavor(executable, flavorName) {
return AdeFactory.getFlavorByName(executable, flavorName);
}
}
99 changes: 73 additions & 26 deletions src/units/execution.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Application, Template } from "@exabyte-io/ade.js";
import { HashedInputArrayMixin } from "@mat3ra/code/dist/js/entity";
import { removeTimestampableKeysFromConfig } from "@mat3ra/code/dist/js/utils";
import { mix } from "mixwith";
import { Template } from "@exabyte-io/ade.js";
import AdeFactory from "@exabyte-io/ade.js/dist/js/AdeFactory";
import {
calculateHashFromObject,
removeCommentsFromSourceCode,
removeEmptyLinesFromString,
removeTimestampableKeysFromConfig,
} from "@mat3ra/code/dist/js/utils";
import _ from "underscore";

import { BaseUnit } from "./base";

export class ExecutionUnit extends mix(BaseUnit).with(HashedInputArrayMixin) {
static Application = Application;

static Template = Template;

export class ExecutionUnit extends BaseUnit {
// keys to be omitted during toJSON
static omitKeys = [
"job",
Expand All @@ -22,18 +22,73 @@ export class ExecutionUnit extends mix(BaseUnit).with(HashedInputArrayMixin) {
"hasRelaxation",
];

/**
* @override this method to provide entities from other sources
*/
_initApplication(config) {
this._application = this.constructor.Application.create(config.application);
this._executable = this._application.getExecutableByConfig(config.executable);
this._flavor = this._executable.getFlavorByConfig(config.flavor);
this._application = AdeFactory.createApplication(config.application);
this._executable = AdeFactory.getExecutableByConfig(
this._application.name,
config.executable,
);
this._flavor = AdeFactory.getFlavorByConfig(this._executable, config.flavor);
this._templates = this._flavor ? this._flavor.inputAsTemplates : [];
}

/**
* @override this method to provide default executable from other source
*/
_getDefaultExecutable() {
return AdeFactory.getExecutableByName(this.application.name);
}

/**
* @override this method to provide default flavor from other source
*/
_getDefaultFlavor() {
return AdeFactory.getFlavorByName(this.executable.name);
}

/**
* @override this method to provide custom templates
*/
_getTemplatesFromInput() {
return this.getInput().map((i) => new Template(i));
}

/**
* @override this method to provide custom input from other sources
*/
_getInput() {
return (
this.input ||
AdeFactory.getInputAsRenderedTemplates(this.flavor, this.getCombinedContext()) ||
[]
);
}

/**
* @override this method to provide custom input as templates
*/
_getInputAsTemplates() {
return AdeFactory.getInputAsTemplates(this.flavor);
}

_initRuntimeItems(keys, config) {
this._initApplication(config);
super._initRuntimeItems(keys);
}

/*
* @summary expects an array with elements containing field [{content: "..."}]
*/
get hashFromArrayInputContent() {
const objectForHashing = this._getInput().map((i) => {
return removeEmptyLinesFromString(removeCommentsFromSourceCode(i.content));
});
return calculateHashFromObject(objectForHashing);
}

get name() {
return this.prop("name", this.flavor.name);
}
Expand All @@ -54,29 +109,25 @@ export class ExecutionUnit extends mix(BaseUnit).with(HashedInputArrayMixin) {
return this._templates;
}

get templatesFromInput() {
return this.input.map((i) => new this.constructor.Template(i));
}

setApplication(application, omitSettingExecutable = false) {
this._application = application;
this.setProp("application", application.toJSON());
if (!omitSettingExecutable) {
this.setExecutable(this.application.defaultExecutable);
this.setExecutable(this._getDefaultExecutable());
}
}

setExecutable(executable) {
this._executable = executable;
this.setProp("executable", executable.toJSON());
this.setFlavor(this.executable.defaultFlavor);
this.setFlavor(this._getDefaultFlavor());
}

setFlavor(flavor) {
this._flavor = flavor;
this.setRuntimeItemsToDefaultValues();
this.setProp("flavor", flavor.toJSON());
this.setTemplates(this.flavor.inputAsTemplates);
this.setTemplates(this._getInputAsTemplates());
}

setTemplates(templates) {
Expand Down Expand Up @@ -126,11 +177,7 @@ export class ExecutionUnit extends mix(BaseUnit).with(HashedInputArrayMixin) {
}

get input() {
return (
this.prop("input") ||
this.flavor.getInputAsRenderedTemplates(this.getCombinedContext()) ||
[]
);
return this.prop("input");
}

get renderingContext() {
Expand Down Expand Up @@ -167,7 +214,7 @@ export class ExecutionUnit extends mix(BaseUnit).with(HashedInputArrayMixin) {
const newRenderingContext = {};
const renderingContext = { ...this.context, ...context };
this.updateContext(renderingContext); // update in-memory context to properly render templates from input below
(fromTemplates ? this.templates : this.templatesFromInput).forEach((t) => {
(fromTemplates ? this.templates : this._getTemplatesFromInput()).forEach((t) => {
newInput.push(t.getRenderedJSON(renderingContext));
Object.assign(
newRenderingContext,
Expand Down Expand Up @@ -204,7 +251,7 @@ export class ExecutionUnit extends mix(BaseUnit).with(HashedInputArrayMixin) {
...super.toJSON(),
executable: this.executable.toJSON(),
flavor: this.flavor.toJSON(),
input: this.input,
input: this._getInput(),
// keys below are not propagated to the parent class on initialization of a new unit unless explicitly given
name: this.name,
// TODO: figure out the problem with storing context below
Expand Down
4 changes: 2 additions & 2 deletions tests/subworkflow.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Application } from "@exabyte-io/ade.js";
import AdeFactory from "@exabyte-io/ade.js/dist/js/AdeFactory";
import { expect } from "chai";

import { createSubworkflowByName } from "../src/subworkflows";
Expand Down Expand Up @@ -101,7 +101,7 @@ describe("subworkflows", () => {
expect(subworkflow.units[0].application.version).to.be.equal("6.3");
expect(subworkflow.units[1].application?.version).to.be.equal(undefined);

const newApplication = Application.createFromNameVersionBuild({
const newApplication = AdeFactory.createApplication({
name: "espresso",
version: "6.7.0",
});
Expand Down
Loading