Skip to content

Commit

Permalink
Merge pull request #5777 from reactioncommerce/release-v2.9.0
Browse files Browse the repository at this point in the history
Release v2.9.0
  • Loading branch information
rosshadden committed Nov 14, 2019
2 parents a308e42 + bf94a63 commit 42f24a5
Show file tree
Hide file tree
Showing 27 changed files with 1,377 additions and 68 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defaults: &defaults
- DOCKER_NAMESPACE: "reactioncommerce"
- DOCKER_NAME: "reaction"
- GLOBAL_CACHE_VERSION: "v2"
- TOOL_NODE_FLAGS: "--max-old-space-size=4096"
- TOOL_NODE_FLAGS: "--max-old-space-size=4000"
working_directory: ~/reaction-app
docker:
- image: circleci/node:8-stretch
Expand Down Expand Up @@ -96,7 +96,7 @@ jobs:
name: Docker build
command: |
docker build \
--build-arg TOOL_NODE_FLAGS="--max-old-space-size=4096" \
--build-arg TOOL_NODE_FLAGS="--max-old-space-size=4000" \
-t "$DOCKER_REPOSITORY:$CIRCLE_SHA1" .
mkdir -p docker-cache
docker save \
Expand Down
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
# v2.9.0

Reaction v2.9.0 adds integration tests for GraphQL API endpoints, security updates and fixes a fulfillment method bug.

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

## Notable changes

### Add integration tests and increase `maxWorkers`

As a part of the major push to remove the Meteor dependency from the GraphQL API, we've been adding more Jest integration tests with the goal of full integration test coverage for all GraphQL queries and mutations provided by all core and included plugins. In this release, we've added tests for the following GraphQL queries: `shopBySlug`, `orderByReferenceId`, `ordersByAccountId`, `addTag` and `removeTag`. See test coverage epic [here](https://github.com/reactioncommerce/reaction/issues/5310).

### Fix a fulfillment bug

Refactors the `updateOrderFulfillmentGroup` GraphQL mutation to update only the specified group, not all fulfillment groups.

### Update packages for security vulnerabilities

Bumps [@reactioncommerce/data-factory](https://github.com/reactioncommerce/data-factory/) to version 1.0.1 and [@reactioncommerce/job-queue](https://github.com/reactioncommerce/reaction-job-queue) to version 1.0.5.


## Tests
- Add integration tests for shopBySlug GraphQL query [#5701](https://github.com/reactioncommerce/reaction/pull/5701)
- Add integration tests for orderByReferenceId GraphQL query [#5697](https://github.com/reactioncommerce/reaction/pull/5697)
- Add integration tests for ordersByAccountId GraphQL query [#5696](https://github.com/reactioncommerce/reaction/pull/5696)
- Add integration tests for addTag GraphQL query [#5683](https://github.com/reactioncommerce/reaction/pull/5683)
- Add integration tests for removeTag GraphQL query [#5687](https://github.com/reactioncommerce/reaction/pull/5687)

## Security
- Upgrade insecure package versions [#5745](https://github.com/reactioncommerce/reaction/pull/5745)

## Refactors & Fixes
- Only update fulfillment group for which id is provided [#5723](https://github.com/reactioncommerce/reaction/pull/5723)

## Contributors
Thanks to [@YuuwakU](https://github.com/YuuwakU) for contributing to this release! 🎉

# v2.8.1
Reaction v2.8.1 adds a bug fix and contains no breaking changes since v2.8.0

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import SimpleSchema from "simpl-schema";
import ReactionError from "@reactioncommerce/reaction-error";
import { Order as OrderSchema } from "../simpleSchemas.js";

const inputSchema = new SimpleSchema({
tracking: {
Expand Down Expand Up @@ -56,33 +55,34 @@ export default async function updateOrderFulfillmentGroup(context, input) {

const modifier = {
$set: {
"shipping.$.updatedAt": new Date(),
"shipping.$[group].updatedAt": new Date(),
"updatedAt": new Date()
}
};

if (tracking) modifier.$set["shipping.$.tracking"] = tracking;
if (trackingUrl) modifier.$set["shipping.$.trackingUrl"] = trackingUrl;
if (tracking) modifier.$set["shipping.$[group].tracking"] = tracking;
if (trackingUrl) modifier.$set["shipping.$[group].trackingUrl"] = trackingUrl;

if (status && orderFulfillmentGroup.workflow.status !== status) {
modifier.$set["shipping.$.workflow.status"] = status;
modifier.$set["shipping.$[group].workflow.status"] = status;
modifier.$push = {
"shipping.$.workflow.workflow": status
"shipping.$[group].workflow.workflow": status
};
}

// Skip updating if we have no updates to make
if (Object.keys(modifier.$set).length === 2) return { order };

OrderSchema.validate(modifier, { modifier: true });

const { modifiedCount, value: updatedOrder } = await Orders.findOneAndUpdate(
{
"_id": orderId,
"shipping._id": orderFulfillmentGroupId
},
modifier,
{ returnOriginal: false }
{
arrayFilters: [{ "group._id": orderFulfillmentGroupId }],
returnOriginal: false
}
);
if (modifiedCount === 0 || !updatedOrder) throw new ReactionError("server-error", "Unable to update order");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,16 +161,19 @@ test("updates an order fulfillment group", async () => {
},
{
$set: {
"shipping.$.tracking": "TRACK_REF",
"shipping.$.trackingUrl": "http://track.me/TRACK_REF",
"shipping.$.updatedAt": jasmine.any(Date),
"shipping.$.workflow.status": "NEW_STATUS",
"shipping.$[group].tracking": "TRACK_REF",
"shipping.$[group].trackingUrl": "http://track.me/TRACK_REF",
"shipping.$[group].updatedAt": jasmine.any(Date),
"shipping.$[group].workflow.status": "NEW_STATUS",
"updatedAt": jasmine.any(Date)
},
$push: {
"shipping.$.workflow.workflow": "NEW_STATUS"
"shipping.$[group].workflow.workflow": "NEW_STATUS"
}
},
{ returnOriginal: false }
{
arrayFilters: [{ "group._id": "group1" }],
returnOriginal: false
}
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ReactionError from "@reactioncommerce/reaction-error";
* @param {Object} context - an object containing the per-request state
* @param {Object} params - request parameters
* @param {String} params.accountId - Account ID to search orders for
* @param {String} params.orderStatus - Workflow status to limit search results
* @param {String} params.orderStatus - Workflow status as a string, or an Array of strings to limit search results
* @param {String} params.shopIds - Shop IDs for the shops that owns the orders
* @returns {Promise<Object>|undefined} - An Array of Order documents, if found
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { decodeShopOpaqueId } from "@reactioncommerce/reaction-graphql-xforms/sh
* @name Query/ordersByAccountId
* @method
* @memberof Order/GraphQL
* @summary Get an order by its reference ID
* @summary Get all orders from an accountId and (optionally) shopIds and status
* @param {Object} parentResult - unused
* @param {ConnectionArgs} args - An object of all arguments that were sent by the client
* @param {String} args.accountId - accountId of owner of the orders
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default async function stripeCapturePayment(context, paymentMethod) {
// If discount is 100%, capture 100% and then refund 100% of transaction
if (captureDetails.amount === accounting.unformat(0)) {
const voidedAmount = unformatFromStripe(paymentMethod.transactions[0].amount);
stripeCaptureCharge(paymentMethod);
await stripeCaptureCharge(context, paymentMethod);

return stripeCreateRefund(context, paymentMethod, voidedAmount);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export default async function createNavigationItem(context, navigationItem) {

const { metadata, draftData = {} } = navigationItem;

if (userHasPermission(["core"]) === false) {
const shopId = await context.queries.primaryShopId(collections);

if (userHasPermission(["core"], shopId) === false) {
throw new ReactionError("access-denied", "You do not have permission to create a navigation item");
}

Expand All @@ -28,8 +30,6 @@ export default async function createNavigationItem(context, navigationItem) {
}
}

const shopId = await context.queries.primaryShopId(collections);

const newNavigationItem = {
...navigationItem,
_id: Random.id(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export default async function deleteNavigationItem(context, _id) {
const { collections, userHasPermission } = context;
const { NavigationItems } = collections;

if (userHasPermission(["core"]) === false) {
const shopId = await context.queries.primaryShopId(collections);

if (userHasPermission(["core"], shopId) === false) {
throw new ReactionError("access-denied", "You do not have permission to delete a navigation item");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const mockNavigationItem = {
}
};

mockContext.queries.primaryShopId = jest.fn().mockName("primaryShopId").mockImplementation(() => "SHOP_ID");

test("calls NavigationItems.deleteOne and returns an object that validates against the schema", async () => {
mockContext.collections.NavigationItems.findOne.mockReturnValueOnce(Promise.resolve(mockNavigationItem));
const navigationItem = await deleteNavigationItemMutation(mockContext, {});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ export default async function navigationTreeById(context, { language, navigation
if (navigationTree) {
// Add language from args so that we can use it in items & draftItems resolvers
navigationTree.language = language;
const shopId = await context.queries.primaryShopId(collections);

const isAdmin = userHasPermission(["admin", "owner", "create-product"]);
const isAdmin = userHasPermission(["admin", "owner", "create-product"], shopId);

// Filter items based on visibility options and user permissions
navigationTree.items = filterNavigationTreeItems(navigationTree.items, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const mockNavigationTree = {
language: "en"
};

mockContext.queries.primaryShopId = jest.fn().mockName("primaryShopId").mockImplementation(() => "SHOP_ID");

test("calls NavigationTrees.findOne and returns a navigation tree", async () => {
mockContext.collections.NavigationTrees.findOne.mockReturnValueOnce(mockNavigationTree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default async function stripeCapturePayment(context, paymentMethod) {
// If discount is 100%, capture 100% and then refund 100% of transaction
if (captureDetails.amount === accounting.unformat(0)) {
const voidedAmount = unformatFromStripe(paymentMethod.transactions[0].amount);
stripeCaptureCharge(paymentMethod);
await stripeCaptureCharge(context, paymentMethod);

return stripeCreateRefund(context, paymentMethod, voidedAmount);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/api/router/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const MetaData = {

Reaction.DOM.setMetaTag({
name: "keywords",
content: keywords ? keywords.toString() : ""
content: keywords.length ? keywords.toString() : ""
});

// set site defaults
Expand Down

0 comments on commit 42f24a5

Please sign in to comment.