fix: merge api

This commit is contained in:
Sebastian Rindom
2021-03-15 08:28:53 +01:00
35 changed files with 5117 additions and 2358 deletions
+5 -5
View File
@@ -17,13 +17,13 @@
"author": "Sebastian Rindom",
"license": "MIT",
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/cli": "^7.13.0",
"@babel/core": "^7.13.8",
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.9.6",
"@babel/preset-env": "^7.13.9",
"@babel/runtime": "^7.13.9",
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
"jest": "^25.5.2",
File diff suppressed because it is too large Load Diff
@@ -8,6 +8,9 @@ class SegmentService extends BaseService {
* e.g.
* {
* write_key: Segment write key given in Segment dashboard
* use_ga_id: If set to true the plugin will look for a ga_id in the cart
* context if present this id will be used as the Google Analytics
* client id.
* }
*/
constructor({ totalsService, productService }, options) {
@@ -3,15 +3,19 @@ class OrderSubscriber {
segmentService,
eventBusService,
orderService,
cartService,
claimService,
returnService,
fulfillmentService,
}) {
this.orderService_ = orderService
this.cartService_ = cartService
this.returnService_ = returnService
this.claimService_ = claimService
this.fulfillmentService_ = fulfillmentService
eventBusService.subscribe(
@@ -239,12 +243,46 @@ class OrderSubscriber {
],
})
const eventContext = {}
const integrations = {}
if (order.cart_id) {
try {
const cart = await this.cartService_.retrieve(order.cart_id, {
select: ["context"],
})
if (cart.context) {
if (cart.context.ip) {
eventContext.ip = cart.context.ip
}
if (cart.context.user_agent) {
eventContext.user_agent = cart.context.user_agent
}
if (segmentService.options_ && segmentService.options_.use_ga_id) {
if (cart.context.ga_id) {
integrations["Google Analytics"] = {
clientId: cart.context.ga_id,
}
}
}
}
} catch (err) {
console.log(err)
console.warn("Failed to gather context for order")
}
}
const orderData = await segmentService.buildOrder(order)
const orderEvent = {
event: "Order Completed",
userId: order.customer_id,
properties: orderData,
timestamp: order.created_at,
context: eventContext,
integrations,
}
segmentService.track(orderEvent)
+1 -2
View File
@@ -44,7 +44,7 @@
},
"peerDependencies": {
"medusa-interfaces": "1.x",
"mongoose": "5.x"
"typeorm": "0.2.x"
},
"dependencies": {
"@babel/plugin-transform-classes": "^7.9.5",
@@ -82,7 +82,6 @@
"request-ip": "^2.1.3",
"resolve-cwd": "^3.0.0",
"scrypt-kdf": "^2.0.1",
"typeorm": "^0.2.29",
"ulid": "^2.3.0",
"uuid": "^8.3.1",
"winston": "^3.2.1"
@@ -11,6 +11,9 @@ describe("POST /store/carts", () => {
subject = await request("POST", `/store/carts`, {
payload: {
region_id: IdMap.getId("testRegion"),
context: {
clientId: "test",
},
},
})
})
@@ -23,6 +26,11 @@ describe("POST /store/carts", () => {
expect(CartServiceMock.create).toHaveBeenCalledTimes(1)
expect(CartServiceMock.create).toHaveBeenCalledWith({
region_id: IdMap.getId("testRegion"),
context: {
ip: "::ffff:127.0.0.1",
user_agent: "node-superagent/3.8.3",
clientId: "test",
},
})
})
@@ -1,3 +1,4 @@
import reqIp from "request-ip"
import { Validator, MedusaError } from "medusa-core-utils"
import { defaultFields, defaultRelations } from "./"
@@ -31,6 +32,9 @@ import { defaultFields, defaultRelations } from "./"
* quantity:
* description: The quantity of the Product Variant to add
* type: integer
* context:
* description: "An optional object to provide context to the Cart. The `context` field is automatically populated with `ip` and `user_agent`"
* type: object
* tags:
* - Cart
* responses:
@@ -53,6 +57,7 @@ export default async (req, res) => {
quantity: Validator.number().required(),
})
.optional(),
context: Validator.object().optional(),
})
const { value, error } = schema.validate(req.body)
@@ -60,6 +65,11 @@ export default async (req, res) => {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
const reqContext = {
ip: reqIp.getClientIp(req),
user_agent: req.get("user-agent"),
}
try {
const lineItemService = req.scope.resolve("lineItemService")
const cartService = req.scope.resolve("cartService")
@@ -77,6 +87,10 @@ export default async (req, res) => {
const toCreate = {
region_id: regionId,
context: {
...reqContext,
...value.context,
},
}
if (req.user && req.user.customer_id) {
@@ -51,6 +51,9 @@ import { defaultFields, defaultRelations } from "./"
* customer_id:
* description: "The id of the Customer to associate the Cart with."
* type: string
* context:
* description: "An optional object to provide context to the Cart."
* type: object
* tags:
* - Cart
* responses:
@@ -85,6 +88,7 @@ export default async (req, res) => {
})
.optional(),
customer_id: Validator.string().optional(),
context: Validator.object().optional(),
})
const { value, error } = schema.validate(req.body)
@@ -4,6 +4,7 @@
* summary: Retrieve Shipping Options
* description: "Retrieves a list of Shipping Options."
* parameters:
* - (query) is_return {boolean} Whether return Shipping Options should be included. By default all Shipping Options are returned.
* - (query) product_ids {string} A comma separated list of Product ids to filter Shipping Options by.
* - (query) region_id {string} the Region to retrieve Shipping Options from.
* tags:
@@ -31,6 +32,10 @@ export default async (req, res) => {
const query = {}
if ("is_return" in req.query) {
query.is_return = req.query.is_return === "true"
}
if (regionId) {
query.region_id = regionId
}
@@ -0,0 +1,13 @@
import { MigrationInterface, QueryRunner } from "typeorm"
export class cartContext1614684597235 implements MigrationInterface {
name = "cartContext1614684597235"
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "cart" ADD "context" jsonb`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "cart" DROP COLUMN "context"`)
}
}
+3
View File
@@ -243,6 +243,9 @@ export class Cart {
@Column({ nullable: true })
idempotency_key: string
@Column({ type: "jsonb", nullable: true })
context: any
// Total fields
shipping_total: number
discount_total: number
+1 -1
View File
@@ -46,7 +46,7 @@ export class Product {
@Column({ default: false })
is_giftcard: boolean
@ManyToMany(() => Image)
@ManyToMany(() => Image, { cascade: ["insert"] })
@JoinTable({
name: "product_images",
joinColumn: {
@@ -0,0 +1,5 @@
import { EntityRepository, Repository } from "typeorm"
import { Image } from "../models/image"
@EntityRepository(Image)
export class ImageRepository extends Repository<Image> {}
+8
View File
@@ -641,6 +641,14 @@ class CartService extends BaseService {
}
}
if ("context" in update) {
const prevContext = cart.context || {}
cart.context = {
...prevContext,
...update.context,
}
}
const result = await cartRepo.save(cart)
if ("email" in update || "customer_id" in update) {
+42 -3
View File
@@ -23,6 +23,7 @@ class ProductService extends BaseService {
productCollectionService,
productTypeRepository,
productTagRepository,
imageRepository,
}) {
super()
@@ -52,6 +53,9 @@ class ProductService extends BaseService {
/** @private @const {ProductCollectionService} */
this.productTagRepository_ = productTagRepository
/** @private @const {ImageRepository} */
this.imageRepository_ = imageRepository
}
withTransaction(transactionManager) {
@@ -69,6 +73,7 @@ class ProductService extends BaseService {
productCollectionService: this.productCollectionService_,
productTagRepository: this.productTagRepository_,
productTypeRepository: this.productTypeRepository_,
imageRepository: this.imageRepository_,
})
cloned.transactionManager_ = transactionManager
@@ -233,7 +238,7 @@ class ProductService extends BaseService {
})
if (existing) {
return existing
return existing.id
}
const created = productTypeRepository.create(type)
@@ -277,10 +282,18 @@ class ProductService extends BaseService {
this.productOptionRepository_
)
const { options, tags, type, ...rest } = productObject
const { options, tags, type, images, ...rest } = productObject
if (!rest.thumbnail && images && images.length) {
rest.thumbnail = images[0]
}
let product = productRepo.create(rest)
if (images && images.length) {
product.images = await this.upsertImages_(images)
}
if (tags) {
product.tags = await this.upsertProductTags_(tags)
}
@@ -310,6 +323,28 @@ class ProductService extends BaseService {
})
}
async upsertImages_(images) {
const imageRepository = this.manager_.getCustomRepository(
this.imageRepository_
)
let productImages = []
for (const img of images) {
const existing = await imageRepository.findOne({
where: { url: img },
})
if (existing) {
productImages.push(existing)
} else {
const created = imageRepository.create({ url: img })
productImages.push(created)
}
}
return productImages
}
/**
* Updates a product. Product variant updates should use dedicated methods,
* e.g. `addVariant`, etc. The function will throw errors if metadata or
@@ -327,7 +362,7 @@ class ProductService extends BaseService {
)
const product = await this.retrieve(productId, {
relations: ["variants", "tags"],
relations: ["variants", "tags", "images"],
})
const {
@@ -344,6 +379,10 @@ class ProductService extends BaseService {
product.thumbnail = images[0]
}
if (images && images.length) {
product.images = await this.upsertImages_(images)
}
if (metadata) {
product.metadata = this.setMetadata_(product, metadata)
}