Skip to content

Commit

Permalink
Merge pull request reactioncommerce#5647 from reactioncommerce/releas…
Browse files Browse the repository at this point in the history
…e-v2.6.0

release v2.6.0
  • Loading branch information
kieckhafer authored Oct 3, 2019
2 parents 5c215e4 + 3638f3f commit b03e63b
Show file tree
Hide file tree
Showing 397 changed files with 7,147 additions and 2,677 deletions.
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
# v2.6.0

Reaction v2.6.0 adds minor features and performance enhancements, fixes bugs and contains no breaking changes since v2.6.0.

This release is being coordinated with [Reaction Platform](https://github.com/reactioncommerce/reaction-platform) and is designed to work with `v2.6.0` of [Reaction Hydra](https://github.com/reactioncommerce/reaction-hydra) and [Example Storefront](https://github.com/reactioncommerce/example-storefront).

## Notable changes

### Packages have begun to migrate from `no-meteor` folders to `node-app`

As part of de-meteorization of the Reaction API, We have started the migration of moving all server side code in `no-meteor` folders to their respective `node-app` folders.

## Feature

- feat: add product and productVariant related GraphQL mutations ([#5562](https://github.com/reactioncommerce/reaction/pull/5562))

## Fixes

- fix: product medatafield UI issues ([#5584](https://github.com/reactioncommerce/reaction/pull/5584))

## Refactors

- refactor: move email-smtp from server/no-meteor to node-app ([#5641](https://github.com/reactioncommerce/reaction/pull/5641))
- refactor: move ui from server/no-meteor to node-app ([#5637](https://github.com/reactioncommerce/reaction/pull/5637))
- refactor: move address-validation-test from server/no-meteor to node-app ([#5638](https://github.com/reactioncommerce/reaction/pull/5638))
- refactor: move `shipping` and `surcharges` from server/no-meteor to node-app ([#5632](https://github.com/reactioncommerce/reaction/pull/5632))
- refactor: move `settings` from server/no-meteor to node-app ([#5634](https://github.com/reactioncommerce/reaction/pull/5634))
- refactor: ackground jobs rewrite: no Meteor dependencies! ([#5580](https://github.com/reactioncommerce/reaction/pull/5580))
- refactor: move `address` from server/no-meteor to node-app ([#5587](https://github.com/reactioncommerce/reaction/pull/5587))
- refactor: move `template` from server/no-meteor to node-app ([#5586](https://github.com/reactioncommerce/reaction/pull/5586))
- refactor: use `reaction-error` external package instead of internal alias ([#5631](https://github.com/reactioncommerce/reaction/pull/5631))
- refactor: move system-information from server/no-meteor to node-app ([#5585](https://github.com/reactioncommerce/reaction/pull/5585))
- refactor: rewrite removeAccountEmail without meteor ([#5577](https://github.com/reactioncommerce/reaction/pull/5577))

# v2.5.0

Reaction v2.5.0 adds minor features and performance enhancements, fixes bugs and contains no breaking changes since v2.4.0.
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import mutations from "./mutations";
import queries from "./queries";
import resolvers from "./resolvers";
import schemas from "./schemas";
import { registerPluginHandler } from "./util/settingsConfig";
import mutations from "./mutations/index.js";
import queries from "./queries/index.js";
import resolvers from "./resolvers/index.js";
import schemas from "./schemas/index.js";
import { registerPluginHandler } from "./util/settingsConfig.js";

/**
* @summary Import and call this function to add this plugin to your API.
Expand Down
5 changes: 5 additions & 0 deletions imports/node-app/core-services/settings/mutations/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import updateAppSettings from "./updateAppSettings.js";

export default {
updateAppSettings
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import {
addGlobalSettingDefaults,
addShopSettingDefaults,
rolesThatCanEditGlobalSetting,
rolesThatCanEditShopSetting
} from "../util/settingsConfig";
rolesThatCanEditShopSetting,
runAfterUpdateHooks
} from "../util/settingsConfig.js";

/**
* @summary Updates app settings for a shop or global app settings.
Expand All @@ -17,8 +18,13 @@ export default async function updateAppSettings(context, settingsUpdates, shopId
const { collections, userHasPermission } = context;
const { AppSettings } = collections;

const updateKeys = Object.keys(settingsUpdates);
if (updateKeys.length === 0) {
throw new ReactionError("invalid-param", "You must request at least one update");
}

// Look up roles that are allowed to set each setting. Throw if not allowed.
Object.getOwnPropertyNames(settingsUpdates).forEach((field) => {
updateKeys.forEach((field) => {
const allowedRoles = shopId ? rolesThatCanEditShopSetting(field) : rolesThatCanEditGlobalSetting(field);
if (allowedRoles.length === 0 || !userHasPermission(allowedRoles, shopId)) {
throw new ReactionError("access-denied", `You are not allowed to edit the "${field}" setting`);
Expand All @@ -36,5 +42,8 @@ export default async function updateAppSettings(context, settingsUpdates, shopId
}
);

// We don't want to await these and delay sending a response back
runAfterUpdateHooks(context, settingsUpdates, shopId);

return shopId ? addShopSettingDefaults(updatedDoc || {}) : addGlobalSettingDefaults(updatedDoc || {});
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addGlobalSettingDefaults, addShopSettingDefaults } from "../util/settingsConfig";
import { addGlobalSettingDefaults, addShopSettingDefaults } from "../util/settingsConfig.js";

/**
* @summary Returns app settings for a shop or global app settings.
Expand Down
5 changes: 5 additions & 0 deletions imports/node-app/core-services/settings/queries/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import appSettings from "./appSettings.js";

export default {
appSettings
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import updateGlobalSettings from "./updateGlobalSettings.js";
import updateShopSettings from "./updateShopSettings.js";

export default {
updateGlobalSettings,
updateShopSettings
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import globalSettings from "./globalSettings.js";
import shopSettings from "./shopSettings.js";

export default {
globalSettings,
shopSettings
};
7 changes: 7 additions & 0 deletions imports/node-app/core-services/settings/resolvers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Mutation from "./Mutation/index.js";
import Query from "./Query/index.js";

export default {
Mutation,
Query
};
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,27 @@ export function rolesThatCanEditShopSetting(field) {
return config.rolesThatCanEdit || [];
}

/**
* @summary Run all afterUpdate hooks that were registered for each updated setting
* @param {Object} context App context
* @param {Object} updates Object with setting name as key and new setting value as value
* @param {String} [shopId] Shop ID. Pass `null` for global settings.
* @return {undefined}
*/
export function runAfterUpdateHooks(context, updates, shopId) {
Object.keys(updates).forEach((field) => {
const config = shopId ? shopSettingsConfig[field] : globalSettingsSchema[field];
if (!config || !config.afterUpdate) return;

config.afterUpdate(context, { shopId, value: updates[field] });
});
}

const configSchema = new SimpleSchema({
"afterUpdate": {
type: Function,
optional: true
},
"defaultValue": {
type: SimpleSchema.oneOf(String, Number, Date, Boolean),
optional: true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import i18n from "./i18n";
import mutations from "./mutations";
import queries from "./queries";
import resolvers from "./resolvers";
import schemas from "./schemas";
import i18n from "./i18n/index.js";
import mutations from "./mutations/index.js";
import queries from "./queries/index.js";
import resolvers from "./resolvers/index.js";
import schemas from "./schemas/index.js";

/**
* @summary Import and call this function to add this plugin to your API.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import selectFulfillmentOptionForGroup from "./selectFulfillmentOptionForGroup";
import updateFulfillmentOptionsForGroup from "./updateFulfillmentOptionsForGroup";
import selectFulfillmentOptionForGroup from "./selectFulfillmentOptionForGroup.js";
import updateFulfillmentOptionsForGroup from "./updateFulfillmentOptionsForGroup.js";

export default {
selectFulfillmentOptionForGroup,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import SimpleSchema from "simpl-schema";
import ReactionError from "@reactioncommerce/reaction-error";
import getCartById from "../util/getCartById";
import getCartById from "../util/getCartById.js";

const inputSchema = new SimpleSchema({
cartId: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import mockContext from "/imports/test-utils/helpers/mockContext";
import selectFulfillmentOptionForGroup from "./selectFulfillmentOptionForGroup";
import mockContext from "@reactioncommerce/api-utils/tests/mockContext.js";
import selectFulfillmentOptionForGroup from "./selectFulfillmentOptionForGroup.js";

jest.mock("../util/getCartById", () => jest.fn().mockImplementation(() => Promise.resolve({
_id: "cartId",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { isEqual } from "lodash";
import SimpleSchema from "simpl-schema";
import ReactionError from "@reactioncommerce/reaction-error";
import xformCartGroupToCommonOrder from "/imports/plugins/core/cart/server/no-meteor/util/xformCartGroupToCommonOrder";
import getCartById from "../util/getCartById";
import getCartById from "../util/getCartById.js";

const inputSchema = new SimpleSchema({
cartId: String,
Expand Down Expand Up @@ -71,7 +70,7 @@ export default async function updateFulfillmentOptionsForGroup(context, input) {
const fulfillmentGroup = (cart.shipping || []).find((group) => group._id === fulfillmentGroupId);
if (!fulfillmentGroup) throw new ReactionError("not-found", `Fulfillment group with ID ${fulfillmentGroupId} not found in cart with ID ${cartId}`);

const commonOrder = await xformCartGroupToCommonOrder(cart, fulfillmentGroup, context);
const commonOrder = await context.queries.getCommonOrderForCartGroup(context, { cartId: cart._id, fulfillmentGroupId: fulfillmentGroup._id });

// In the future we want to do this async and subscribe to the results
const rates = await context.queries.getFulfillmentMethodsWithQuotes(commonOrder, context);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Factory from "/imports/test-utils/helpers/factory";
import mockContext from "/imports/test-utils/helpers/mockContext";
import updateFulfillmentOptionsForGroup from "./updateFulfillmentOptionsForGroup";
import Factory from "/imports/test-utils/helpers/factory.js";
import mockContext from "@reactioncommerce/api-utils/tests/mockContext.js";
import updateFulfillmentOptionsForGroup from "./updateFulfillmentOptionsForGroup.js";

jest.mock("../util/getCartById", () => jest.fn().mockImplementation(() => Promise.resolve({
_id: "cartId",
Expand All @@ -26,10 +26,12 @@ jest.mock("../util/getCartById", () => jest.fn().mockImplementation(() => Promis
const fakeCart = Factory.Cart.makeOne();
const fakeQuote = Factory.ShipmentQuote.makeOne();
const mockGetFulfillmentMethodsWithQuotes = jest.fn().mockName("getFulfillmentMethodsWithQuotes");
const mockGetCommonOrderForCartGroup = jest.fn().mockName("getCommonOrderForCartGroup");

beforeAll(() => {
mockContext.queries = {
getFulfillmentMethodsWithQuotes: mockGetFulfillmentMethodsWithQuotes
getFulfillmentMethodsWithQuotes: mockGetFulfillmentMethodsWithQuotes,
getCommonOrderForCartGroup: mockGetCommonOrderForCartGroup
};
if (!mockContext.mutations.saveCart) {
mockContext.mutations.saveCart = jest.fn().mockName("context.mutations.saveCart").mockImplementation(async (_, cart) => cart);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Logger from "@reactioncommerce/logger";
import extendCommonOrder from "../util/extendCommonOrder";
import extendCommonOrder from "../util/extendCommonOrder.js";

/**
* @name getFulfillmentMethodsWithQuotes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import getFulfillmentMethodsWithQuotes from "./getFulfillmentMethodsWithQuotes";
import getFulfillmentMethodsWithQuotes from "./getFulfillmentMethodsWithQuotes.js";

export default {
getFulfillmentMethodsWithQuotes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import selectFulfillmentOptionForGroup from "./selectFulfillmentOptionForGroup";
import updateFulfillmentOptionsForGroup from "./updateFulfillmentOptionsForGroup";
import selectFulfillmentOptionForGroup from "./selectFulfillmentOptionForGroup.js";
import updateFulfillmentOptionsForGroup from "./updateFulfillmentOptionsForGroup.js";

export default {
selectFulfillmentOptionForGroup,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { decodeCartOpaqueId, decodeFulfillmentGroupOpaqueId } from "@reactioncommerce/reaction-graphql-xforms/cart";
import { decodeFulfillmentMethodOpaqueId } from "@reactioncommerce/reaction-graphql-xforms/fulfillment";
import selectFulfillmentOptionForGroupMutation from "../../mutations/selectFulfillmentOptionForGroup";
import selectFulfillmentOptionForGroupMutation from "../../mutations/selectFulfillmentOptionForGroup.js";

/**
* @name Mutation/selectFulfillmentOptionForGroup
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { decodeCartOpaqueId, decodeFulfillmentGroupOpaqueId } from "@reactioncommerce/reaction-graphql-xforms/cart";
import updateFulfillmentOptionsForGroupMutation from "../../mutations/updateFulfillmentOptionsForGroup";
import updateFulfillmentOptionsForGroupMutation from "../../mutations/updateFulfillmentOptionsForGroup.js";

/**
* @name Mutation/updateFulfillmentOptionsForGroup
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FulfillmentMethod from "./FulfillmentMethod";
import Mutation from "./Mutation";
import FulfillmentMethod from "./FulfillmentMethod/index.js";
import Mutation from "./Mutation/index.js";

/**
* Fulfillment related GraphQL resolvers
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ReactionError from "@reactioncommerce/reaction-error";
import hashLoginToken from "/imports/node-app/core/util/hashLoginToken";
import hashLoginToken from "../../../core/util/hashLoginToken";

/**
* @summary Gets a cart from the db by ID. If there is an account for the request, verifies that the
Expand Down
49 changes: 43 additions & 6 deletions imports/node-app/core/ReactionNodeApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import collectionIndex from "/imports/utils/collectionIndex";
import createApolloServer from "./createApolloServer";
import getRootUrl from "/imports/plugins/core/core/server/util/getRootUrl";
import getAbsoluteUrl from "/imports/plugins/core/core/server/util/getAbsoluteUrl";
import initReplicaSet from "./util/initReplicaSet";

export default class ReactionNodeApp {
constructor(options = {}) {
Expand Down Expand Up @@ -265,13 +266,24 @@ export default class ReactionNodeApp {
* @returns {Promise<undefined>} Nothing
*/
async start({ mongoUrl, port } = {}) {
if (this.options.shouldInitReplicaSet) {
try {
await initReplicaSet(mongoUrl);
} catch (error) {
Logger.warn(`Failed to initialize a MongoDB replica set. This may result in errors or some things not working. Error: ${error.message}`);
}
}

// (1) Connect to MongoDB database
await this.connectToMongo({ mongoUrl });

// (2) Run service startup functions
// (2) Init the server here. Some startup may need `app.expressApp`
this.initServer();

// (3) Run service startup functions
await this.runServiceStartup();

// (3) Start the Express GraphQL server
// (4) Start the Express GraphQL server
await this.startServer({ port });
}

Expand All @@ -281,11 +293,27 @@ export default class ReactionNodeApp {
* @returns {Promise<undefined>} Nothing
*/
async stop() {
// (1) Disconnect from MongoDB database
await this.disconnectFromMongo();

// (2) Stop the Express GraphQL server
// (1) Stop the Express GraphQL server
await this.stopServer();

// (2) Run all "shutdown" functions registered by plugins
const shutdownFunctionsRegisteredByPlugins = this.functionsByType.shutdown;
if (Array.isArray(shutdownFunctionsRegisteredByPlugins)) {
// We are intentionally running these in series, in the order in which they were registered
for (const shutdownFunctionInfo of shutdownFunctionsRegisteredByPlugins) {
Logger.info(`Running shutdown function "${shutdownFunctionInfo.func.name}" for plugin "${shutdownFunctionInfo.pluginName}"...`);
const startTime = Date.now();
await shutdownFunctionInfo.func(this.context); // eslint-disable-line no-await-in-loop
const elapsedMs = Date.now() - startTime;
Logger.info(`Shutdown function "${shutdownFunctionInfo.func.name}" for plugin "${shutdownFunctionInfo.pluginName}" finished in ${elapsedMs}ms`);
}
}

// (3) Stop app events since the handlers will not have database access after this point
appEvents.stop();

// (4) Disconnect from MongoDB database
await this.disconnectFromMongo();
}

/**
Expand Down Expand Up @@ -323,5 +351,14 @@ export default class ReactionNodeApp {
}

this._registerFunctionsByType(plugin.functionsByType, plugin.name);

if (plugin.contextAdditions) {
Object.keys(plugin.contextAdditions).forEach((key) => {
if ({}.hasOwnProperty.call(this.context, key)) {
throw new Error(`Plugin ${plugin.name} is trying to add ${key} key to context but it's already there`);
}
this.context[key] = plugin.contextAdditions[key];
});
}
}
}
6 changes: 6 additions & 0 deletions imports/node-app/core/createApolloServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ export default function createApolloServer(options = {}) {
tokenMiddleware(contextFromOptions)
);

// Redirect for graphql-alpha route
app.all("/graphql-alpha", (req, res) => {
// Redirect to path once graphql-alpha is received
res.redirect(path);
});

apolloServer.applyMiddleware({ app, cors: true, path });

return {
Expand Down
Loading

0 comments on commit b03e63b

Please sign in to comment.