Skip to content

Commit

Permalink
Merge pull request #5977 from reactioncommerce/fix-aldeed-minor-fixes…
Browse files Browse the repository at this point in the history
…-3.0

Some minor 3.0 fixes
  • Loading branch information
willopez authored Dec 24, 2019
2 parents 729c9dc + 50d423a commit 3ff93ac
Show file tree
Hide file tree
Showing 35 changed files with 143 additions and 118 deletions.
8 changes: 2 additions & 6 deletions src/core-services/account/resolvers/Shop/administrators.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import getPaginatedResponse from "@reactioncommerce/api-utils/graphql/getPaginatedResponse.js";
import wasFieldRequested from "@reactioncommerce/api-utils/graphql/wasFieldRequested.js";
import { decodeShopOpaqueId } from "../../xforms/id.js";

/**
* @name Shop/administrators
* @method
* @memberof Shop/GraphQL
* @summary find and return the administrators (users with "admin" or "owner" role) for a shop
* @param {Object} shop - The shop object returned by the parent resolver
* @param {Object} shop Shop returned by the parent resolver
* @param {ConnectionArgs} connectionArgs - an object of all arguments that were sent by the client
* @param {Object} context - an object containing the per-request state
* @param {Object} info Info about the GraphQL request
* @returns {Promise<Object[]>} Promise that resolves with array of user account objects
*/
export default async function administrators(shop, connectionArgs, context, info) {
// Transform ID from base64
const dbShopId = decodeShopOpaqueId(shop._id);

const query = await context.queries.shopAdministrators(context, dbShopId);
const query = await context.queries.shopAdministrators(context, shop._id);
return getPaginatedResponse(query, connectionArgs, {
includeHasNextPage: wasFieldRequested("pageInfo.hasNextPage", info),
includeHasPreviousPage: wasFieldRequested("pageInfo.hasPreviousPage", info),
Expand Down
11 changes: 6 additions & 5 deletions src/core-services/account/resolvers/Shop/administrators.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import getFakeMongoCursor from "@reactioncommerce/api-utils/tests/getFakeMongoCursor.js";
import administratorsResolver from "./administrators.js";

const base64ID = "cmVhY3Rpb24vc2hvcDoxMjM="; // reaction/shop:123
const shopId = "123";

const mockAccounts = [
{ _id: "a1", name: "Owner" },
Expand All @@ -15,10 +15,12 @@ test("calls queries.shopAdministrators and returns a partial connection", async
const shopAdministrators = jest.fn().mockName("queries.shopAdministrators")
.mockReturnValueOnce(Promise.resolve(mockAdministratorsQuery));

const result = await administratorsResolver({ _id: base64ID }, {}, {
const context = {
queries: { shopAdministrators },
userId: "999"
}, { fieldNodes: [] });
};

const result = await administratorsResolver({ _id: shopId }, {}, context, { fieldNodes: [] });

expect(result).toEqual({
nodes: mockAccounts,
Expand All @@ -29,6 +31,5 @@ test("calls queries.shopAdministrators and returns a partial connection", async
totalCount: null
});

expect(shopAdministrators).toHaveBeenCalled();
expect(shopAdministrators.mock.calls[0][1]).toBe("123");
expect(shopAdministrators).toHaveBeenCalledWith(context, shopId);
});
11 changes: 3 additions & 8 deletions src/core-services/account/resolvers/Shop/groups.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import getPaginatedResponse from "@reactioncommerce/api-utils/graphql/getPaginatedResponse.js";
import wasFieldRequested from "@reactioncommerce/api-utils/graphql/wasFieldRequested.js";
import { decodeShopOpaqueId } from "../../xforms/id.js";

/**
* @name Shop/groups
* @method
* @memberof Shop/GraphQL
* @summary find and return the groups for a shop
* @param {Object} resolverArgs - an object containing the result returned from the resolver
* @param {String} resolverArgs._id - id of group to query
* @param {Object} shop Shop returned by the parent resolver
* @param {GroupConnectionArgs} connectionArgs - an object of all arguments that were sent by the client. {@link ConnectionArgs|See default connection arguments}
* @param {Object} context - an object containing the per-request state
* @param {Object} info Info about the GraphQL request
* @returns {Promise<Object[]>} Promise that resolves with array of user Group objects
*/
export default async function groups({ _id }, connectionArgs, context, info) {
// Transform ID from base64
const dbShopId = decodeShopOpaqueId(_id);
const query = await context.queries.groups(context, dbShopId);

export default async function groups(shop, connectionArgs, context, info) {
const query = await context.queries.groups(context, shop._id);
return getPaginatedResponse(query, connectionArgs, {
includeHasNextPage: wasFieldRequested("pageInfo.hasNextPage", info),
includeHasPreviousPage: wasFieldRequested("pageInfo.hasPreviousPage", info),
Expand Down
11 changes: 6 additions & 5 deletions src/core-services/account/resolvers/Shop/groups.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import getFakeMongoCursor from "@reactioncommerce/api-utils/tests/getFakeMongoCursor.js";
import groupsResolver from "./groups.js";

const base64ID = "cmVhY3Rpb24vc2hvcDoxMjM="; // reaction/shop:123
const shopId = "123";

const mockGroups = [
{ _id: "a1", name: "admin" },
Expand All @@ -14,9 +14,11 @@ const mockGroupsQuery = getFakeMongoCursor("Groups", mockGroups);
test("calls queries.groups and returns a partial connection", async () => {
const groups = jest.fn().mockName("queries.groups").mockReturnValueOnce(Promise.resolve(mockGroupsQuery));

const result = await groupsResolver({ _id: base64ID }, {}, {
const context = {
queries: { groups }
}, { fieldNodes: [] });
};

const result = await groupsResolver({ _id: shopId }, {}, context, { fieldNodes: [] });

expect(result).toEqual({
nodes: mockGroups,
Expand All @@ -27,6 +29,5 @@ test("calls queries.groups and returns a partial connection", async () => {
totalCount: null
});

expect(groups).toHaveBeenCalled();
expect(groups.mock.calls[0][1]).toBe("123");
expect(groups).toHaveBeenCalledWith(context, shopId);
});
12 changes: 3 additions & 9 deletions src/core-services/account/resolvers/Shop/roles.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import getPaginatedResponse from "@reactioncommerce/api-utils/graphql/getPaginatedResponse.js";
import wasFieldRequested from "@reactioncommerce/api-utils/graphql/wasFieldRequested.js";
import { decodeShopOpaqueId } from "../../xforms/id.js";

/**
* @name Shop/roles
* @method
* @memberof Shop/GraphQL
* @summary find and return the roles for a shop
* @param {Object} _ - unused
* @param {Object} shop Shop returned by the parent resolver
* @param {Object} connectionArgs - an object of all arguments that were sent by the client
* @param {String} connectionArgs.shopId - id of shop to query
* @param {String} connectionArgs.after - Connection argument
* @param {String} connectionArgs.before - Connection argument
* @param {Number} connectionArgs.first - Connection argument
Expand All @@ -18,12 +16,8 @@ import { decodeShopOpaqueId } from "../../xforms/id.js";
* @param {Object} info Info about the GraphQL request
* @returns {Promise<Object[]>} Promise that resolves with array of user Roles objects
*/
export default async function roles({ _id }, connectionArgs, context, info) {
// Transform ID from base64
const dbShopId = decodeShopOpaqueId(_id);

const query = await context.queries.roles(context, dbShopId);

export default async function roles(shop, connectionArgs, context, info) {
const query = await context.queries.roles(context, shop._id);
return getPaginatedResponse(query, connectionArgs, {
includeHasNextPage: wasFieldRequested("pageInfo.hasNextPage", info),
includeHasPreviousPage: wasFieldRequested("pageInfo.hasPreviousPage", info),
Expand Down
11 changes: 6 additions & 5 deletions src/core-services/account/resolvers/Shop/roles.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import getFakeMongoCursor from "@reactioncommerce/api-utils/tests/getFakeMongoCursor.js";
import rolesResolver from "./roles.js";

const base64ID = "cmVhY3Rpb24vc2hvcDoxMjM="; // reaction/shop:123
const shopId = "123";

const mockRoles = [
{ _id: "a1", name: "admin" },
Expand All @@ -14,9 +14,11 @@ const mockRolesQuery = getFakeMongoCursor("Tags", mockRoles);
test("calls queries.roles and returns a partial connection", async () => {
const roles = jest.fn().mockName("queries.roles").mockReturnValueOnce(Promise.resolve(mockRolesQuery));

const result = await rolesResolver({ _id: base64ID }, {}, {
const context = {
queries: { roles }
}, { fieldNodes: [] });
};

const result = await rolesResolver({ _id: shopId }, {}, context, { fieldNodes: [] });

expect(result).toEqual({
nodes: mockRoles,
Expand All @@ -27,6 +29,5 @@ test("calls queries.roles and returns a partial connection", async () => {
totalCount: null
});

expect(roles).toHaveBeenCalled();
expect(roles.mock.calls[0][1]).toBe("123");
expect(roles).toHaveBeenCalledWith(context, shopId);
});
1 change: 1 addition & 0 deletions src/core-services/address/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"deleteItemButtonText": "Delete",
"entryFormSubmitButtonText": "Add service",
"helpText": "Add one or more services to enable address validation for this shop. The first service that matches the country filter will be used.",
"saveFailed": "There was a problem saving your changes",
"serviceNameLabel": "Validation service",
"title": "Address Validation"
}
Expand Down
5 changes: 4 additions & 1 deletion src/core-services/catalog/queries/catalogItemProduct.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import ReactionError from "@reactioncommerce/reaction-error";
* @param {Object} context - an object containing the per-request state
* @param {Object} params - request parameters
* @param {String} [params._id] - Product id to include
* @param {String} [param.shopId] - ID of shop that owns the product
* @param {String} [param.slug] - Product slug (handle)
* @returns {Object} - A Product from the Catalog
*/
export default async function catalogItemProduct(context, { _id, slug } = {}) {
export default async function catalogItemProduct(context, { _id, shopId, slug } = {}) {
const { collections } = context;
const { Catalog } = collections;

Expand All @@ -25,6 +26,8 @@ export default async function catalogItemProduct(context, { _id, slug } = {}) {
"product.isVisible": true
};

if (shopId) query.shopId = shopId;

if (_id) {
query._id = _id;
} else {
Expand Down
8 changes: 1 addition & 7 deletions src/core-services/catalog/queries/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,12 @@ import catalogItemProduct from "./catalogItemProduct.js";
import findCatalogProductsAndVariants from "./findCatalogProductsAndVariants.js";
import findProductAndVariant from "./findProductAndVariant.js";
import findVariantInCatalogProduct from "./findVariantInCatalogProduct.js";
import tag from "./tag.js";
import tags from "./tags.js";
import tagsByIds from "./tagsByIds.js";

export default {
catalogItems,
catalogItemsAggregate,
catalogItemProduct,
findCatalogProductsAndVariants,
findProductAndVariant,
findVariantInCatalogProduct,
tag,
tags,
tagsByIds
findVariantInCatalogProduct
};
4 changes: 0 additions & 4 deletions src/core-services/catalog/resolvers/CatalogProduct/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@ import resolveShopFromShopId from "@reactioncommerce/api-utils/graphql/resolveSh
import xformCatalogProductMedia from "../../utils/xformCatalogProductMedia.js";
import xformCatalogProductVariants from "../../utils/xformCatalogProductVariants.js";
import { encodeProductOpaqueId, encodeCatalogProductOpaqueId } from "../../xforms/id.js";
import tagIds from "./tagIds.js";
import tags from "./tags.js";

export default {
_id: (node) => encodeCatalogProductOpaqueId(node._id),
media: (node, args, context) => node.media && node.media.map((mediaItem) => xformCatalogProductMedia(mediaItem, context)),
primaryImage: (node, args, context) => xformCatalogProductMedia(node.primaryImage, context),
productId: (node) => encodeProductOpaqueId(node.productId),
shop: resolveShopFromShopId,
tagIds,
tags,
variants: (node, args, context, info) => node.variants && xformCatalogProductVariants(context, node.variants, {
catalogProduct: node,
fields: graphqlFields(info)
Expand Down
11 changes: 9 additions & 2 deletions src/core-services/catalog/resolvers/Query/catalogItemProduct.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { decodeCatalogItemOpaqueId } from "../../xforms/id.js";
import { decodeCatalogItemOpaqueId, decodeShopOpaqueId } from "../../xforms/id.js";

/**
* @name Query.catalogItemProduct
Expand All @@ -7,12 +7,13 @@ import { decodeCatalogItemOpaqueId } from "../../xforms/id.js";
* @summary Get a CatalogItemProduct from the Catalog
* @param {Object} _ - unused
* @param {ConnectionArgs} args - an object of all arguments that were sent by the client
* @param {String} args.shopId - shop ID for catalog item product
* @param {String} args.slugOrId - slug or id for catalog item product
* @param {Object} context - an object containing the per-request state
* @returns {Promise<Object>} A CatalogItemProduct object
*/
export default async function catalogItemProduct(_, args, context) {
const { slugOrId } = args;
const { shopId: opaqueShopId, slugOrId } = args;

let productId;
let productSlug;
Expand All @@ -22,8 +23,14 @@ export default async function catalogItemProduct(_, args, context) {
productSlug = slugOrId;
}

let shopId;
if (opaqueShopId) {
shopId = decodeShopOpaqueId(opaqueShopId);
}

return context.queries.catalogItemProduct(context, {
_id: productId,
shopId,
slug: productSlug
});
}
30 changes: 6 additions & 24 deletions src/core-services/catalog/schemas/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -159,30 +159,6 @@ type CatalogProduct implements CatalogProductOrVariant & Node {
"When a shopper purchases this product, what types of fulfillment can they choose from?"
supportedFulfillmentTypes: [FulfillmentType]!

"The list of tag IDs that have been applied to this product"
tagIds: [ID]

"The list of tags that have been applied to this product"
tags(
"Return only results that come after this cursor. Use this with `first` to specify the number of results to return."
after: ConnectionCursor,

"Return only results that come before this cursor. Use this with `last` to specify the number of results to return."
before: ConnectionCursor,

"Return at most this many results. This parameter may be used with either `after` or `offset` parameters."
first: ConnectionLimitInt,

"Return at most this many results. This parameter may be used with the `before` parameter."
last: ConnectionLimitInt,

"Return results sorted in this order"
sortOrder: SortOrder = asc,

"By default, tags are sorted by ID. Set this to sort by one of the other allowed fields"
sortBy: TagSortByField = _id
): TagConnection

"Product title"
title: String

Expand Down Expand Up @@ -432,6 +408,12 @@ extend type Query {

"Gets product from catalog"
catalogItemProduct(
"""
ID of the shop that owns the catalog product. Not required but highly recommended if you
have multiple shops and `slugOrId` is a slug because slugs are unique only within a shop.
"""
shopId: ID

"Provide either a product ID or slug"
slugOrId: String
): CatalogItemProduct
Expand Down
6 changes: 4 additions & 2 deletions src/core-services/payments/mutations/approveOrderPayments.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ const inputSchema = new SimpleSchema({
export default async function approveOrderPayments(context, input = {}) {
inputSchema.validate(input);

const { appEvents, collections, userId } = context;
const { appEvents, collections, userId = null } = context;
const { Orders } = collections;
const { orderId, paymentIds, shopId } = input;

await context.validatePermissions(`reaction:orders:${orderId}`, "approve:payment", { shopId, legacyRoles: ["orders", "order/fulfillment"] });
if (!context.isInternalCall) {
await context.validatePermissions(`reaction:orders:${orderId}`, "approve:payment", { shopId, legacyRoles: ["orders", "order/fulfillment"] });
}

const order = await Orders.findOne({ _id: orderId, shopId });
if (!order) throw new ReactionError("not-found", "Order not found");
Expand Down
2 changes: 1 addition & 1 deletion src/core-services/product/mutations/cloneProducts.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export default async function cloneProducts(context, input) {
newProduct.isVisible = false;
if (newProduct.title) {
newProduct.title = await createTitle(context, newProduct.title, newProduct._id);
newProduct.handle = await createHandle(context, getSlug(newProduct.title), newProduct._id);
newProduct.handle = await createHandle(context, getSlug(newProduct.title), newProduct._id, newProduct.shopId);
}

const { insertedId: productInsertedId } = await Products.insertOne(newProduct, { validate: false });
Expand Down
6 changes: 3 additions & 3 deletions src/core-services/product/mutations/updateProduct.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,14 @@ export default async function updateProduct(context, input) {

// Slugify the handle input
if (typeof productInput.slug === "string") {
updateDocument.handle = await createHandle(context, getSlug(productInput.slug), productId);
updateDocument.handle = await createHandle(context, getSlug(productInput.slug), productId, shopId);
delete updateDocument.slug;
}

// If a title is supplied, and the currently stored product doesn't have a handle,
// then slugify the title and save it as the new handle (slug)
if (typeof productInput.title === "string" && !currentProduct.handle) {
updateDocument.handle = await createHandle(context, getSlug(productInput.title), productId);
if (typeof productInput.title === "string" && !currentProduct.handle && !updateDocument.handle) {
updateDocument.handle = await createHandle(context, getSlug(productInput.title), productId, shopId);
}

inputSchema.validate(updateDocument);
Expand Down
Loading

0 comments on commit 3ff93ac

Please sign in to comment.