import { call, put, takeEvery, select } from "redux-saga/effects";
import BlueprintApi from "../../data/BlueprintApi";
import * as composer from "../composer";
import {
FETCHING_BLUEPRINTS,
fetchingBlueprintsSucceeded,
fetchingBlueprintNamesSucceeded,
FETCHING_BLUEPRINT_CONTENTS,
fetchingBlueprintContentsSucceeded,
reloadingBlueprintContentsSucceeded,
CREATING_BLUEPRINT,
creatingBlueprintSucceeded,
UPDATE_BLUEPRINT_COMPONENTS,
SET_BLUEPRINT_USERS,
setBlueprintUsersSucceeded,
SET_BLUEPRINT_HOSTNAME,
setBlueprintHostnameSucceeded,
SET_BLUEPRINT_DESCRIPTION,
setBlueprintDescriptionSucceeded,
DELETING_BLUEPRINT,
deletingBlueprintSucceeded,
DELETE_HISTORY,
UNDO,
REDO,
blueprintsFailure,
blueprintContentsFailure,
FETCHING_COMP_DEPS,
setCompDeps,
} from "../actions/blueprints";
import { makeGetBlueprintById, makeGetSelectedDeps } from "../selectors";
function* fetchBlueprintsFromName(blueprintName) {
const response = yield call(composer.getBlueprintInfo, blueprintName);
yield put(fetchingBlueprintsSucceeded(response));
}
// need to test what happens when there are no blueprints
function* fetchBlueprints() {
try {
const blueprintNames = yield call(composer.listBlueprints);
yield put(fetchingBlueprintNamesSucceeded(blueprintNames));
yield* blueprintNames.map((blueprintName) => fetchBlueprintsFromName(blueprintName));
} catch (error) {
console.log("errorloadBlueprintsSaga", error);
yield put(blueprintsFailure(error));
}
}
function* fetchBlueprintContents(action) {
try {
const { blueprintId } = action.payload;
const response = yield call(composer.depsolveBlueprint, blueprintId);
const blueprintData = response.blueprints[0];
let components = [];
if (blueprintData.blueprint.packages.length > 0 || blueprintData.blueprint.modules.length > 0) {
components = yield call(generateComponents, blueprintData);
}
const workspaceChanges = yield call(composer.diffBlueprintToWorkspace, blueprintId);
let pastComponents;
let workspacePendingChanges = [];
if (workspaceChanges.diff.length > 0) {
workspacePendingChanges = workspaceChanges.diff.map((change) => {
return {
componentOld: change.old === null ? null : change.old.Package ? change.old.Package : change.old.Module,
componentNew: change.new === null ? null : change.new.Package ? change.new.Package : change.new.Module,
};
});
pastComponents = generatePastComponents(workspaceChanges, blueprintData.blueprint.packages);
}
const blueprint = {
...blueprintData.blueprint,
components,
id: blueprintId,
localPendingChanges: [],
workspacePendingChanges,
errorState: response.errors[0],
};
const pastBlueprint = pastComponents
? [{ ...blueprint, ...pastComponents, workspacePendingChanges: [], errorState: {} }]
: [];
yield put(fetchingBlueprintContentsSucceeded(blueprint, pastBlueprint, response.errors[0]));
} catch (error) {
console.log("Error in fetchBlueprintContentsSaga", error);
yield put(blueprintContentsFailure(error, action.payload.blueprintId));
}
}
function generatePastComponents(workspaceChanges, packages) {
const updatedPackages = workspaceChanges.diff
.filter((change) => change.old !== null)
.map((change) => (change.old.Package ? change.old.Package : change.old.Module));
const originalPackages = packages.filter((originalPackage) => {
const addedPackage = workspaceChanges.diff.find(
(change) => change.old === null && change.new.Package.name === originalPackage.name
);
if (addedPackage === undefined) {
return true;
}
});
const blueprintPackages = updatedPackages.concat(originalPackages);
const blueprintComponents = blueprintPackages.map((component) => {
const componentData = { ...component, inBlueprint: true, userSelected: true };
return componentData;
});
const blueprint = {
components: blueprintComponents,
packages: blueprintPackages,
};
return blueprint;
}
function* generateComponents(blueprintData) {
// List of selected components
const packageNames = blueprintData.blueprint.packages.map((component) => component.name);
const moduleNames = blueprintData.blueprint.modules.map((component) => component.name);
const selectedComponentNames = packageNames.concat(moduleNames);
// List of all components
let componentNames = [];
let componentsRaw = [];
if (blueprintData.dependencies.length > 0) {
componentNames = blueprintData.dependencies.map((component) => component.name);
componentsRaw = flattenComponents(blueprintData.dependencies);
} else {
componentNames = selectedComponentNames;
componentsRaw = blueprintData.blueprint.packages.concat(blueprintData.blueprint.modules);
}
// Get component info
const componentInfo = yield call(composer.getComponentInfo, componentNames);
const components = componentsRaw.map((component) => {
const info = componentInfo.find((item) => item.name === component.name);
const componentData = {
name: component.name,
description: info.description,
homepage: info.homepage,
summary: info.summary,
inBlueprint: true,
userSelected: selectedComponentNames.includes(component.name),
ui_type: "RPM",
version: component.version,
release: component.release ? component.release : "",
};
return componentData;
});
return components;
}
function flattenComponents(components) {
const previousComponents = {};
const flattened = components.filter((component) => {
return previousComponents.hasOwnProperty(component.name) ? false : (previousComponents[component.name] = true);
});
return flattened;
}
function* reloadBlueprintContents(blueprintId) {
// after updating components or deleting the workspace changes,
// get the latest depsolved blueprint components
try {
const response = yield call(composer.depsolveBlueprint, blueprintId);
const blueprintData = response.blueprints[0];
let components = [];
if (blueprintData.blueprint.packages.length > 0 || blueprintData.blueprint.modules.length > 0) {
components = yield call(generateComponents, blueprintData);
}
const blueprint = {
...blueprintData.blueprint,
components,
id: blueprintId,
errorState: response.errors[0],
};
yield put(reloadingBlueprintContentsSucceeded(blueprint, response.errors[0]));
} catch (error) {
console.log("Error in fetchBlueprintContentsSaga", error);
yield put(blueprintContentsFailure(error, blueprintId));
}
}
function* getBlueprintHistory(blueprintId) {
const getBlueprintById = makeGetBlueprintById();
const blueprintHistory = yield select(getBlueprintById, blueprintId);
const oldestBlueprint = blueprintHistory.past[0] ? blueprintHistory.past[0] : blueprintHistory.present;
return [oldestBlueprint, blueprintHistory.present];
}
function* setBlueprintUsers(action) {
try {
const { blueprintId, users } = action.payload;
// commit the oldest blueprint with the updated users
const blueprintHistory = yield call(getBlueprintHistory, blueprintId);
const blueprintToPost = BlueprintApi.postedBlueprintData({
...blueprintHistory[0],
customizations: { ...blueprintHistory[0].customizations, user: users },
});
yield call(composer.newBlueprint, blueprintToPost);
// get updated blueprint info (i.e. version)
const response = yield call(composer.getBlueprintInfo, blueprintId);
yield put(setBlueprintUsersSucceeded(response));
// post present blueprint object to workspace
if (response.changed === true) {
const workspace = {
...blueprintHistory[1],
version: response.version,
customizations: { ...blueprintHistory[1].customizations, user: users },
};
yield call(composer.commitToWorkspace, workspace);
}
} catch (error) {
console.log("Error in setBlueprintHostname", error);
yield put(blueprintsFailure(error));
}
}
function* setBlueprintHostname(action) {
try {
const { blueprint, hostname } = action.payload;
// commit the oldest blueprint with the updated hostname
const blueprintHistory = yield call(getBlueprintHistory, blueprint.id);
const blueprintToPost = BlueprintApi.postedBlueprintData({
...blueprintHistory[0],
customizations: { ...blueprintHistory[0].customizations, hostname },
});
yield call(composer.newBlueprint, blueprintToPost);
// get updated blueprint info (i.e. version)
const response = yield call(composer.getBlueprintInfo, blueprint.name);
yield put(setBlueprintHostnameSucceeded(response));
// post present blueprint object to workspace
if (response.changed === true) {
const workspace = {
...blueprintHistory[1],
version: response.version,
customizations: { ...blueprintHistory[1].customizations, hostname },
};
yield call(composer.commitToWorkspace, workspace);
}
} catch (error) {
console.log("Error in setBlueprintHostname", error);
yield put(blueprintsFailure(error));
}
}
function* setBlueprintDescription(action) {
try {
const { blueprint, description } = action.payload;
// commit the oldest blueprint with the updated hostname
const blueprintHistory = yield call(getBlueprintHistory, blueprint.id);
const blueprintToPost = BlueprintApi.postedBlueprintData({ ...blueprintHistory[0], description });
yield call(composer.newBlueprint, blueprintToPost);
// get updated blueprint info (i.e. version)
const response = yield call(composer.getBlueprintInfo, blueprint.name);
yield put(setBlueprintDescriptionSucceeded(response));
// post present blueprint object to workspace
if (response.changed === true) {
const workspace = { ...blueprintHistory[1], version: response.version, description };
yield call(composer.commitToWorkspace, workspace);
}
} catch (error) {
console.log("Error in setBlueprintDescription", error);
yield put(blueprintsFailure(error));
}
}
function* deleteBlueprint(action) {
try {
const { blueprintId } = action.payload;
const response = yield call(composer.deleteBlueprint, blueprintId);
yield put(deletingBlueprintSucceeded(response));
} catch (error) {
console.log("errorDeleteBlueprintsSaga", error);
yield put(blueprintsFailure(error));
}
}
function* createBlueprint(action) {
try {
const { blueprint } = action.payload;
yield call(composer.newBlueprint, blueprint);
yield put(creatingBlueprintSucceeded(blueprint));
} catch (error) {
console.log("errorCreateBlueprintSaga", error);
yield put(blueprintsFailure(error));
}
}
function* commitToWorkspace(action) {
try {
const { blueprintId, reload } = action.payload;
const getBlueprintById = makeGetBlueprintById();
const blueprint = yield select(getBlueprintById, blueprintId);
const blueprintData = {
name: blueprint.present.name,
description: blueprint.present.description,
modules: blueprint.present.modules,
packages: blueprint.present.packages,
version: blueprint.present.version,
groups: blueprint.present.groups,
};
if (blueprint.present.customizations !== undefined) {
blueprintData.customizations = blueprint.present.customizations;
}
yield call(composer.commitToWorkspace, blueprintData);
if (reload !== false) {
yield call(reloadBlueprintContents, blueprintId);
}
} catch (error) {
console.log("commitToWorkspaceError", error);
yield put(blueprintsFailure(error));
}
}
function* deleteWorkspace(action) {
try {
const { blueprintId, reload } = action.payload;
yield call(composer.deleteWorkspace, blueprintId);
if (reload !== false) {
yield call(reloadBlueprintContents, blueprintId);
}
} catch (error) {
console.log("deleteWorkspaceError", error);
yield put(blueprintsFailure("failed delete workspace"));
}
}
function* fetchCompDeps(action) {
try {
const { component, blueprintId } = action.payload;
const response = yield call(composer.getComponentDependencies, component.name);
let responseIndex;
if (response[0].builds) {
responseIndex = response.findIndex((item) => {
return item.builds[0].release === component.release && item.builds[0].source.version === component.version;
});
} else {
responseIndex = 0;
}
const deps = response[responseIndex].dependencies.filter((item) => item.name !== component.name);
const updatedDeps = deps.map((dep) => {
const depData = {
ui_type: "RPM",
...dep,
};
delete depData.epoch;
delete depData.arch;
return depData;
});
const getBlueprintById = makeGetBlueprintById();
const blueprint = yield select(getBlueprintById, blueprintId);
const { components } = blueprint.present;
const getSelectedDeps = makeGetSelectedDeps();
const selectedDeps = yield select(getSelectedDeps, updatedDeps, components);
const updatedComp = {
name: component.name,
dependencies: selectedDeps,
};
yield put(setCompDeps(updatedComp, blueprintId));
} catch (error) {
console.log("Error in fetchInputDeps", error);
}
}
export default function* () {
yield takeEvery(CREATING_BLUEPRINT, createBlueprint);
yield takeEvery(FETCHING_BLUEPRINT_CONTENTS, fetchBlueprintContents);
yield takeEvery(SET_BLUEPRINT_USERS, setBlueprintUsers);
yield takeEvery(SET_BLUEPRINT_HOSTNAME, setBlueprintHostname);
yield takeEvery(SET_BLUEPRINT_DESCRIPTION, setBlueprintDescription);
yield takeEvery(DELETING_BLUEPRINT, deleteBlueprint);
yield takeEvery(UPDATE_BLUEPRINT_COMPONENTS, commitToWorkspace);
yield takeEvery(UNDO, commitToWorkspace);
yield takeEvery(REDO, commitToWorkspace);
yield takeEvery(DELETE_HISTORY, deleteWorkspace);
yield takeEvery(FETCHING_BLUEPRINTS, fetchBlueprints);
yield takeEvery(FETCHING_COMP_DEPS, fetchCompDeps);
}