@mercurjs/docs 2.3.8-canary.2 → 2.3.8-canary.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/learn/architecture.mdx +166 -263
- package/content/platform/attribute/concepts/attribute-types.mdx +73 -46
- package/content/platform/attribute/concepts/global-vs-inline.mdx +54 -32
- package/content/platform/attribute/concepts/variant-axes.mdx +54 -32
- package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +96 -53
- package/content/platform/attribute/guides/create-a-variant-axis.mdx +106 -57
- package/content/platform/attribute/guides/create-an-attribute.mdx +105 -56
- package/content/platform/attribute/overview.mdx +37 -33
- package/content/platform/attribute/reference/data-models.mdx +53 -38
- package/content/platform/attribute/reference/events.mdx +47 -24
- package/content/platform/attribute/reference/links.mdx +98 -27
- package/content/platform/attribute/reference/service.mdx +71 -32
- package/content/platform/attribute/reference/workflows.mdx +158 -28
- package/content/platform/catalog/concepts/master-products.mdx +39 -37
- package/content/platform/catalog/concepts/product-seller-allowlist.mdx +56 -35
- package/content/platform/catalog/concepts/status-lifecycle.mdx +55 -49
- package/content/platform/catalog/concepts/variants-categories-collections.mdx +55 -35
- package/content/platform/catalog/guides/allowlist-stores.mdx +100 -37
- package/content/platform/catalog/guides/create-a-master-product.mdx +124 -48
- package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +120 -55
- package/content/platform/catalog/overview.mdx +39 -35
- package/content/platform/catalog/reference/data-models.mdx +154 -43
- package/content/platform/catalog/reference/events.mdx +41 -25
- package/content/platform/catalog/reference/links.mdx +196 -28
- package/content/platform/catalog/reference/service.mdx +57 -35
- package/content/platform/catalog/reference/workflows.mdx +124 -30
- package/content/platform/commission/concepts/order-commission-lines.mdx +54 -52
- package/content/platform/commission/concepts/rule-matching.mdx +53 -63
- package/content/platform/commission/concepts/rules-and-rates.mdx +61 -59
- package/content/platform/commission/guides/batch-update-rules.mdx +69 -41
- package/content/platform/commission/guides/create-a-rate.mdx +101 -53
- package/content/platform/commission/guides/refresh-order-commission-lines.mdx +104 -35
- package/content/platform/commission/overview.mdx +43 -35
- package/content/platform/commission/reference/data-models.mdx +59 -45
- package/content/platform/commission/reference/events.mdx +28 -34
- package/content/platform/commission/reference/links.mdx +42 -33
- package/content/platform/commission/reference/service.mdx +103 -28
- package/content/platform/commission/reference/workflows.mdx +101 -27
- package/content/platform/offer/concepts/pricing-and-inventory.mdx +63 -49
- package/content/platform/offer/concepts/shipping.mdx +28 -29
- package/content/platform/offer/concepts/what-is-an-offer.mdx +48 -45
- package/content/platform/offer/guides/bulk-create-offers.mdx +97 -61
- package/content/platform/offer/guides/create-an-offer.mdx +112 -60
- package/content/platform/offer/guides/manage-offer-inventory.mdx +78 -38
- package/content/platform/offer/overview.mdx +39 -32
- package/content/platform/offer/reference/data-models.mdx +77 -47
- package/content/platform/offer/reference/events.mdx +44 -18
- package/content/platform/offer/reference/links.mdx +92 -40
- package/content/platform/offer/reference/service.mdx +66 -31
- package/content/platform/offer/reference/workflows.mdx +146 -37
- package/content/platform/order-group/concepts/computed-totals.mdx +40 -37
- package/content/platform/order-group/concepts/order-splitting.mdx +41 -39
- package/content/platform/order-group/concepts/the-order-group.mdx +39 -32
- package/content/platform/order-group/guides/list-order-groups.mdx +96 -56
- package/content/platform/order-group/guides/retrieve-an-order-group.mdx +93 -42
- package/content/platform/order-group/guides/split-a-cart.mdx +115 -40
- package/content/platform/order-group/overview.mdx +42 -32
- package/content/platform/order-group/reference/data-models.mdx +48 -31
- package/content/platform/order-group/reference/events.mdx +29 -17
- package/content/platform/order-group/reference/links.mdx +90 -29
- package/content/platform/order-group/reference/service.mdx +55 -31
- package/content/platform/order-group/reference/workflows.mdx +83 -24
- package/content/platform/payout/concepts/account-lifecycle.mdx +64 -67
- package/content/platform/payout/concepts/accounts-and-onboarding.mdx +50 -40
- package/content/platform/payout/concepts/payout-pipeline.mdx +70 -73
- package/content/platform/payout/guides/create-a-payout-account.mdx +65 -41
- package/content/platform/payout/guides/process-a-provider-webhook.mdx +84 -38
- package/content/platform/payout/guides/start-provider-onboarding.mdx +90 -46
- package/content/platform/payout/overview.mdx +43 -34
- package/content/platform/payout/reference/data-models.mdx +63 -36
- package/content/platform/payout/reference/events.mdx +56 -30
- package/content/platform/payout/reference/links.mdx +52 -22
- package/content/platform/payout/reference/service.mdx +72 -33
- package/content/platform/payout/reference/workflows.mdx +80 -19
- package/content/platform/product-edit/concepts/change-actions.mdx +52 -45
- package/content/platform/product-edit/concepts/change-pipeline.mdx +60 -52
- package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +43 -46
- package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +104 -56
- package/content/platform/product-edit/guides/edit-a-product.mdx +126 -46
- package/content/platform/product-edit/guides/request-a-revision.mdx +99 -39
- package/content/platform/product-edit/overview.mdx +41 -34
- package/content/platform/product-edit/reference/data-models.mdx +73 -47
- package/content/platform/product-edit/reference/events.mdx +39 -29
- package/content/platform/product-edit/reference/links.mdx +32 -24
- package/content/platform/product-edit/reference/service.mdx +45 -26
- package/content/platform/product-edit/reference/workflows.mdx +173 -31
- package/content/platform/review/concepts/product-vs-seller-reviews.mdx +42 -41
- package/content/platform/review/concepts/ratings-and-moderation.mdx +45 -44
- package/content/platform/review/concepts/the-review-model.mdx +52 -45
- package/content/platform/review/guides/compute-aggregate-ratings.mdx +62 -37
- package/content/platform/review/guides/create-a-review.mdx +109 -39
- package/content/platform/review/guides/moderate-a-review.mdx +83 -37
- package/content/platform/review/guides/respond-to-a-review.mdx +85 -40
- package/content/platform/review/overview.mdx +43 -33
- package/content/platform/review/reference/data-models.mdx +41 -27
- package/content/platform/review/reference/events.mdx +55 -37
- package/content/platform/review/reference/links.mdx +80 -22
- package/content/platform/review/reference/service.mdx +61 -33
- package/content/platform/review/reference/workflows.mdx +80 -24
- package/content/platform/store/concepts/lifecycle.mdx +68 -44
- package/content/platform/store/concepts/store-entity.mdx +45 -27
- package/content/platform/store/concepts/team.mdx +56 -35
- package/content/platform/store/guides/create-a-store.mdx +102 -35
- package/content/platform/store/guides/manage-the-team.mdx +122 -32
- package/content/platform/store/guides/moderate-a-store.mdx +107 -38
- package/content/platform/store/overview.mdx +41 -34
- package/content/platform/store/reference/data-models.mdx +132 -57
- package/content/platform/store/reference/events.mdx +42 -17
- package/content/platform/store/reference/links.mdx +67 -49
- package/content/platform/store/reference/service.mdx +105 -33
- package/content/platform/store/reference/workflows.mdx +304 -24
- package/content/references/panel-extensions/layouts.mdx +169 -0
- package/llms.txt +112 -111
- package/package.json +1 -1
|
@@ -1,87 +1,97 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Reviews"
|
|
3
3
|
sidebarTitle: "Overview"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Let customers rate the products and stores they bought from, then moderate, answer, and average those ratings."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Use Mercur to let customers rate the products they bought and the stores
|
|
8
|
-
bought from.
|
|
7
|
+
Use Mercur to let customers rate the products they bought and the stores that sold them.
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
of it is exposed directly through the Admin, Vendor, and Store APIs.
|
|
9
|
+
A review is one customer’s rating of one target: a master product or a store. Each review is tied to the order that earned it, starts as `pending`, and waits for an operator to publish or reject it. The Review module ships inside `@mercurjs/core`, so it’s registered with the rest of the marketplace modules and needs no extra install. It connects to products, stores, orders, and customers through module links.
|
|
10
|
+
|
|
11
|
+
<img className="block dark:hidden" src="/images/figures/review-overview-light.svg" alt="Behind, createReviewWorkflow, which writes every review. In front, the review table: order #1001 backs a published seller review of ACME and a pending product review of the linen shirt, beside a rejected review from order #998." />
|
|
12
|
+
<img className="hidden dark:block" src="/images/figures/review-overview-dark.svg" alt="Behind, createReviewWorkflow, which writes every review. In front, the review table: order #1001 backs a published seller review of ACME and a pending product review of the linen shirt, beside a rejected review from order #998." />
|
|
15
13
|
|
|
16
14
|
<Note>
|
|
17
|
-
|
|
18
|
-
both product and seller reviews. A `reference` discriminator decides which one
|
|
19
|
-
a given review is about. There is no separate product-review or seller-review
|
|
20
|
-
table.
|
|
15
|
+
The panels call the target a store. The code calls the same record a `Seller`, so a store review has `reference: "seller"`. Reviews use the id prefix `rev` and resolve with `MercurModules.REVIEW`.
|
|
21
16
|
</Note>
|
|
22
17
|
|
|
23
18
|
## Key features
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
20
|
+
The Review module covers a review from submission to the number on a storefront:
|
|
21
|
+
|
|
22
|
+
- **One model, two targets**: a single `Review` record rates a product or a store, chosen by its `reference` field
|
|
23
|
+
- **Order-backed**: the order must belong to the customer, and each order can review a given target once
|
|
24
|
+
- **Moderation**: reviews start `pending`, and an operator sets them to `published` or `rejected`
|
|
25
|
+
- **Store responses**: a store answers a review once through `seller_note`
|
|
26
|
+
- **Rating helpers**: service methods compute average ratings per product or per store at query time
|
|
27
|
+
- **Compensation**: every review workflow rolls its write back if a later step fails
|
|
28
|
+
|
|
29
|
+
## How it works
|
|
30
|
+
|
|
31
|
+
A review moves through four stages:
|
|
32
|
+
|
|
33
|
+
1. A customer calls `POST /store/reviews` with an order they own, which runs `createReviewWorkflow`
|
|
34
|
+
2. The workflow validates the order, creates the review as `pending`, and links it to its customer, order, and target
|
|
35
|
+
3. An operator publishes or rejects it with `updateReviewWorkflow`, and the store answers it with `respondReviewWorkflow`
|
|
36
|
+
4. Your own storefront route reads published reviews and averages them with the service’s rating helpers
|
|
37
|
+
|
|
38
|
+
<Warning>
|
|
39
|
+
Core ships no public endpoint that lists a product’s or a store’s published reviews, and no route returns an average rating. `GET /store/reviews` returns only the signed-in customer’s own reviews. Add a route for storefront display, as shown in [Compute aggregate ratings](/platform/review/guides/compute-aggregate-ratings).
|
|
40
|
+
</Warning>
|
|
31
41
|
|
|
32
42
|
## Get started
|
|
33
43
|
|
|
34
|
-
Learn how
|
|
44
|
+
Learn how reviews, their targets, and their statuses fit together.
|
|
35
45
|
|
|
36
46
|
<CardGroup cols={2}>
|
|
37
47
|
<Card title="The review model" icon="star" href="/platform/review/concepts/the-review-model">
|
|
38
|
-
The
|
|
48
|
+
The fields of a review: rating, notes, status, and display id.
|
|
39
49
|
</Card>
|
|
40
50
|
<Card title="Product vs seller reviews" icon="tags" href="/platform/review/concepts/product-vs-seller-reviews">
|
|
41
|
-
|
|
51
|
+
How `reference` picks the target and which links anchor each review.
|
|
42
52
|
</Card>
|
|
43
53
|
<Card title="Ratings & moderation" icon="gauge" href="/platform/review/concepts/ratings-and-moderation">
|
|
44
|
-
|
|
54
|
+
Status changes, store responses, and how averages are computed.
|
|
45
55
|
</Card>
|
|
46
56
|
</CardGroup>
|
|
47
57
|
|
|
48
58
|
## Examples
|
|
49
59
|
|
|
50
|
-
Build against
|
|
60
|
+
Build against reviews in your own server code.
|
|
51
61
|
|
|
52
62
|
<CardGroup cols={2}>
|
|
53
63
|
<Card title="Create a review" icon="plus" href="/platform/review/guides/create-a-review">
|
|
54
|
-
Run `createReviewWorkflow` from a route
|
|
64
|
+
Run `createReviewWorkflow` from a custom route.
|
|
55
65
|
</Card>
|
|
56
66
|
<Card title="Moderate a review" icon="gavel" href="/platform/review/guides/moderate-a-review">
|
|
57
67
|
Publish, reject, and delete reviews in code.
|
|
58
68
|
</Card>
|
|
59
69
|
<Card title="Respond to a review" icon="reply" href="/platform/review/guides/respond-to-a-review">
|
|
60
|
-
Attach a store
|
|
70
|
+
Attach a store’s answer with `respondReviewWorkflow`.
|
|
61
71
|
</Card>
|
|
62
72
|
<Card title="Aggregate ratings" icon="calculator" href="/platform/review/guides/compute-aggregate-ratings">
|
|
63
|
-
|
|
73
|
+
Serve average ratings for products and stores.
|
|
64
74
|
</Card>
|
|
65
75
|
</CardGroup>
|
|
66
76
|
|
|
67
77
|
## Resources
|
|
68
78
|
|
|
69
|
-
|
|
79
|
+
The data model, links, workflows, service methods, and events behind reviews.
|
|
70
80
|
|
|
71
81
|
<CardGroup cols={2}>
|
|
72
82
|
<Card title="Data models" icon="table" href="/platform/review/reference/data-models">
|
|
73
|
-
The `Review`
|
|
83
|
+
The `Review` model, its fields, and its indexes.
|
|
74
84
|
</Card>
|
|
75
85
|
<Card title="Links" icon="link" href="/platform/review/reference/links">
|
|
76
|
-
|
|
86
|
+
The four links to products, sellers, orders, and customers.
|
|
77
87
|
</Card>
|
|
78
88
|
<Card title="Workflows" icon="diagram-project" href="/platform/review/reference/workflows">
|
|
79
|
-
Create, update, respond, and delete workflows.
|
|
89
|
+
Create, update, respond, and delete workflows with their steps.
|
|
80
90
|
</Card>
|
|
81
91
|
<Card title="Service" icon="gear" href="/platform/review/reference/service">
|
|
82
|
-
|
|
92
|
+
Generated methods and the rating helpers.
|
|
83
93
|
</Card>
|
|
84
94
|
<Card title="Events" icon="bell" href="/platform/review/reference/events">
|
|
85
|
-
|
|
95
|
+
The mutation events the module emits.
|
|
86
96
|
</Card>
|
|
87
97
|
</CardGroup>
|
|
@@ -1,36 +1,50 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Review data model"
|
|
3
3
|
sidebarTitle: "Data models"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Every field and index of the Review model in the Review module."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The Review
|
|
8
|
-
data model and fields.
|
|
7
|
+
The Review module owns one data model, `Review`. It’s defined in `packages/core/src/modules/review/models/review.ts` and stored in the `review` table. Resolve the module with `MercurModules.REVIEW`.
|
|
9
8
|
|
|
10
9
|
## Review
|
|
11
10
|
|
|
12
|
-
Table `review`, id prefix `rev`.
|
|
13
|
-
seller.
|
|
11
|
+
A customer’s rating of one product or one seller. Table `review`, id prefix `rev`.
|
|
14
12
|
|
|
15
|
-
| Field | Type |
|
|
13
|
+
| Field | Type | Description |
|
|
16
14
|
| --- | --- | --- |
|
|
17
|
-
| `id` | text | Primary key |
|
|
18
|
-
| `display_id` |
|
|
19
|
-
| `reference` | enum | `product` or `seller
|
|
20
|
-
| `rating` |
|
|
21
|
-
| `customer_note` | text |
|
|
22
|
-
| `seller_note` | text |
|
|
23
|
-
| `status` | enum | `pending`, `published`, or `rejected
|
|
24
|
-
| `created_at` |
|
|
25
|
-
| `updated_at` |
|
|
26
|
-
| `deleted_at` |
|
|
27
|
-
|
|
28
|
-
The
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
15
|
+
| `id` | `text` | Primary key with the prefix `rev`, such as `rev_01JD8Q1234567` |
|
|
16
|
+
| `display_id` | `autoincrement` | Serial number for display, stored as a Postgres `serial` |
|
|
17
|
+
| `reference` | `enum` | The target type: `product` or `seller`. Required |
|
|
18
|
+
| `rating` | `number` | The score, stored as an `integer`. Required, no range constraint in the model |
|
|
19
|
+
| `customer_note` | `text` | The customer’s text. Nullable, searchable |
|
|
20
|
+
| `seller_note` | `text` | The store’s answer. Nullable, searchable |
|
|
21
|
+
| `status` | `enum` | `pending`, `published`, or `rejected`. Default `pending` |
|
|
22
|
+
| `created_at` | `timestamptz` | Set on creation |
|
|
23
|
+
| `updated_at` | `timestamptz` | Set on every change |
|
|
24
|
+
| `deleted_at` | `timestamptz` | Nullable. Set by a soft delete |
|
|
25
|
+
|
|
26
|
+
The model has no column for its target, order, or customer. Those live in [module links](/platform/review/reference/links), and `reference` tells you whether to follow `product` or `seller`.
|
|
27
|
+
|
|
28
|
+
### Indexes
|
|
29
|
+
|
|
30
|
+
Two indexes speed up the lists the APIs run:
|
|
31
|
+
|
|
32
|
+
| Index | Columns | Condition |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| `IDX_review_deleted_at` | `deleted_at` | `deleted_at IS NULL` |
|
|
35
|
+
| `IDX_review_reference_status_created_at` | `reference`, `status`, `created_at` | `deleted_at IS NULL` |
|
|
36
|
+
|
|
37
|
+
### Relations
|
|
38
|
+
|
|
39
|
+
The model defines no relations of its own. Through links, a review exposes these `query.graph` fields:
|
|
40
|
+
|
|
41
|
+
| Field | Target | Set when |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| `product` | Product | `reference` is `product` |
|
|
44
|
+
| `seller` | Seller | `reference` is `seller` |
|
|
45
|
+
| `order` | Order | Always |
|
|
46
|
+
| `customer` | Customer | Always |
|
|
47
|
+
|
|
48
|
+
### Types
|
|
49
|
+
|
|
50
|
+
`@mercurjs/types` exports `ReviewDTO`, `ReviewReference` (`"product" | "seller"`), and `ReviewStatus` (`"pending" | "published" | "rejected"`) for this model.
|
|
@@ -1,61 +1,79 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Review events"
|
|
3
3
|
sidebarTitle: "Events"
|
|
4
|
-
description: "
|
|
4
|
+
description: "The mutation events the Review module service emits, and how to subscribe to them."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The Review
|
|
8
|
-
do **not** currently emit their own domain events. There is no `review.created`
|
|
9
|
-
or `review.published` event to subscribe to today.
|
|
7
|
+
Mercur defines no review events of its own, and the review workflows don’t call `emitEventStep`. The Review module service emits Medusa’s standard mutation events instead, because every method `MedusaService` generates publishes an event when it changes a `Review` row. Subscribe to these to notify a store, refresh a search index, or recompute a cached rating.
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
route or workflow and run the follow-up logic there, or emit your own event and
|
|
13
|
-
subscribe to it.
|
|
9
|
+
## Event list
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
Event names follow Medusa’s `<module>.<model>.<action>` pattern, with the module and the model both named `review`:
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
| Event | Emitted when | Payload |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `review.review.created` | `createReviewWorkflow` or `createReviews` creates a review | `{ id }` |
|
|
16
|
+
| `review.review.updated` | `updateReviewWorkflow`, `respondReviewWorkflow`, or `updateReviews` changes a review | `{ id }` |
|
|
17
|
+
| `review.review.deleted` | `deleteReviewWorkflow` or `softDeleteReviews` soft deletes a review, or `deleteReviews` removes one | `{ id }` |
|
|
18
|
+
| `review.review.restored` | `restoreReviews` restores a soft deleted review, including a delete rollback | `{ id }` |
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
import { Modules } from "@medusajs/framework/utils"
|
|
21
|
-
import { createReviewWorkflow } from "@mercurjs/core/workflows"
|
|
20
|
+
The payload holds only the review id. Load the review in the subscriber when you need its fields or links.
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
})
|
|
22
|
+
<Note>
|
|
23
|
+
`review.review.updated` doesn’t say which field changed. A moderation, a customer edit, and a store answer all emit the same event, so compare against your own state when you need to tell them apart.
|
|
24
|
+
</Note>
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
await eventBus.emit({ name: "review.created", data: { id: result.id } })
|
|
26
|
+
## Subscribe to an event
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
```
|
|
28
|
+
Create a subscriber that loads the review and its links:
|
|
34
29
|
|
|
35
30
|
```ts title="src/subscribers/review-created.ts"
|
|
36
31
|
import type { SubscriberArgs, SubscriberConfig } from "@medusajs/framework"
|
|
32
|
+
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
|
37
33
|
|
|
38
|
-
export default async function
|
|
34
|
+
export default async function reviewCreated({
|
|
39
35
|
event,
|
|
40
36
|
container,
|
|
41
37
|
}: SubscriberArgs<{ id: string }>) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
|
39
|
+
const { data: [review] } = await query.graph({
|
|
40
|
+
entity: "review",
|
|
41
|
+
fields: ["rating", "reference", "seller.email", "product.title"],
|
|
42
|
+
filters: { id: event.data.id },
|
|
43
|
+
})
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
event
|
|
45
|
+
const logger = container.resolve(ContainerRegistrationKeys.LOGGER)
|
|
46
|
+
logger.info(`Review ${event.data.id} rated ${review.rating}`)
|
|
48
47
|
}
|
|
48
|
+
|
|
49
|
+
export const config: SubscriberConfig = { event: "review.review.created" }
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
This logs each new review. Replace the log with your side effect, such as a notification to `review.seller.email` for a store review.
|
|
53
|
+
|
|
54
|
+
## Emit a named event
|
|
55
|
+
|
|
56
|
+
To give a moderation step its own event, wrap `updateReviewWorkflow` in your own workflow and add Medusa’s `emitEventStep`:
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
```ts title="src/workflows/publish-review.ts"
|
|
59
|
+
import {
|
|
60
|
+
createWorkflow,
|
|
61
|
+
WorkflowResponse,
|
|
62
|
+
} from "@medusajs/framework/workflows-sdk"
|
|
63
|
+
import { emitEventStep } from "@medusajs/medusa/core-flows"
|
|
64
|
+
import { updateReviewWorkflow } from "@mercurjs/core/workflows"
|
|
65
|
+
|
|
66
|
+
export const publishReviewWorkflow = createWorkflow(
|
|
67
|
+
"publish-review",
|
|
68
|
+
(input: { id: string }) => {
|
|
69
|
+
const review = updateReviewWorkflow.runAsStep({
|
|
70
|
+
input: { id: input.id, status: "published" },
|
|
71
|
+
})
|
|
72
|
+
emitEventStep({ eventName: "review.published", data: { id: input.id } })
|
|
73
|
+
|
|
74
|
+
return new WorkflowResponse(review)
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
```
|
|
58
78
|
|
|
59
|
-
|
|
60
|
-
to your own workflow that wraps the review workflow, rather than relying on an
|
|
61
|
-
event. Events are handled asynchronously and outside the workflow's compensation.
|
|
79
|
+
`review.published` is a name you define, so subscribe to the same string.
|
|
@@ -1,43 +1,101 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Review links"
|
|
3
3
|
sidebarTitle: "Links"
|
|
4
|
-
description: "
|
|
4
|
+
description: "The four module links that connect reviews to products, sellers, orders, and customers."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
**module links**. A review carries no foreign keys to its target on the model
|
|
9
|
-
itself. Instead, four links anchor each review to the rest of the marketplace.
|
|
10
|
-
Once a link is defined, you retrieve related records with `query.graph` using the
|
|
11
|
-
link alias.
|
|
7
|
+
A review stores no foreign keys. Four module links in `packages/core/src/links/` connect it to the records around it, and `query.graph` follows them by alias. Every link is a list link on the other module’s side: one product, seller, order, or customer has many reviews, and each review has one of each it’s linked to.
|
|
12
8
|
|
|
13
9
|
```ts
|
|
14
10
|
const { data: reviews } = await query.graph({
|
|
15
11
|
entity: "review",
|
|
16
|
-
fields: ["id", "rating", "product
|
|
12
|
+
fields: ["id", "rating", "product.title", "seller.name", "order.id"],
|
|
13
|
+
filters: { status: "published" },
|
|
17
14
|
})
|
|
18
15
|
```
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
This loads published reviews with their target and order. Only one of `product` and `seller` is set on each review.
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
## Product and review
|
|
20
|
+
|
|
21
|
+
Connects a master product to its product reviews. Written by `createReviewWorkflow` through the `create-remote-links` step when `reference` is `product`.
|
|
22
|
+
|
|
23
|
+
| Property | Value |
|
|
23
24
|
| --- | --- |
|
|
24
|
-
|
|
|
25
|
-
|
|
|
25
|
+
| File | `product-review.ts` |
|
|
26
|
+
| Sides | `Modules.PRODUCT` `product` and `MercurModules.REVIEW` `review` |
|
|
27
|
+
| Cardinality | One product, many reviews |
|
|
28
|
+
| Link table | `product_product_review_review` |
|
|
29
|
+
| Aliases | `review.product`, `product.reviews` |
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
```ts
|
|
32
|
+
const { data: [product] } = await query.graph({
|
|
33
|
+
entity: "product",
|
|
34
|
+
fields: ["title", "reviews.rating", "reviews.status"],
|
|
35
|
+
filters: { id: "prod_01JD8Q1234567" },
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Seller and review
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
Connects a seller to its store reviews. Written by `createReviewWorkflow` through the `create-remote-links` step when `reference` is `seller`. The Vendor API scopes reviews to a store through this link.
|
|
31
42
|
|
|
32
|
-
|
|
|
43
|
+
| Property | Value |
|
|
33
44
|
| --- | --- |
|
|
34
|
-
|
|
|
35
|
-
|
|
|
45
|
+
| File | `seller-review.ts` |
|
|
46
|
+
| Sides | `MercurModules.SELLER` `seller` and `MercurModules.REVIEW` `review` |
|
|
47
|
+
| Cardinality | One seller, many reviews |
|
|
48
|
+
| Link table | `seller_seller_review_review` |
|
|
49
|
+
| Aliases | `review.seller`, `seller.reviews` |
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
```ts
|
|
52
|
+
const { data: [seller] } = await query.graph({
|
|
53
|
+
entity: "seller",
|
|
54
|
+
fields: ["name", "reviews.rating", "reviews.seller_note"],
|
|
55
|
+
filters: { id: "sel_01JD8Q1234567" },
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Order and review
|
|
60
|
+
|
|
61
|
+
Connects a review to the order that earned it. Written for every review by the `create-review` step. `createReviewWorkflow` reads it to reject a second review of the same target from one order.
|
|
62
|
+
|
|
63
|
+
| Property | Value |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| File | `order-review.ts` |
|
|
66
|
+
| Sides | `Modules.ORDER` `order` and `MercurModules.REVIEW` `review` |
|
|
67
|
+
| Cardinality | One order, many reviews |
|
|
68
|
+
| Link table | `order_order_review_review` |
|
|
69
|
+
| Aliases | `review.order`, `order.reviews` |
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const { data: [order] } = await query.graph({
|
|
73
|
+
entity: "order",
|
|
74
|
+
fields: ["display_id", "reviews.reference", "reviews.rating"],
|
|
75
|
+
filters: { id: "order_01JD8Q1234567" },
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Customer and review
|
|
80
|
+
|
|
81
|
+
Connects a review to the customer who wrote it. Written for every review by the `create-review` step. The Store API limits a customer to their own reviews through this link.
|
|
82
|
+
|
|
83
|
+
| Property | Value |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| File | `customer-review.ts` |
|
|
86
|
+
| Sides | `Modules.CUSTOMER` `customer` and `MercurModules.REVIEW` `review` |
|
|
87
|
+
| Cardinality | One customer, many reviews |
|
|
88
|
+
| Link table | `customer_customer_review_review` |
|
|
89
|
+
| Aliases | `review.customer`, `customer.reviews` |
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const { data: [customer] } = await query.graph({
|
|
93
|
+
entity: "customer",
|
|
94
|
+
fields: ["email", "reviews.id", "reviews.status"],
|
|
95
|
+
filters: { id: "cus_01JD8Q1234567" },
|
|
96
|
+
})
|
|
97
|
+
```
|
|
39
98
|
|
|
40
99
|
<Note>
|
|
41
|
-
|
|
42
|
-
seller, order, or customer has many reviews.
|
|
100
|
+
`deleteReviewWorkflow` soft deletes the review but keeps all of its link rows. The service’s rating helpers join through the product and seller link tables, so they still count a soft deleted review.
|
|
43
101
|
</Note>
|
|
@@ -1,54 +1,82 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Review module service"
|
|
3
3
|
sidebarTitle: "Service"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Resolve the Review module service, its generated CRUD methods, and its three rating helpers."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The Review module
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
The Review module service, `ReviewModuleService`, reads and writes `Review` records directly and computes average ratings. Use it in subscribers, scheduled jobs, and routes when you don’t need a workflow’s validation, links, or compensation.
|
|
8
|
+
|
|
9
|
+
## Resolve the service
|
|
10
|
+
|
|
11
|
+
Resolve the service from the Medusa container with `MercurModules.REVIEW`, which is the string `"review"`:
|
|
10
12
|
|
|
11
13
|
```ts
|
|
12
14
|
import { MercurModules } from "@mercurjs/types"
|
|
13
15
|
|
|
14
|
-
const
|
|
16
|
+
const reviewService = container.resolve(MercurModules.REVIEW)
|
|
15
17
|
|
|
16
|
-
const [reviews, count] = await
|
|
17
|
-
status: "published",
|
|
18
|
-
}
|
|
18
|
+
const [reviews, count] = await reviewService.listAndCountReviews(
|
|
19
|
+
{ status: "published" },
|
|
20
|
+
{ order: { created_at: "DESC" }, take: 20 }
|
|
21
|
+
)
|
|
19
22
|
```
|
|
20
23
|
|
|
24
|
+
This lists the 20 newest published reviews and the total count. Filters apply to the review’s own fields, so you can’t filter by product or seller here: use `query.graph` from the linked side, as shown in the [Links reference](/platform/review/reference/links).
|
|
25
|
+
|
|
26
|
+
<Warning>
|
|
27
|
+
Writing through the service skips the workflow checks. `createReviews` doesn’t validate the order, reject duplicates, or write any link, and `updateReviews` doesn’t stop a second store answer.
|
|
28
|
+
</Warning>
|
|
29
|
+
|
|
21
30
|
## Generated methods
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
`MedusaService` generates these methods for the `Review` model:
|
|
24
33
|
|
|
25
34
|
| Method | Description |
|
|
26
35
|
| --- | --- |
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `updateReviews(data)` | Update
|
|
32
|
-
| `deleteReviews(ids)` | Delete
|
|
33
|
-
| `softDeleteReviews(ids)` |
|
|
34
|
-
| `restoreReviews(ids)` |
|
|
36
|
+
| `retrieveReview(id, config?)` | Retrieve one review. Throws when it doesn’t exist |
|
|
37
|
+
| `listReviews(filters?, config?)` | List reviews |
|
|
38
|
+
| `listAndCountReviews(filters?, config?)` | List reviews and the total count |
|
|
39
|
+
| `createReviews(data)` | Create one review or an array of reviews |
|
|
40
|
+
| `updateReviews(data)` | Update by `id`, or by `{ selector, data }` |
|
|
41
|
+
| `deleteReviews(ids)` | Delete reviews permanently |
|
|
42
|
+
| `softDeleteReviews(ids)` | Set `deleted_at` on reviews |
|
|
43
|
+
| `restoreReviews(ids)` | Clear `deleted_at` on reviews |
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
Each write emits a mutation event, listed in the [Events reference](/platform/review/reference/events).
|
|
37
46
|
|
|
38
|
-
|
|
47
|
+
## getAvgRating
|
|
39
48
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
Returns the average rating of one product or one seller.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
getAvgRating(
|
|
53
|
+
type: "seller" | "product",
|
|
54
|
+
id: string
|
|
55
|
+
): Promise<string | null>
|
|
56
|
+
```
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
for usage.
|
|
58
|
+
The result is the Postgres `AVG` of `rating` as a numeric string, such as `"4.5000000000000000"`, or `null` when the target has no linked reviews. It counts every linked review whatever its `status`, including soft deleted ones.
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
## getProductsWithRating
|
|
61
|
+
|
|
62
|
+
Returns every product with its average rating.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
getProductsWithRating(fields: string[]): Promise<Record<string, unknown>[]>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`fields` are column names of the `product` table, such as `["id", "title"]`. Each row gains a `rating` numeric string, or `null`. The query left joins the `product_product_review_review` link table and groups by product, with no filters, pagination, or `deleted_at` checks.
|
|
69
|
+
|
|
70
|
+
## getSellersWithRating
|
|
71
|
+
|
|
72
|
+
Returns every seller with its average rating.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
getSellersWithRating(fields: string[]): Promise<Record<string, unknown>[]>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`fields` are column names of the `seller` table, such as `["id", "name"]`. It works like `getProductsWithRating`, joining through `seller_seller_review_review`.
|
|
79
|
+
|
|
80
|
+
<Note>
|
|
81
|
+
The return types above describe the rows. The source declares no explicit return type for the two list helpers, so TypeScript infers Knex’s result type.
|
|
82
|
+
</Note>
|