**What**
Initialize work on the fulfillment module entities.
This pr finally also include the indexes as i was working on some utilities i though it would make sense to test them directly.
Also this pr add a new utility to generate proper index for our entity properties. It also include a new monkey patch for the migration generator to handle most of if exists/not exists cases. The monkey patch is a workaround the fact that the transpilation does work well with the ECMA used by mikro orm and therefore we end up with some constructor issue when mikro orm try to instanciate the custom generator class extension.
**Comment**
- The rule part will likely evolved when we reach the point of rule filtering based data, so no need for details review I believe
FIXES CORE-1714
FIXES CORE-1715
FIXES CORE-1718
FIXES CORE-1722
FIXES CORE-1723
Current schema diagram

**What**
- Remove services that do not have any custom business and replace them with a simple interfaces
- Abstract module service provide the following base implementation
- retrieve
- list
- listAndCount
- delete
- softDelete
- restore
The above methods are created for the main model and also for each other models for which a config is provided
all method such as list, listAndCount, delete, softDelete and restore are pluralized with the model it refers to
**Migration**
- [x] product
- [x] pricing
- [x] promotion
- [x] cart
- [x] auth
- [x] customer
- [x] payment
- [x] Sales channel
- [x] Workflow-*
**Usage**
**Module**
The module service can now extend the ` ModulesSdkUtils.abstractModuleServiceFactory` which returns a class with the default implementation for each method and each model following the standard naming convention mentioned above.
This factory have 3 template arguments being the container, the main model DTO and an object representing the other model with a config object that contains at list the DTO and optionally a singular and plural property in case it needs to be set manually. It looks like the following:
```ts
export default class PricingModuleService</* ... */>
extends ModulesSdkUtils.abstractModuleServiceFactory<
InjectedDependencies,
PricingTypes.PriceSetDTO,
{
Currency: { dto: PricingTypes.CurrencyDTO }
MoneyAmount: { dto: PricingTypes.MoneyAmountDTO }
PriceSetMoneyAmount: { dto: PricingTypes.PriceSetMoneyAmountDTO }
PriceSetMoneyAmountRules: {
dto: PricingTypes.PriceSetMoneyAmountRulesDTO
}
PriceRule: { dto: PricingTypes.PriceRuleDTO }
RuleType: { dto: PricingTypes.RuleTypeDTO }
PriceList: { dto: PricingTypes.PriceListDTO }
PriceListRule: { dto: PricingTypes.PriceListRuleDTO }
}
>(PriceSet, generateMethodForModels, entityNameToLinkableKeysMap)
implements PricingTypes.IPricingModuleService
{
// ...
}
```
In the above, the singular and plural can be inferred as there is no tricky naming. Also, the default implementation does not remove the fact that you need to provides all the overloads etc in your module service interface. The above will provide a default implementation following the interface `AbstractModuleService` which is also auto generated, hence you will have the following methods available:
**for the main model**
- list
- retrieve
- listAndCount
- delete
- softDelete
- restore
**for the other models**
- list**MyModels**
- retrieve**MyModel**
- listAndCount**MyModels**
- delete**MyModels**
- softDelete**MyModels**
- restore**MyModels**
**Internal module service**
The internal module service can now extend `ModulesSdkUtils.internalModuleServiceFactory` which takes only one template argument which is the container type.
All internal services provides a default implementation for all retrieve, list, listAndCount, create, update, delete, softDelete, restore methods which follow the following interface `ModulesSdkTypes.InternalModuleService`:
```ts
export interface InternalModuleService<
TEntity extends {},
TContainer extends object = object
> {
get __container__(): TContainer
retrieve(
idOrObject: string,
config?: FindConfig<any>,
sharedContext?: Context
): Promise<TEntity>
retrieve(
idOrObject: object,
config?: FindConfig<any>,
sharedContext?: Context
): Promise<TEntity>
list(
filters?: FilterQuery<any> | BaseFilterable<FilterQuery<any>>,
config?: FindConfig<any>,
sharedContext?: Context
): Promise<TEntity[]>
listAndCount(
filters?: FilterQuery<any> | BaseFilterable<FilterQuery<any>>,
config?: FindConfig<any>,
sharedContext?: Context
): Promise<[TEntity[], number]>
create(data: any[], sharedContext?: Context): Promise<TEntity[]>
create(data: any, sharedContext?: Context): Promise<TEntity>
update(data: any[], sharedContext?: Context): Promise<TEntity[]>
update(data: any, sharedContext?: Context): Promise<TEntity>
update(
selectorAndData: {
selector: FilterQuery<any> | BaseFilterable<FilterQuery<any>>
data: any
},
sharedContext?: Context
): Promise<TEntity[]>
update(
selectorAndData: {
selector: FilterQuery<any> | BaseFilterable<FilterQuery<any>>
data: any
}[],
sharedContext?: Context
): Promise<TEntity[]>
delete(idOrSelector: string, sharedContext?: Context): Promise<void>
delete(idOrSelector: string[], sharedContext?: Context): Promise<void>
delete(idOrSelector: object, sharedContext?: Context): Promise<void>
delete(idOrSelector: object[], sharedContext?: Context): Promise<void>
delete(
idOrSelector: {
selector: FilterQuery<any> | BaseFilterable<FilterQuery<any>>
},
sharedContext?: Context
): Promise<void>
softDelete(
idsOrFilter: string[] | InternalFilterQuery,
sharedContext?: Context
): Promise<[TEntity[], Record<string, unknown[]>]>
restore(
idsOrFilter: string[] | InternalFilterQuery,
sharedContext?: Context
): Promise<[TEntity[], Record<string, unknown[]>]>
upsert(data: any[], sharedContext?: Context): Promise<TEntity[]>
upsert(data: any, sharedContext?: Context): Promise<TEntity>
}
```
When a service is auto generated you can use that interface to type your class property representing the expected internal service.
**Repositories**
The repositories can now extend `DALUtils.mikroOrmBaseRepositoryFactory` which takes one template argument being the entity or the template entity and provides all the default implementation. If the repository is auto generated you can type it using the `RepositoryService` interface. Here is the new interface typings.
```ts
export interface RepositoryService<T = any> extends BaseRepositoryService<T> {
find(options?: FindOptions<T>, context?: Context): Promise<T[]>
findAndCount(
options?: FindOptions<T>,
context?: Context
): Promise<[T[], number]>
create(data: any[], context?: Context): Promise<T[]>
// Becareful here, if you have a custom internal service, the update data should never be the entity otherwise
// both entity and update will point to the same ref and create issues with mikro orm
update(data: { entity; update }[], context?: Context): Promise<T[]>
delete(
idsOrPKs: FilterQuery<T> & BaseFilterable<FilterQuery<T>>,
context?: Context
): Promise<void>
/**
* Soft delete entities and cascade to related entities if configured.
*
* @param idsOrFilter
* @param context
*
* @returns [T[], Record<string, string[]>] the second value being the map of the entity names and ids that were soft deleted
*/
softDelete(
idsOrFilter: string[] | InternalFilterQuery,
context?: Context
): Promise<[T[], Record<string, unknown[]>]>
restore(
idsOrFilter: string[] | InternalFilterQuery,
context?: Context
): Promise<[T[], Record<string, unknown[]>]>
upsert(data: any[], context?: Context): Promise<T[]>
}
```
**What**
Currently, when exporting a workflow using the workflowExport util, it is mandatory to first call the resulted function passing the container before being able to call run, registerStepSuccess or failure on it. Now, it is possible to either continue that way, or to directly call the run, registerStepSuccess or failure on the exported workflow and at that moment it is possible to pass a container if needed
e.g
```ts
const workflow = exportWorkflow("id" as any, "result_step", prepare)
const wfRunner = workflow(container)
wfRunner.run(...) // Here the container is not expected
```
or
```ts
const workflow = exportWorkflow("id" as any, "result_step", prepare)
workflow.run(...) // Here we can now pass an optional container
```
**What**
Create a service abstraction for the modules internal service layer. The objective is to reduce the effort of building new modules when the logic is the same or otherwise allow to override the default behavior.
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
**What**
Reduce the work effort to create repositories when building new modules by abstracting the most common cases into the base class default implementation returned by a factory
- [x] Migrate all modules
Co-authored-by: Riqwan Thamir <5105988+riqwan@users.noreply.github.com>
* feat(orchestration, workflows-sdk): Add events management and implementation to manage async workflows
* Create fresh-boxes-scream.md
* cleanup
* fix: resolveValue input ref
* resolve value recursive
* chore: resolve result value only for new api
* chore: save checkpoint before scheduling
* features
* fix: beginTransaction checking existing transaction
---------
Co-authored-by: Carlos R. L. Rodrigues <rodrigolr@gmail.com>
Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
* feat(workflows-sdk): Allow primitive values to be passed as well as WorkflowDataProperties
* feat(workflows-sdk): improve playground tests
* Create nine-shrimps-reply.md
**What**
Allow a step to not define an expected input, previously even if no input was expected, an object was always expected to be passed to the stepFunction inside the workflow composition. Now
```ts
const stepWithoutArgs = createStep("step1", () => {
return new StepResponse("string")
})
const stepWithoutExepectedInput = createStep("step2", (_: {}, context) => {
console.log("input", _) // {}
return new StepResponse("string")
})
const workflow = createWorkflow("workflow1", () => {
stepWithoutArgs()
return stepWithoutExepectedInput()
})
workflow()
.run()
.then((res) => {
console.log(res.result) // string
})
```
**What**
One problem with local development can be the dependencies management. To mitigate that, transform checks from an instance of to value check as well as harmonize utils function to be part of the class as a static method instead of separate utilities. By using this approach we are not dependent of the origin of the class and therefore it should ease the user experience.
**NOTE**
As a next step to discuss, we should probably move the interfaces from the interfaces/medusa package to the utils package. Then we can deprecate the interfaces package and remove it at a later time
**What**
- Allow to create step that can be configured to have a max retry
- Step end retry mechanism on permanent failure
Also added an API to override a step configuration from within the createWorkflow
```ts
const step = createStep({ name: "step", maxRetries: 3 }, async (_, context) => {
return new StepResponse({ output: "output" })
})
const workflow = createWorkflow("workflow", function () {
const res = step().config({ maxRetries: 5 }) // This will override the original maxRetries of 3
})
```
**NOTE**
We can maybe find another name than config on the step workflow data to override the step config.
**What**
Resolve potential discrepency between what is calculated in the `createFromCart` and the cart/order totals calculation.
When the giftCard is taxable then the following calculation is applied
```ts
cart.subtotal + cart.shipping_total - cart.discount_total
```
otherwise
```ts
cart.subtotal +
cart.shipping_total +
cart.tax_total -
cart.discount_total
```
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
**What**
At the moment, when importing something from medusa entry point, if two medusa packages are installed because of a plugin, the evaluation of the import can end up throwing that a workflow is already defined. To prevent that from happening, we can just not throw if it is already defined and just return prematurely and make it idempotent.
**What**
- transform remote query object back to string array
- get all fields and fields from given relations from a GraphQL schema
Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
**What**
First iteration:
Admin list products return everything but does not include proper filtering by discount id or price list id. Also, same as the previous pr, the fields/expand is not included
depends on https://github.com/medusajs/medusa/pull/5130
**What**
Update cart must use the remote query when being in product isolation mode.
This pr is a first iteration that aims to quickly make compatible the end point in isolation. In a future iteration, this end point will be migrated to a workflow
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
**What**
For simplicity and compatibility with the front, the fields are stored as an array of strings containing the list of fields that should be selected by a query. In order to reduce the delta between this object shape and what is expected by the remoteQuery when passing an object, we built util to make the translation from fields to config.
This will allow us to update the endpoint fields to specify what exactly needs to be selected and based on that we will be able to build the remote query object. Furthermore, it will allow us to drop back the functionality of custom fields and relations. we will still have to take into account the limit constraint of an url size including the parameters if a user want to select everything from a relation. In that case, we might maybe think about handling this case once the modules will export all available relations and fields so that the remote joiner would be able to pass them all automatically if the relation is present but a `*` is passed or no values in the fields during the translation with the util. But this is something we can come up in a separate iteration
**What**
- includes some type fixes in the DAL layer
- List products including their prices and filtered by the sales channel as well as q parameter and category scope and all other filters
- Assign shipping profile
- ordering
- Add missing columns in the product module
- update product module migrations
**Comment**
- In regards to the fields, we can pass whatever we want the module will only return the one that exists (default behavior), but on the other hand, that is not possible for the relations.
**question**
- To simplify usage, should we expose the fields/relations available from the module to simplify building a query for the user and be aware of what the module provides
**todo**
- Add back the support for the user to ask for fields/relations
**What**
Add a new configuration on the relationship to specify if the relation is consumed from an internal service (from medusa core). In that case do not check if the service is part of the loaded modules
**What**
Integration tests to validate the workflow compensation.
Also, fix the transaction state when the workflow is compensating and some steps does not have any compensation
**What**
The listProducts handler should not be specific to the workflow.
Same for the product removal, shouldn't expect an entire DTO but just a collection of object that at least contains the id.
Same principle applied to other product handlers
* Feat: create product with product module
* feat: create product wip
* feat: create product wip
* feat: update product relation and generate image migration
* lint
* conitnue implementation
* continue implementation and add integration tests for produceService.create
* Add integration tests for product creation at the module level for the complete flow
* only use persist since write operations are always wrapped in a transaction which will be committed and flushed
* simplify the transaction wrapper to make future changes easier
* feat: move some utils to the utils package to simplify its usage
* tests: fix unit tests
* feat: create variants along side the product
* Add more integration tests an update migrations
* chore: Update actions workflow to include packages integration tests
* small types and utils cleanup
* chore: Add support for database debug option
* chore: Add missing types in package.json from types and util, validate that all the models are sync with medusa
* expose retrieve method
* fix types issues
* fix unit tests and move integration tests workflow with the plugins integration tests
* chore: remove migration function export from the definition to prevent them to be ran by the medusa cli just in case
* fix package.json script
* chore: workflows
* feat: start creating the create product workflow
* feat: add empty step for prices and sales channel
* tests: update scripts and action envs
* fix imports
* feat: Add proper soft deleted support + add product deletion service public api
* chore: update migrations
* chore: update migrations
* chore: update todo
* feat: Add product deletion to the create-product workflow as compensation
* chore: cleanup product utils
* feat: Add support for cascade soft-remove
* feat: refactor repository to take into account withDeleted
* fix integration tests
* Add support for force delete -> delete, cleanup repositories and improvements
* Add support for restoring a product and add integration tests
* cleaup + tests
* types
* fix integration tests
* remove unnecessary comments
* move specific mikro orm usage to the DAL
* Cleanup workflow functions
* Make deleted_at optional at the property level and add url index for the images
* address feedback + cleanup
* fix export
* merge migrations into one
* feat(product, types): added missing product variant methods (#4475)
* chore: added missing product variant methods
* chore: address PR feedback
* chore: catch undefined case for retrieve + specs for variant service
* chore: align TEntity + add changeset
* chore: revert changeset, TEntity to ProductVariant
* chore: write tests for pagination, unskip the test
* Create chilled-mice-deliver.md
* update integration fixtuers
* update pipeline node version
* rename github action
* fix pipeline
* feat(medusa, types): added missing category tests and service methods (#4499)
* chore: added missing category tests and service methods
* chore: added type changes to module service
* chore: address pr feedback
* update repositories manager usage and serialisation from the write public API
* move serializisation to the DAL
* rename template args
* chore: added collection methods for module and collection service (#4505)
* chore: added collection methods for module and collection service
* Create fresh-islands-teach.md
* chore: move retrieve entity to utils package
* chore: make products optional in DTO type
---------
Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
* feat(product): Apply transaction decorators to the services (#4512)
---------
Co-authored-by: Riqwan Thamir <rmthamir@gmail.com>
Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
* feat(medusa, stripe, paypal): Add update payment session support to the abstract payment processor API
* continue to add updatePaymentData
* feat: Add update payment data to paypal as well and add stripe tests
* fix stripe updatePaymentData returned object type
* fix tests
* Create three-ravens-raise.md
* fix paypal updatePayment data impl
---------
Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
**What**
There is actually an issue with using the `fields` query params with the way the repositories are using our custom query strategy. This pr aims to fix this issue by reworking the strategy.
What we had to do was to rework the way the selects are built for each subquery in order to follow the aliasing convention and to be taken into consideration. Alongside these changes, the join used to always select everything, this needed to be changed so that if there are any selects provided for a join, the join should not select everything and let the query select the fields that are requested.
Another notable change is that all the repositories are now using the repository util in order to centralize the customization and to have a single place to update when this kind of issue arises. This means that the eager relations when using the query builder are not necessarily taken into account. For that reason, I have removed the `shipping_option` eager option in favor of explicitly asking for the relations like we started to do it in some places.
FIXES CORE-1413