Skip to content

Commit

Permalink
Merge branch 'trunk' into feat/upgrade-mongodb
Browse files Browse the repository at this point in the history
  • Loading branch information
vanpho93 authored Dec 19, 2022
2 parents 87fea30 + 924e7f1 commit 189a6f8
Show file tree
Hide file tree
Showing 30 changed files with 1,562 additions and 8 deletions.
9 changes: 9 additions & 0 deletions .changeset/lazy-zoos-listen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@reactioncommerce/api-core": minor
"@reactioncommerce/api-plugin-accounts": minor
"@reactioncommerce/api-plugin-orders": minor
"@reactioncommerce/api-plugin-products": minor
"@reactioncommerce/api-utils": minor
---

Filter feature. This new feature provides a common function that can be used in a new query endpoint to get filtered results from any collection.
94 changes: 94 additions & 0 deletions packages/api-core/src/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,100 @@ enum MassUnit {
oz
}

"Relational Operator Types used in filtering inside a single condition"
enum RelationalOperatorTypes{
"Begins With used with String types to filter based on the beginning of the string"
beginsWith

"Ends With used with String types to filter based on the end of the string"
endsWith

"Equal to"
eq

"Greater Than"
gt

"Greater Than or Equal"
gte

"In used with Array types to filter based on the array containing the value"
in

"Less Than"
lt

"Less Than or Equal"
lte

"Not Equal to"
ne

"Not In used with Array types to filter based on the array not containing the value"
nin

"Regex used with String types to filter based on the regex pattern"
regex
}

"Single Condition for filter, use exactly one of the optional input value type"
input SingleConditionInput {
"Value to filter if it is Boolean input"
booleanValue: Boolean

"Flag to set if the regex is case insensitive"
caseSensitive: Boolean

"Value to filter if it is Date input"
dateValue: DateTime

"Value to filter if it is Float Array input"
floatArrayValue: [Float]

"Value to filter if it is Float input"
floatValue: Float

"Value to filter if it is Integer Array input"
integerArrayValue: [Int]

"Value to filter if it is Integer input"
integerValue: Int

"Field name"
key: String!

"Logical NOT operator to negate the condition"
logicalNot: Boolean

"Relational Operator to join the key and value"
relationalOperator: RelationalOperatorTypes!

"Value to filter if it is String Array input"
stringArrayValue: [String]

"Value to filter if it is String input"
stringValue: String
}

"Filter with One level of conditions (use either 'any' or 'all' not both)"
input ConditionsArray {
"Array of single-conditions"
all: [SingleConditionInput]

"Array of single-conditions"
any: [SingleConditionInput]
}

"Filter with nested conditions of input (use either 'any' or 'all' not both)"
input FilterConditionsInput {
"Array holding Nested conditions (use either 'any' or 'all' not both)"
all: [ConditionsArray]

"Array holding Nested conditions (use either 'any' or 'all' not both)"
any: [ConditionsArray]
}


"A list of URLs for various sizes of an image"
type ImageSizes {
"Use this URL to get a large resolution file for this image"
Expand Down
2 changes: 1 addition & 1 deletion packages/api-plugin-accounts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
"sideEffects": false,
"dependencies": {
"@reactioncommerce/api-utils": "^1.16.9",
"@reactioncommerce/api-utils": "~1.17.1",
"@reactioncommerce/db-version-check": "^1.0.0",
"@reactioncommerce/logger": "^1.1.3",
"@reactioncommerce/random": "^1.0.2",
Expand Down
24 changes: 24 additions & 0 deletions packages/api-plugin-accounts/src/queries/filterAccounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import generateFilterQuery from "@reactioncommerce/api-utils/generateFilterQuery.js";

/**
* @name filterAccounts
* @method
* @memberof GraphQL/Accounts
* @summary Query the Accounts collection for a list of customers/accounts
* @param {Object} context - an object containing the per-request state
* @param {Object} conditions - object containing the filter conditions
* @param {String} shopId - shopID to filter by
* @returns {Promise<Object>} Accounts object Promise
*/
export default async function filterAccounts(context, conditions, shopId) {
const { collections: { Accounts } } = context;

if (!shopId) {
throw new Error("shopId is required");
}
await context.validatePermissions("reaction:legacy:accounts", "read", { shopId });

const { filterQuery } = generateFilterQuery(context, "Account", conditions, shopId);

return Accounts.find(filterQuery);
}
25 changes: 25 additions & 0 deletions packages/api-plugin-accounts/src/queries/filterCustomers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import generateFilterQuery from "@reactioncommerce/api-utils/generateFilterQuery.js";

/**
* @name filterCustomers
* @method
* @memberof GraphQL/Customers
* @summary Query the Accounts collection for a list of customers/accounts
* @param {Object} context - an object containing the per-request state
* @param {Object} conditions - object containing the filter conditions
* @param {String} shopId - shopID to filter by
* @returns {Promise<Object>} Accounts object Promise
*/
export default async function filterCustomers(context, conditions, shopId) {
const { collections: { Accounts } } = context;

if (!shopId) {
throw new Error("shopId is required");
}
await context.validatePermissions("reaction:legacy:accounts", "read", { shopId });

const { filterQuery } = generateFilterQuery(context, "Account", conditions, shopId);

filterQuery.groups = { $in: [null, []] }; // filter out non-customer accounts
return Accounts.find(filterQuery);
}
4 changes: 4 additions & 0 deletions packages/api-plugin-accounts/src/queries/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import groupsByAccount from "./groupsByAccount.js";
import groupsById from "./groupsById.js";
import invitations from "./invitations.js";
import userAccount from "./userAccount.js";
import filterAccounts from "./filterAccounts.js";
import filterCustomers from "./filterCustomers.js";

export default {
filterAccounts,
filterCustomers,
accountByUserId,
accounts,
customers,
Expand Down
31 changes: 31 additions & 0 deletions packages/api-plugin-accounts/src/resolvers/Query/filterAccounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import getPaginatedResponse from "@reactioncommerce/api-utils/graphql/getPaginatedResponse.js";
import wasFieldRequested from "@reactioncommerce/api-utils/graphql/wasFieldRequested.js";

/**
* @name Query/accounts
* @method
* @memberof Accounts/Query
* @summary Query for a list of accounts
* @param {Object} _ - unused
* @param {Object} args - an object of all arguments that were sent by the client
* @param {String} args.shopId - id of shop to query
* @param {Object} args.conditions - object containing the filter conditions
* @param {Object} context - an object containing the per-request state
* @param {Object} info Info about the GraphQL request
* @returns {Promise<Object>} Accounts
*/
export default async function filterAccounts(_, args, context, info) {
const {
shopId,
conditions,
...connectionArgs
} = args;

const query = await context.queries.filterAccounts(context, conditions, shopId);

return getPaginatedResponse(query, connectionArgs, {
includeHasNextPage: wasFieldRequested("pageInfo.hasNextPage", info),
includeHasPreviousPage: wasFieldRequested("pageInfo.hasPreviousPage", info),
includeTotalCount: wasFieldRequested("totalCount", info)
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import getPaginatedResponse from "@reactioncommerce/api-utils/graphql/getPaginatedResponse.js";
import wasFieldRequested from "@reactioncommerce/api-utils/graphql/wasFieldRequested.js";

/**
* @name Query/accounts
* @method
* @memberof Customers/Query
* @summary Query for a list of customers
* @param {Object} _ - unused
* @param {Object} args - an object of all arguments that were sent by the client
* @param {String} args.shopId - id of shop to query
* @param {Object} args.conditions - object containing the filter conditions
* @param {Object} context - an object containing the per-request state
* @param {Object} info Info about the GraphQL request
* @returns {Promise<Object>} Accounts
*/
export default async function filterCustomers(_, args, context, info) {
const {
shopId,
conditions,
...connectionArgs
} = args;

const query = await context.queries.filterCustomers(context, conditions, shopId);

return getPaginatedResponse(query, connectionArgs, {
includeHasNextPage: wasFieldRequested("pageInfo.hasNextPage", info),
includeHasPreviousPage: wasFieldRequested("pageInfo.hasPreviousPage", info),
includeTotalCount: wasFieldRequested("totalCount", info)
});
}
4 changes: 4 additions & 0 deletions packages/api-plugin-accounts/src/resolvers/Query/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import group from "./group.js";
import groups from "./groups.js";
import invitations from "./invitations.js";
import viewer from "./viewer.js";
import filterAccounts from "./filterAccounts.js";
import filterCustomers from "./filterCustomers.js";

export default {
filterAccounts,
filterCustomers,
account,
accounts,
customers,
Expand Down
60 changes: 60 additions & 0 deletions packages/api-plugin-accounts/src/schemas/account.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,66 @@ extend type Query {
id: ID!
): Account

"Query to get a filtered list of Accounts"
filterAccounts(
"Shop ID"
shopId: ID!,

"Input Conditions for fliter (use either 'any' or 'all' not both)"
conditions: FilterConditionsInput,

"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 only results that come after the Nth result. This parameter may be used with the `first` parameter."
offset: Int

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

"By default, accounts are sorted by createdAt. Set this to sort by one of the other allowed fields"
sortBy: AccountSortByField = createdAt
): AccountConnection

"Query to get a filtered list of Customers"
filterCustomers(
"Shop ID"
shopId: ID!,

"Input Conditions for fliter (use either 'any' or 'all' not both)"
conditions: FilterConditionsInput,

"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 only results that come after the Nth result. This parameter may be used with the `first` parameter."
offset: Int

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

"By default, customers are sorted by createdAt. Set this to sort by one of the other allowed fields"
sortBy: AccountSortByField = createdAt
): AccountConnection

"Returns accounts optionally filtered by account groups"
accounts(
"Return only accounts in any of these groups"
Expand Down
2 changes: 1 addition & 1 deletion packages/api-plugin-orders/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
"sideEffects": false,
"dependencies": {
"@reactioncommerce/api-utils": "^1.16.5",
"@reactioncommerce/api-utils": "~1.17.1",
"@reactioncommerce/logger": "^1.1.4",
"@reactioncommerce/random": "^1.0.2",
"@reactioncommerce/reaction-error": "^1.0.1",
Expand Down
25 changes: 25 additions & 0 deletions packages/api-plugin-orders/src/queries/filterOrders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import generateFilterQuery from "@reactioncommerce/api-utils/generateFilterQuery.js";

/**
* @name filterOrders
* @method
* @memberof GraphQL/Orders
* @summary Query the Orders collection for a list of orders
* @param {Object} context - an object containing the per-request state
* @param {Object} conditions - object containing the filter conditions
* @param {String} shopId - shopID to filter by
* @returns {Promise<Object>} Orders object Promise
*/
export default async function filterOrders(context, conditions, shopId) {
const { collections: { Orders } } = context;

if (!shopId) {
throw new Error("shopId is required");
}

await context.validatePermissions("reaction:legacy:orders", "read", { shopId });

const { filterQuery } = generateFilterQuery(context, "Order", conditions, shopId);

return Orders.find(filterQuery);
}
2 changes: 2 additions & 0 deletions packages/api-plugin-orders/src/queries/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import orders from "./orders.js";
import ordersByAccountId from "./ordersByAccountId.js";
import refunds from "./refunds.js";
import refundsByPaymentId from "./refundsByPaymentId.js";
import filterOrders from "./filterOrders.js";

export default {
filterOrders,
orderById,
orderByReferenceId,
orders,
Expand Down
Loading

0 comments on commit 189a6f8

Please sign in to comment.