+
Skip to content

New data migration fulfillment #6633

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

Merged
merged 10 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/api-plugin-fulfillment/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import register from "./src/index.js";

export { default as migrations } from "./migrations/index.js";

export default register;
177 changes: 177 additions & 0 deletions packages/api-plugin-fulfillment/migrations/2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
const COLL_FF_SOURCE = "Shipping";
const COLL_FF_DEST = "Fulfillment";
const COLL_FFR_SOURCE = "FlatRateFulfillmentRestrictions";
const COLL_FFR_DEST = "FulfillmentRestrictions";
const FF_TYPE = "shipping";
const FF_METHOD = "flatRate";
const COLL_DEST = "Fulfillment";

const newGlobalPermissions = [
"reaction:legacy:fulfillmentTypes/update:settings",
"reaction:legacy:fulfillmentRestrictions/create",
"reaction:legacy:fulfillmentRestrictions/delete",
"reaction:legacy:fulfillmentRestrictions/read",
"reaction:legacy:fulfillmentRestrictions/update",
"reaction:legacy:fulfillmentTypes/create",
"reaction:legacy:fulfillmentTypes/delete",
"reaction:legacy:fulfillmentTypes/read",
"reaction:legacy:fulfillmentTypes/update",
"reaction:legacy:fulfillmentMethods/create",
"reaction:legacy:fulfillmentMethods/delete",
"reaction:legacy:fulfillmentMethods/read",
"reaction:legacy:fulfillmentMethods/update"
];

/**
* @summary migrates the database down one version
* @param {Object} context Migration context
* @param {Object} context.db MongoDB `Db` instance
* @param {Function} context.progress A function to report progress, takes percent
* number as argument.
* @return {undefined}
*/
async function down({ db, progress }) {
progress(0);

const allGroups = await db.collection("Groups").find({}).toArray();
const affectedGlobalGroups = [];
allGroups.forEach((group) => {
const perms = group.permissions;
if (perms && Array.isArray(perms) && perms.length) {
const found = newGlobalPermissions.some((elem) => perms.includes(elem));
if (found) affectedGlobalGroups.push(group.slug);
}
});

await db.collection("Groups").updateMany({
slug: { $in: affectedGlobalGroups }
}, {
$pullAll: { permissions: newGlobalPermissions }
});
progress(10);

await db.collection(COLL_FF_DEST).drop();
progress(50);

await db.collection(COLL_FFR_DEST).drop();
progress(100);
}

/**
* @summary Performs migration up from previous data version
* @param {Object} context Migration context
* @param {Object} context.db MongoDB `Db` instance
* @param {Function} context.progress A function to report progress, takes percent
* number as argument.
* @return {undefined}
*/
async function up({ db, progress }) {
progress(0);

const shippingCopyResp = await db.collection(COLL_FF_SOURCE).aggregate([{ $match: {} }, { $out: COLL_FF_DEST }]).next();
if (shippingCopyResp) throw new Error("Error in copying Shipping collection"); // above command returns null if successful
progress(20);

const flatRateRestCopyResp = await db.collection(COLL_FFR_SOURCE).aggregate([{ $match: {} }, { $out: COLL_FFR_DEST }]).next();
if (flatRateRestCopyResp) throw new Error("Error in copying FlatRateFulfillmentRestrictions collection"); // above command returns null if successful
progress(40);

const operations = [];

const ffTypeUpdate = {
updateMany: {
filter: { fulfillmentType: { $exists: false } },
update: {
$set: {
fulfillmentType: FF_TYPE
}
}
}
};

const ffMethodUpdate = {
updateMany: {
filter: { methods: { $exists: true } },
update: {
$set: {
"methods.$[eachMethod].fulfillmentMethod": FF_METHOD
}
},
arrayFilters: [
{
"eachMethod.fulfillmentMethod": { $exists: false }
}
]
}
};

operations.push(ffTypeUpdate);
operations.push(ffMethodUpdate);

const bulkWriteResp = await db.collection(COLL_DEST).bulkWrite(operations);
Copy link
Collaborator

@brent-hoover brent-hoover Dec 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will probably work but it's not really what the bulkWrite API is really for an would probably be better as written as a single query. Bulkwrite is when you are doing a large amount of atomic operations, not one large one.

if (bulkWriteResp.writeErrors && bulkWriteResp.writeErrors.length) throw new Error("Error while updating Fulfillment collection");
progress(50);

const oldPerms = [
"reaction:legacy:shippingMethods/create",
"reaction:legacy:shippingMethods/delete",
"reaction:legacy:shippingMethods/read",
"reaction:legacy:shippingMethods/update",
"reaction:legacy:shippingRestrictions/create",
"reaction:legacy:shippingRestrictions/delete",
"reaction:legacy:shippingRestrictions/read",
"reaction:legacy:shippingRestrictions/update"
];

const mapperSetFFMethodsRestricts = {
"reaction:legacy:shippingMethods/create": "reaction:legacy:fulfillmentMethods/create",
"reaction:legacy:shippingMethods/delete": "reaction:legacy:fulfillmentMethods/delete",
"reaction:legacy:shippingMethods/read": "reaction:legacy:fulfillmentMethods/read",
"reaction:legacy:shippingMethods/update": "reaction:legacy:fulfillmentMethods/update",
"reaction:legacy:shippingRestrictions/create": "reaction:legacy:fulfillmentRestrictions/create",
"reaction:legacy:shippingRestrictions/delete": "reaction:legacy:fulfillmentRestrictions/delete",
"reaction:legacy:shippingRestrictions/read": "reaction:legacy:fulfillmentRestrictions/read",
"reaction:legacy:shippingRestrictions/update": "reaction:legacy:fulfillmentRestrictions/update"
};
const mapperSetFFTypes = {
"reaction:legacy:shippingMethods/create": "reaction:legacy:fulfillmentTypes/create",
"reaction:legacy:shippingMethods/delete": "reaction:legacy:fulfillmentTypes/delete",
"reaction:legacy:shippingMethods/read": "reaction:legacy:fulfillmentTypes/read",
"reaction:legacy:shippingMethods/update": "reaction:legacy:fulfillmentTypes/update"
};
const allGroups = await db.collection("Groups").find({}).toArray();

for (let index = 0; index < allGroups.length; index += 1) {
const currentGroup = allGroups[index];
const currentPerms = currentGroup.permissions;
const permsToAdd = [];

if (currentPerms && Array.isArray(currentPerms) && currentPerms.length) {
oldPerms.forEach((oldPerm) => {
if (currentPerms.includes(oldPerm)) {
permsToAdd.push(mapperSetFFMethodsRestricts[oldPerm]);
if (mapperSetFFTypes[oldPerm]) {
permsToAdd.push(mapperSetFFTypes[oldPerm]);
}
}
});
}
if (permsToAdd.length) {
permsToAdd.push("reaction:legacy:fulfillmentTypes/update:settings"); // add this setting to groups deailing with ff-types
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo in comment: dealing

// eslint-disable-next-line no-await-in-loop
await db.collection("Groups").updateOne(
{ _id: currentGroup._id },
{
$addToSet: { permissions: { $each: permsToAdd } }
}
);
}
}

progress(100);
}

export default {
down,
up
};
13 changes: 13 additions & 0 deletions packages/api-plugin-fulfillment/migrations/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { migrationsNamespace } from "./migrationsNamespace.js";
import migration2 from "./2.js";

export default {
tracks: [
{
namespace: migrationsNamespace,
migrations: {
2: migration2
}
}
]
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const migrationsNamespace = "fulfillment";
1 change: 1 addition & 0 deletions packages/api-plugin-fulfillment/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@reactioncommerce/api-utils": "^1.16.9",
"@reactioncommerce/reaction-error": "^1.0.1",
"@reactioncommerce/random": "^1.0.2",
"@reactioncommerce/db-version-check": "^1.0.0",
"lodash": "^4.17.21",
"simpl-schema": "^1.12.2"
},
Expand Down
34 changes: 34 additions & 0 deletions packages/api-plugin-fulfillment/src/preStartup.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,45 @@
import doesDatabaseVersionMatch from "@reactioncommerce/db-version-check";
import { migrationsNamespace } from "../migrations/migrationsNamespace.js";
import { extendFulfillmentSchemas } from "./simpleSchemas.js";

const expectedVersion = 4;

/**
* @summary Checks if the version of the database matches requirement
* @param {Object} context Startup context
* @returns {undefined}
*/
async function dbVersionCheck(context) {
const setToExpectedIfMissing = async () => {
const anyFulfillment = await context.collections.Fulfillment.findOne();
const anyRestriction = await context.collections.FulfillmentRestrictions.findOne();

return !anyFulfillment || !anyRestriction;
};

const ok = await doesDatabaseVersionMatch({
// `db` is a Db instance from the `mongodb` NPM package,
// such as what is returned when you do `client.db()`
db: context.app.db,
// These must match one of the namespaces and versions
// your package exports in the `migrations` named export
expectedVersion,
namespace: migrationsNamespace,
setToExpectedIfMissing
});

if (!ok) {
throw new Error(`Database needs migrating. The "${migrationsNamespace}" namespace must be at version ${expectedVersion}.`);
}
}

/**
* @summary Called before startup to extend schemas
* @param {Object} context Startup context
* @returns {undefined}
*/
export default async function fulfillmentPreStartup(context) {
await dbVersionCheck(context);
const allFulfillmentTypesArray = context.fulfillment?.registeredFulfillmentTypes;
extendFulfillmentSchemas(context.simpleSchemas, allFulfillmentTypesArray);
}
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

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

点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载