Skip to content
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

feat: better cart and order item attributes, with labels #5253

Merged
merged 5 commits into from
Jul 1, 2019
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
7 changes: 2 additions & 5 deletions imports/collections/schemas/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@ import { Metafield } from "./metafield";
* @name CartItemAttribute
* @memberof Schemas
* @type {SimpleSchema}
* @property {String} label optional
* @property {String} label required
* @property {String} value optional
*/
const CartItemAttribute = new SimpleSchema({
label: {
type: String,
optional: true
},
label: String,
value: {
type: String,
optional: true
Expand Down
4 changes: 3 additions & 1 deletion imports/collections/schemas/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export const SocialMetadata = new SimpleSchema({
* @memberof Schemas
* @type {SimpleSchema}
* @property {String} _id required
* @property {String} attributeLabel required
* @property {String} barcode optional
* @property {Date} createdAt required
* @property {Number} height optional, default value: `0`
Expand All @@ -122,6 +123,7 @@ export const VariantBaseSchema = new SimpleSchema({
type: String,
label: "Catalog product variant Id"
},
"attributeLabel": String,
"barcode": {
type: String,
label: "Barcode",
Expand Down Expand Up @@ -226,7 +228,7 @@ export const VariantBaseSchema = new SimpleSchema({
});

/**
* @name VariantBaseSchema
* @name CatalogVariantSchema
* @memberof Schemas
* @type {SimpleSchema}
* @extends VariantBaseSchema
Expand Down
21 changes: 21 additions & 0 deletions imports/collections/schemas/orders.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,29 @@ export const OrderDiscount = new SimpleSchema({
discountId: String
});

/**
* @name OrderItemAttribute
* @memberof Schemas
* @type {SimpleSchema}
* @property {String} label required
* @property {String} value optional
*/
export const OrderItemAttribute = new SimpleSchema({
label: String,
value: {
type: String,
optional: true
}
});

/**
* @name OrderItem
* @memberof Schemas
* @summary Defines one item in an order
* @type {SimpleSchema}
* @property {String} _id Unique ID for the item
* @property {String} addedAt Date/time when this was first added to the cart/order
* @property {OrderItemAttribute[]} attributes Attributes of this item
* @property {String} cancelReason Free text reason for cancel, if this item is canceled
* @property {String} createdAt Date/time when this order item was created
* @property {Document[]} documents optional
Expand All @@ -186,6 +202,11 @@ export const OrderDiscount = new SimpleSchema({
export const OrderItem = new SimpleSchema({
"_id": String,
"addedAt": Date,
"attributes": {
type: Array,
optional: true
},
"attributes.$": OrderItemAttribute,
"cancelReason": {
type: String,
optional: true
Expand Down
18 changes: 7 additions & 11 deletions imports/collections/schemas/products.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,14 @@ export const ProductVariant = new SimpleSchema({
"ancestors.$": {
type: String
},
"attributeLabel": {
type: String,
optional: true
},
"barcode": {
label: "Barcode",
type: String,
optional: true,
custom() {
if (Meteor.isClient) {
if (this.siblingField("type").value === "inventory" && !this.value) {
return SimpleSchema.ErrorTypes.REQUIRED;
}
}
}
optional: true
},
"createdAt": {
label: "Created at",
Expand Down Expand Up @@ -155,8 +152,7 @@ export const ProductVariant = new SimpleSchema({
"optionTitle": {
label: "Option",
type: String,
optional: true,
defaultValue: "Untitled Option"
optional: true
},
"originCountry": {
type: String,
Expand All @@ -174,7 +170,7 @@ export const ProductVariant = new SimpleSchema({
"title": {
label: "Label",
type: String,
defaultValue: ""
optional: true
},
"type": {
label: "Type",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,16 @@ export default async function addCartItems(context, currentItems, inputItems, op
// not ordered unless back-ordering is enabled.

// Until we do a more complete attributes revamp, we'll do our best to fudge attributes here.
// The main issue is we do not have labels.
const attributes = [];
if (parentVariant) {
attributes.push({
label: null, // Set label to null for now. We expect to use it in the future.
value: parentVariant.title
label: parentVariant.attributeLabel,
value: parentVariant.optionTitle
});
}
attributes.push({
label: null, // Set label to null for now. We expect to use it in the future.
value: chosenVariant.title
label: chosenVariant.attributeLabel,
value: chosenVariant.optionTitle
});

const cartItem = {
Expand Down
7 changes: 0 additions & 7 deletions imports/plugins/core/catalog/server/methods/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,12 +409,6 @@ Meteor.methods({
};

const isOption = ancestors.length > 1;
if (isOption) {
Object.assign(newVariant, {
optionTitle: "Untitled",
title: `${parent.title} - Untitled`
});
}

createProduct(newVariant, { product, parentVariant, isOption });

Expand Down Expand Up @@ -650,7 +644,6 @@ Meteor.methods({
// Create a product variant
createProduct({
ancestors: [newSimpleProduct._id],
title: "",
type: "variant" // needed for multi-schema
}, { product: newSimpleProduct, parentVariant: null, isOption: false });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const productFieldsThatNeedPublishing = [

const variantFieldsThatNeedPublishing = [
"_id",
"attributeLabel",
"barcode",
"compareAtPrice",
"height",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ type CatalogProductVariant implements CatalogProductOrVariant & Node {
"The CatalogProductVariant ID. Do not assume that this is the same as the related variant ID. See `variantId` for that."
_id: ID!

"""
The attribute label describes the category of variant, for example, "Color" or "Size".
In most cases this will be the same for all variants at the same level.
"""
attributeLabel: String!

"The product variant barcode value, if it has one"
barcode: String

Expand All @@ -213,7 +219,7 @@ type CatalogProductVariant implements CatalogProductOrVariant & Node {
"The minimum quantity that must be added to a cart"
minOrderQuantity: Int

"An option title. If this is set and this variant has a CatalogProductVariant parent, treat this variant as an option"
"A short title to use for product detail select lists"
optionTitle: String

"Child variants, if any"
Expand All @@ -231,7 +237,12 @@ type CatalogProductVariant implements CatalogProductOrVariant & Node {
"A stock keeping unit (SKU) identifier for this product"
sku: String

"Product title"
"""
The full variant title for use on cart, checkout, and order summaries and on invoices.
This fully describes the configured variant. For example, if this is an option with
`optionTitle` "Large", its parent variant has `optionTitle` "Red", and the product
`title` is "Fancy T-Shirt", then this `title` will be something like "Fancy T-Shirt - Red - Large".
"""
title: String

"The date and time at which this CatalogProduct was last updated, which is when the related product was most recently published"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export function xformVariant(variant, variantMedia) {

return {
_id: variant._id,
attributeLabel: variant.attributeLabel,
barcode: variant.barcode,
createdAt: variant.createdAt || new Date(),
height: variant.height,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const updatedAt = new Date("2018-04-17T15:34:28.043Z");
const mockVariants = [
{
_id: internalVariantIds[0],
attributeLabel: "attributeLabel",
barcode: "barcode",
createdAt,
height: 0,
Expand Down Expand Up @@ -49,6 +50,7 @@ const mockVariants = [
},
{
_id: internalVariantIds[0],
attributeLabel: "attributeLabel",
barcode: "barcode",
createdAt,
height: 0,
Expand Down
117 changes: 61 additions & 56 deletions imports/plugins/core/core/server/Reaction/absoluteUrl.app-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,83 +6,88 @@ import { sinon } from "meteor/practicalmeteor:sinon";
import { composeUrl } from "/lib/core/url-common";
import core from "./core";

describe("AbsoluteUrlMixin", () => {
let sandbox;
/**
* @return {String} A random string
*/
function randomString() {
return Math.random().toString(36);
}

beforeEach(() => {
sandbox = sinon.sandbox.create();
});

afterEach(() => {
sandbox.restore();
});

describe("#absoluteUrl", () => {
let path;
let connectionHost;
let rootUrl;
let meteorRootUrl;
core.onAppStartupComplete(() => {
describe("AbsoluteUrlMixin", () => {
let sandbox;

beforeEach(() => {
// commonly used test vars
path = randomString();
connectionHost = `${randomString()}.reactioncommerce.com`;
rootUrl = `https://${randomString()}.reactioncommerce.com`;

// mocking Meteor
meteorRootUrl = composeUrl.defaultOptions.rootUrl;
// this is a round-about way of mocking $ROOT_URL
composeUrl.defaultOptions.rootUrl = rootUrl;
sandbox = sinon.sandbox.create();
});

afterEach(() => {
// restore Meteor
composeUrl.defaultOptions.rootUrl = meteorRootUrl;
sandbox.restore();
});

describe("outside of a connection", () => {
it("defaults to $ROOT_URL", () => {
expect(core.absoluteUrl()).to.include(rootUrl);
});
});
describe("#absoluteUrl", () => {
let path;
let connectionHost;
let rootUrl;
let meteorRootUrl;

describe("within a connection", () => {
beforeEach(() => {
// for reference: https://github.com/meteor/meteor/blob/ed98a07125cd072552482ca2244239f034857814/packages/ddp-server/livedata_server.js#L279-L295
sinon.stub(DDP._CurrentMethodInvocation, "get").returns({
connection: { httpHeaders: { host: connectionHost } }
});
// commonly used test vars
path = randomString();
connectionHost = `${randomString()}.reactioncommerce.com`;
rootUrl = `https://${randomString()}.reactioncommerce.com`;

// mocking Meteor
meteorRootUrl = composeUrl.defaultOptions.rootUrl;
// this is a round-about way of mocking $ROOT_URL
composeUrl.defaultOptions.rootUrl = rootUrl;
});

afterEach(() => {
DDP._CurrentMethodInvocation.get.restore();
// restore Meteor
composeUrl.defaultOptions.rootUrl = meteorRootUrl;
});

it("uses the current connection's host", () => {
expect(core.absoluteUrl()).to.include(connectionHost);
describe("outside of a connection", () => {
it("defaults to $ROOT_URL", () => {
expect(core.absoluteUrl()).to.include(rootUrl);
});
});

it("uses $ROOT_URL's protocol/scheme", () => {
// this would he http:// if $ROOT_URL had not used https://
expect(core.absoluteUrl()).to.startsWith("https://");
describe("within a connection", () => {
beforeEach(() => {
// for reference: https://github.com/meteor/meteor/blob/ed98a07125cd072552482ca2244239f034857814/packages/ddp-server/livedata_server.js#L279-L295
sinon.stub(DDP._CurrentMethodInvocation, "get").returns({
connection: { httpHeaders: { host: connectionHost } }
});
});

afterEach(() => {
DDP._CurrentMethodInvocation.get.restore();
});

it("uses the current connection's host", () => {
expect(core.absoluteUrl()).to.include(connectionHost);
});

it("uses $ROOT_URL's protocol/scheme", () => {
// this would he http:// if $ROOT_URL had not used https://
expect(core.absoluteUrl()).to.startsWith("https://");
});
});
});

it("accepts options the same way composeUrl does", () => {
const options = {
secure: true,
replaceLocalhost: true,
rootUrl: "http://127.0.0.1"
};
it("accepts options the same way composeUrl does", () => {
willopez marked this conversation as resolved.
Show resolved Hide resolved
const options = {
secure: true,
replaceLocalhost: true,
rootUrl: "http://127.0.0.1"
};

const reactionVersion = core.absoluteUrl(path, options);
const meteorVersion = composeUrl(path, options);
const reactionVersion = core.absoluteUrl(path, options);
const meteorVersion = composeUrl(path, options);

expect(reactionVersion).to.equal(meteorVersion);
expect(reactionVersion).to.equal(meteorVersion);
});
});
});

function randomString() {
return Math.random().toString(36);
}
});
Loading