Commit Graph

660 Commits

Author SHA1 Message Date
Sebastian Rindom
555eb41fca feat(tax): add endpoints to manage tax rate rules (#6557)
**What**
Adds endpoints to manage tax rules on a tax rate:
- Create a tax rule: POST /admin/tax-rates/:id/rules 
- Delete a tax rule: DELETE /admin/tax-rates/:id/rules/:rule_id
- Replace tax rules: POST /admin/tax-rates/:id -- with { rules: [...] } in body.

### Noteworthy things I bumped into

**Updating nested relationships**
A TaxRate can have multiple TaxRules and in this PR we enable users to replace all TaxRules associated with a TaxRate in one operation. If working with the module directly this can be done with:

```javascript
taxModuleService.update(rateId, { rules: [{ ... }] })
```

Internally in the `update` function the TaxModule first soft deletes any TaxRules that exist on the TaxRate and then creates new TaxRules for the passed rules ([see test](https://github.com/medusajs/medusa/pull/6557/files#diff-cdcbab80ac7928b80648088ec57a3ab09dddd4409d6afce034f2caff08ee022bR78)).

A challenge arises when doing this in a compensatable way in a workflow. To see this imagine the following:
1. `updateTaxRatesWorkflow` gets the current data for the tax rates to update. This includes the tax rates' rules.
2. `updateTaxRatesWorkflow` calls `taxModuleService.update` with new rules. 
3. Internally, the tax module deletes the rules in 1. and creates new rules.
4. Imagine an error happens in a following step and the workflow has to compensate.
5. The workflow uses the data from 1. and calls upsert. The tax module may correctly update the previous tax rules so they are no longer soft deleted. However, upsert (at least not by default) doesn't delete the new rules that were created in 2.

As illustrated by 5. compensating the update is not pretty. To get around this I instead opted to let the workflow handle setting the rules for a rate that makes the compensation more straightforward to handle. [See workflow here](https://github.com/medusajs/medusa/pull/6557/files#diff-ff19e1f2fa32289aefff90d33c05c154f9605a3c5da6a62683071a1fcaedfd7bR89).

**Using nested workflows**
Initially, I wanted to use the `setTaxRateRulesWorkflow` within the `updateTaxRatesWorkflow`. And this worked great for the invoke phase. However, when I needed to compensate the update workflow (and hence also had to compensate the set rules workflow), I found that the workflow engine no longer had the set rules transaction in memory and therefore could not roll it back. ([This is where I try to rollback](https://github.com/medusajs/medusa/pull/6557/files#diff-ff19e1f2fa32289aefff90d33c05c154f9605a3c5da6a62683071a1fcaedfd7bR62), but the transaction id can't be found).

I therefore opted to copy the steps from the set tax rate rules workflow into the update tax rates workflow; however, once we figure out a good way to ensure we can compensate nested workflows we should move to the nested workflow instead. 

This also made me realize that the current implementation of workflows that use `refreshCartPromotions` may create inconsistencies in case of failures (cc: @riqwan).
2024-03-04 10:30:54 +00:00
Riqwan Thamir
d550be3685 feat(core-flows,link-modules,modules-sdk): add cart <> promotion link as source of truth (#6561)
what:

- adds promotion cart link
- update steps to create and remove links
2024-03-04 09:50:49 +00:00
Riqwan Thamir
8dad2b51a2 feat(medusa-react,medusa,utils): add users/me endpoint + add missing specs (#6441)
**what:**

- adds /me endpoint
- adds fixes to routes
- adds specs for auth endpoint
- updates dotenv package versions


Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
2024-03-04 09:07:47 +00:00
Oli Juhl
883cb0dca7 feat: Add payment collection creation for cart (#6527) 2024-03-04 09:02:01 +01:00
Stevche Radevski
71ed21de4a feat: Add supported currencies to store model (#6562)
I ultimately went with having a flat list of settings for the store. It wouldn't be very difficult to change it if we wish to do so, but for now this keeps the codebase simpler
2024-03-01 15:43:47 +00:00
Stevche Radevski
347aba719c fix: Move default country loading for region to the loader, fix a bug with cascade (#6559) 2024-03-01 14:58:55 +01:00
Stevche Radevski
196e821ff2 feat: Modify api key and sales channel link to use modules and add test (#6546) 2024-03-01 09:24:50 +00:00
Oli Juhl
cdb01e073b feat(link-modules): Cart, Payment Collection link definition (#6508)
* Add cart payment collection joiner confg

* fix migration

* chore: Exclude inventory tests

* remove jest ignore

* add back payment module
2024-02-29 19:33:16 +01:00
Oli Juhl
296d7faad4 chore: V2 core loader + modules integration-tests (#6544) 2024-02-29 16:46:30 +01:00
Stevche Radevski
dc025302a1 feat: Add currency module and remove currency models from region and pricing modules (#6536)
What:
- Creates a new currency module
- Removes currency model from the pricing module
- Removes currency model from region module
2024-02-29 15:09:59 +00:00
Sebastian Rindom
c4760dfd5f feat(tax): v2 api tax rates and regions deletes (#6541) 2024-02-29 12:06:11 +00:00
Sebastian Rindom
6279fb3c67 feat(tax): add support for updating tax rates (#6537) 2024-02-29 11:03:18 +00:00
Sebastian Rindom
2407b443f1 feat(tax): add endpoints to create tax regions and tax rates (#6533)
**What**
Adds:
- POST /admin/tax-regions
- POST /admin/tax-rates
- GET /admin/tax-rates
- `createTaxRegionsWorkflow`
- `createTaxRatesWorkflow`
2024-02-29 10:26:21 +00:00
Riqwan Thamir
557d86afbf feat(medusa,core-flows): update cart adjustments on item updates (#6539) 2024-02-28 19:35:24 +01:00
Sebastian Rindom
adad66e13f feat(tax): migration file (#6523)
**What**
- Tax Module Migration file.
- Skeleton for API routes and integrations tests for tax API in v2
2024-02-28 14:40:35 +00:00
Riqwan Thamir
0d46abf0ff feat(types): promotion module uses big number (#6522)
what: 

- promotion modules (application method and campaign budget) uses big number fields instead of numeric fields
- refreshes the migrations to include new fields for big numbers
- adds a promotion_ prefix to promotion models
- uses `take: null` within the module to prevent the default pagination on performing actions within the module
2024-02-28 11:57:43 +00:00
Oli Juhl
c5d35ec7f2 chore(cart): Use module native soft-delete/delete methods (#6491) 2024-02-28 10:45:56 +00:00
Stevche Radevski
d60f3adc03 feat: Add basic endpoints and workflows for Store module (#6515) 2024-02-28 10:08:11 +00:00
Stevche Radevski
753bd93ba1 feat(api-key): Add api-key authentication to middleware (#6521)
Also did a bit of a cleanup on the auth middleware. There should be no behavioral changes, just moved code around.
2024-02-27 13:44:37 +00:00
Oli Juhl
3ee0f599c1 feat: Line Items API Routes (#6478)
**What**
- `POST /store/carts/:id/line-items`
- `POST /store/carts/:id/line-items/:id`
- `DELETE /store/carts/:id/line-items/:id`

**Outstanding**
- Integration tests
- Module integrations: Payment, Fulfillment, Promotions

Depends on #6475 and #6449.
2024-02-27 12:47:00 +00:00
Riqwan Thamir
f5c2256286 feat(core-flows,medusa,types,utils): adds update cart API with promotions (#6514)
what:

- adds update cart API
  - workflow
  - promotions
  - sales channel
  - region
  - customer
2024-02-27 12:09:30 +00:00
Stevche Radevski
690e8c2e09 feat(api-key): Allow revoking in the future, and enforce the secret key (#6484)
Since there is quite a bit of code here already, I'll do the middleware changes in a separate PR
2024-02-27 10:37:32 +00:00
Philip Korsholm
e747f9d4aa feat: Refresh invite (#6469) 2024-02-27 07:16:52 +00:00
Philip Korsholm
7bddb58542 feat: Update authentication middleware (#6447)
* authentication middleware update

* disable customer authentication

* call correct feature flag method

* fix authentication middleware for store/customers

* fix integration tests and add middleware for admin customers

* update seeders

* customer groups fix

* add authentication middleware for all admin endpoints

* Feat(medusa, user): require authentication for invite accept (#6448)

* initial invite token validation for authentication invocation

* remove invite auth

* remove unused import

* cleanup tests

* refactor to auth instead of auth_user

* pr feedback

* update authenticatedRequest type

* update store authenticated endpoints

* update routes with type

* fix build

* fix build

* fix build

* use auth middleware for api-keys
2024-02-27 13:50:18 +08:00
Oli Juhl
7ebe885ec9 feat: Create cart with line items (#6449)
**What**
- Add support for creating a cart with items
- Add endpoint `POST /store/carts/:id/line-items`
- Add `CreateCartWorkflow`
- Add `AddToCartWorkflow`
- Add steps for both workflows

**Testing**
- Endpoints
- Workflows

I would still call this a first iteration, as we are missing a few pieces of the full flow, such as payment sessions, discounts, and taxes.

Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
2024-02-26 13:32:16 +00:00
Riqwan Thamir
ac86362e81 feat(workflows-sdk,core-flows,medusa,types): add workflow to update promotions to cart (#6474)
what:

- adds API + workflow to add/remove promotions in a cart
- minor fixes in promotions module
- minor type fixes in cart module
- typing fix in workflows-sdk (Thanks @adrien2p)
- fix step result in workflows-sdk (Thanks @adrien2p)

RESOLVES CORE-1768

Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
2024-02-26 12:43:57 +00:00
Stevche Radevski
0582545065 feat(api-key): Add the endpoints and workflows for api key module 2024-02-23 11:01:08 +01:00
Stevche Radevski
cfefd59249 feat(regions): Add support for updating countries in a region (#6432)
**What**
Add missing support for updating countries on the POST /admin/regions/:id route.
Furthermore, the PR refactors how the normalization and validation was handled in both create and update scenarios.
Finally, the PR adds a unique index on the country entity, which ensures at the DB level that we cannot have a duplicate country in the DB for a single region.
2024-02-20 11:46:42 +00:00
Oli Juhl
691f68c3b8 feat(region): Region create, delete, update admin endpoints (#6332)
**What**
Add `POST /admin/regions`
Add `POST /admin/regions/:id`
Add `DELETE /admin/regions/:id`

All are added for v2 using API Routes and workflows.

In follow-up PRs, I will add support for passing countries in update and create.

Update: First follow-up PR is #6372
2024-02-20 10:19:07 +00:00
Oli Juhl
680dfcdad3 feat(core-flow, medusa): Create cart with Sales Channel (#6427) 2024-02-19 09:42:11 +01:00
Oli Juhl
5db3ec09e2 feat: Cart Customer link + create cart with customer (#6426) 2024-02-19 08:28:44 +01:00
Philip Korsholm
f6d38163bb Feat(medusa, user, core-flows): User creation with invites (#6413)
**What**
- add accept invite endpoint 

**Invite accept flow**
- authenticate using the auth endpoint to create and auth-user
- invoke accept endpoint with token and info to create user
2024-02-19 05:29:15 +00:00
Oli Juhl
1ba35b02dd feat: Cart SalesChannel link + clean-up (#6418) 2024-02-16 20:48:56 +01:00
Oli Juhl
24fb102a56 feat: CartRegion link, definition + workflow (#6392) 2024-02-16 14:06:26 +00:00
Philip Korsholm
02c53ec93f feat(medusa, types, core-flows): Add invite endpoints for api-v2 (#6395)
**What**
- Add invite endpoints for simple operations on invites
2024-02-14 15:33:26 +00:00
Oli Juhl
1ed5f918c3 feat(cart): POST /store/carts/:id (#6274)
Depends on:
- #6262 
- #6273
2024-02-14 15:03:02 +00:00
Philip Korsholm
4d51f095b3 feat(user, types): add invite and user properties + migration (#6327)
**What**
- Add invite model 
- Populate user model
2024-02-14 13:58:14 +00:00
Carlos R. L. Rodrigues
0c2a460751 feat(medusa): workflow engine api (#6330)
What:

   Workflow Engine API.
   Endpoints for:
     - List workflow executions
     - Run a workflow
     - Set async steps as success or failure
     - Retrieve the details of a workflow run
2024-02-13 15:19:10 +00:00
Oli Juhl
95d0e58d31 feat(region): Add admin region get + list endpoints (#6322)
**What**
Add `GET /admin/regions`
Add `GET /admin/regions/:id`

Blocked by #6320 

Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
2024-02-11 17:13:49 +00:00
Philip Korsholm
882aa549bd Feat(auth): Remove auth provider entity (#6314)
**What**
- remove auth provider entity

**Why**
- The auth provider entity was not really used anywhere

**How**
- Keeping loader behavior as is but removing the 

Co-authored-by: Sebastian Rindom <7554214+srindom@users.noreply.github.com>
2024-02-06 07:54:34 +00:00
Oli Juhl
ede221d4f7 feat(cart): POST /store/carts (#6273)
Depends on #6262
2024-02-05 15:15:15 +00:00
Philip Korsholm
e2738ab91d feat(auth): Make token auth default (#6305)
**What**
- make token auth the default being returned from authentication endpoints in api-v2
- Add `auth/session` to convert token to session based auth
- add regex-scopes to authenticate middleware 

Co-authored-by: Sebastian Rindom <7554214+srindom@users.noreply.github.com>
2024-02-05 08:17:08 +00:00
Adrien de Peretti
a7be5d7b6d chore: Abstract module service (#6188)
**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[]>
}
```
2024-02-02 14:20:32 +00:00
Philip Korsholm
9fda6a6824 feat(auth): add authentication endpoints (#6265)
**What**
- Add authentication endpoints: 
  - `/auth/[scope]/[provider]` 
  - `/auth/[scope]/[provider]/callback`
- update authenticate-middleware handler
- Add scope field to user
- Add unique constraint on scope and entity_id

note: there's still some remaining work related to jwt auth to be handled, this is mainly focussed on session auth with endpoints



Co-authored-by: Sebastian Rindom <7554214+srindom@users.noreply.github.com>
2024-02-02 10:45:32 +00:00
Carlos R. L. Rodrigues
45134e4d11 chore: local workflow proxying methods to pass context (#6263)
What:
- When calling a module's method inside a Local Workflow the MedusaContext is passed as the last argument to the method if not provided
- Add `requestId` to req
- A couple of fixes on Remote Joiner and the data fetcher for internal services

Why:
- The context used to initialize the workflow has to be shared with all modules. properties like transactionId will be used to emit events and requestId to trace logs for example.
2024-02-01 13:37:26 +00:00
Sebastian Rindom
a2bf6756ac feat(customer): manage default address selection (#6295)
**What**
- Catches unique constraints on customer_id, is_default_billing/is_default_shipping and reformats
- Adds an step to create and update of addresses that unsets the previous default shipping/billing address if necessary.
  - This creates a behavior in the API where you can always set an address to be default and it will automatically unset the previous one for you.
2024-02-01 12:28:14 +00:00
Sebastian Rindom
a28822e0d4 feat(customer): store addresses (#6283)
- GET /customers/me/addresses
- POST /customers/me/addresses
- GET /customers/me/addresses/:address_id
- POST /customers/me/addresses/:address_id
- DELETE /customers/me/addresses/:address_id
2024-02-01 10:11:52 +00:00
Sebastian Rindom
7903a15e0f feat(customer): Add create and retrieve customer from store side (#6267)
**What**
- GET /store/customers/me
- POST /store/customers
- Workflow for customer account creation
- Authentication middleware on customer routes
2024-01-31 11:58:29 +00:00
Sebastian Rindom
f41877ef61 feat(customer): add customer group customer management (#6276)
**What**
- GET /admin/customer-groups/:id/customers
- POST /admin/customer-groups/:id/customers/batch
- POST /admin/customer-groups/:id/customers/remove

Workflows
2024-01-31 10:24:22 +00:00
Sebastian Rindom
36ec3ea3aa feat(customer): admin addresses (#6235)
**What**
- GET /admin/customers/:id/addresses
- POST /admin/customers/:id/addresses
- POST /admin/customers/:id/addresses/:address_id
- DELETE /admin/customers/:id/addresses/:address_id
2024-01-31 09:55:07 +00:00