docs: added file module docs (#7278)
This commit is contained in:
1 parent
8b2429d24f
commit
803e4aad02
32 files changed
+745
-3448
No files matched your search
@@ -12,8 +12,6 @@ An architectural module implements features and mechanisms related to the Medusa
|
|||||||
|
|
||||||
Since modules are interchangeable, you have more control over Medusa’s architecture. For example, you can choose to use Memcached for event handling instead of Redis.
|
Since modules are interchangeable, you have more control over Medusa’s architecture. For example, you can choose to use Memcached for event handling instead of Redis.
|
||||||
|
|
||||||
Medusa currently supports two types of architectural modules: Cache and event modules.
|
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
|
|
||||||
Refer to the [Architectural Modules reference](!resources!/architectural-modules) for a list of Medusa’s architectural modules.
|
Refer to the [Architectural Modules reference](!resources!/architectural-modules) for a list of Medusa’s architectural modules.
|
||||||
@@ -26,4 +24,4 @@ Refer to the [Architectural Modules reference](!resources!/architectural-modules
|
|||||||
|
|
||||||
You create an architectural module like you’ve been creating custom modules: create a module with a service and export that service in the module’s definition file. The module's service extends an abstract class or interface provided by Medusa with predefined method signatures.
|
You create an architectural module like you’ve been creating custom modules: create a module with a service and export that service in the module’s definition file. The module's service extends an abstract class or interface provided by Medusa with predefined method signatures.
|
||||||
|
|
||||||
The next chapters explain the different architectural module types and how to create an architectural module for each.
|
The next chapters explain the different architectural module types and how to create an architectural module for each.
|
||||||
@@ -18,4 +18,16 @@ The underlying database, third-party service, or caching logic is flexible since
|
|||||||
|
|
||||||
By default, Medusa uses the In-Memory Cache Module. This module uses a plain JavaScript Map object to store the cache data.
|
By default, Medusa uses the In-Memory Cache Module. This module uses a plain JavaScript Map object to store the cache data.
|
||||||
|
|
||||||
This is useful for development. However, for production, it's highly recommended to use other Cache Modules, such as the [Redis Cache Module](!resources!/architectural-modules/cache/redis).
|
This is useful for development. However, for production, it's highly recommended to use other Cache Modules, such as the [Redis Cache Module](!resources!/architectural-modules/cache/redis).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List of Cache Modules
|
||||||
|
|
||||||
|
Refer to the Medusa Learning Resources for a [list of available cache modules](!resources!/architectural-modules/cache).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create a Cache Module
|
||||||
|
|
||||||
|
To create a cache module, refer to [this guide in the Medusa Learning Resources](!resources!/architectural-modules/cache/create).
|
||||||
@@ -12,7 +12,7 @@ In previous chapters, you learned that the Medusa application emits events, and
|
|||||||
|
|
||||||
The Event Module implements the underlying publish/subscribe system that handles queueing events, emitting them, and executing their subscribers.
|
The Event Module implements the underlying publish/subscribe system that handles queueing events, emitting them, and executing their subscribers.
|
||||||
|
|
||||||
This makes the event architecture customizable, as you can either choose one of Medusa’s Event Modules or create your own.
|
This makes the event architecture customizable, as you can either choose one of Medusa’s event modules or create your own.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,3 +21,15 @@ This makes the event architecture customizable, as you can either choose one of
|
|||||||
By default, Medusa uses the Local Event Module. This module uses Node’s EventEmitter to implement the publish/subscribe system.
|
By default, Medusa uses the Local Event Module. This module uses Node’s EventEmitter to implement the publish/subscribe system.
|
||||||
|
|
||||||
This is useful for development. However, for production, it’s highly recommended to use other Event Modules, such as the [Redis Event Module](!resources!/architectural-modules/event/redis).
|
This is useful for development. However, for production, it’s highly recommended to use other Event Modules, such as the [Redis Event Module](!resources!/architectural-modules/event/redis).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List of Event Modules
|
||||||
|
|
||||||
|
Refer to the Medusa Learning Resources for a [list of available event modules](!resources!/architectural-modules/event).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create a Event Module
|
||||||
|
|
||||||
|
To create an event module, refer to [this guide in the Medusa Learning Resources](!resources!/architectural-modules/event/create).
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
export const metadata = {
|
||||||
|
title: `${pageNumber} File Module`,
|
||||||
|
}
|
||||||
|
|
||||||
|
# {metadata.title}
|
||||||
|
|
||||||
|
In this chapter, you’ll learn about what the File Module is.
|
||||||
|
|
||||||
|
## What is the File Module?
|
||||||
|
|
||||||
|
The File Module exposes the functionalities to upload assets, such as product images, to the Medusa application.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is a File Provider Module?
|
||||||
|
|
||||||
|
A file provider module implements the logic of handling uploads and downloads. The File Module must have one file provider module configured.
|
||||||
|
|
||||||
|
By default, Medusa uses the Local File Module. This module uploads files to the `uploads` directory of your Medusa application.
|
||||||
|
|
||||||
|
{/* TODO add once s3 module is working/published? */}
|
||||||
|
|
||||||
|
{/* This is useful for development. However, for production, it’s highly recommended to use other File Modules, such as the [S3 Module](!resources!/architectural-modules/event/redis). */}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List of File Provider Modules
|
||||||
|
|
||||||
|
Refer to the Medusa Learning Resources for a [list of available file provider modules](!resources!/architectural-modules/file).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create a File Provider Module
|
||||||
|
|
||||||
|
To create a file provider module, refer to [this guide in the Medusa Learning Resources](!resources!/architectural-modules/references/file-provider-module).
|
||||||
@@ -9,5 +9,4 @@ In the next chapters, you’ll learn more about customizable aspects of Medusa
|
|||||||
By the end of these chapters, you’ll learn about:
|
By the end of these chapters, you’ll learn about:
|
||||||
|
|
||||||
- What an architectural module is.
|
- What an architectural module is.
|
||||||
- The cache architectural module and how to create one.
|
- The different types of architectural modules, including cache and event modules.
|
||||||
- The events architectural module and how to create one.
|
|
||||||
@@ -253,22 +253,14 @@ export const sidebar = sidebarAttachHrefCommonOptions(
|
|||||||
{
|
{
|
||||||
path: "/architectural-concepts/cache-module",
|
path: "/architectural-concepts/cache-module",
|
||||||
title: "Cache Module",
|
title: "Cache Module",
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: "/architectural-concepts/cache-module/create",
|
|
||||||
title: "Create Cache Module",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/architectural-concepts/event-module",
|
path: "/architectural-concepts/event-module",
|
||||||
title: "Event Module",
|
title: "Event Module",
|
||||||
children: [
|
},
|
||||||
{
|
{
|
||||||
path: "/architectural-concepts/event-module/create",
|
path: "/architectural-concepts/file-module",
|
||||||
title: "Create Event Module",
|
title: "File Module",
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: `${pageNumber} How to Create a Cache Module`,
|
title: `How to Create a Cache Module`,
|
||||||
}
|
}
|
||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
In this chapter, you’ll learn how to create a Cache Module.
|
In this guide, you’ll learn how to create a Cache Module.
|
||||||
|
|
||||||
## 1. Create Module Directory
|
## 1. Create Module Directory
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { ChildDocs } from "docs-ui"
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: `Cache Modules`,
|
||||||
|
}
|
||||||
|
|
||||||
|
# {metadata.title}
|
||||||
|
|
||||||
|
Learn how to create a cache module in [this guide](./create/page.mdx).
|
||||||
|
|
||||||
|
<ChildDocs type="item" filters={["Guides"]} onlyTopLevel={true} />
|
||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: `${pageNumber} How to Create an Event Module`,
|
title: `How to Create an Event Module`,
|
||||||
}
|
}
|
||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
In this chapter, you’ll learn how to create an Event Module.
|
In this guide, you’ll learn how to create an Event Module.
|
||||||
|
|
||||||
## 1. Create Module Directory
|
## 1. Create Module Directory
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { ChildDocs } from "docs-ui"
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: `Event Modules`,
|
||||||
|
}
|
||||||
|
|
||||||
|
# {metadata.title}
|
||||||
|
|
||||||
|
Learn how to create a event module in [this guide](./create/page.mdx).
|
||||||
|
|
||||||
|
<ChildDocs type="item" filters={["Guides"]} onlyTopLevel={true} />
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import { Table } from "docs-ui"
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: `Local File Provider Module`,
|
||||||
|
}
|
||||||
|
|
||||||
|
# {metadata.title}
|
||||||
|
|
||||||
|
The Local File Provider Module stores files uploaded to your Medusa application in the `/uploads` directory.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Install the Local File Module
|
||||||
|
|
||||||
|
To install the Local File Provider Module, run the following command in the directory of your Medusa application:
|
||||||
|
|
||||||
|
```bash npm2yarn
|
||||||
|
npm install @medusajs/file-local-next
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, add the module into the `providers` array of the File Module:
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
|
||||||
|
The File Module accepts one provider only.
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
```js title="medusa-config.js"
|
||||||
|
module.exports = {
|
||||||
|
// ...
|
||||||
|
modules: {
|
||||||
|
// ...
|
||||||
|
[Modules.FILE]: {
|
||||||
|
resolve: "@medusajs/file",
|
||||||
|
options: {
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
resolve: "@medusajs/file-local-next",
|
||||||
|
options: {
|
||||||
|
config: {
|
||||||
|
local: {
|
||||||
|
// provider options...
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local File Module Options
|
||||||
|
|
||||||
|
<Table>
|
||||||
|
<Table.Header>
|
||||||
|
<Table.Row>
|
||||||
|
<Table.HeaderCell>Option</Table.HeaderCell>
|
||||||
|
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||||
|
<Table.HeaderCell>Default</Table.HeaderCell>
|
||||||
|
</Table.Row>
|
||||||
|
</Table.Header>
|
||||||
|
<Table.Body>
|
||||||
|
<Table.Row>
|
||||||
|
<Table.Cell>
|
||||||
|
|
||||||
|
`upload_dir`
|
||||||
|
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
|
||||||
|
The directory to upload files to.
|
||||||
|
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
|
||||||
|
`uploads`
|
||||||
|
|
||||||
|
</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
<Table.Row>
|
||||||
|
<Table.Cell>
|
||||||
|
|
||||||
|
`upload_dir`
|
||||||
|
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
|
||||||
|
The URL of the Medusa application.
|
||||||
|
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
|
||||||
|
`http://localhost:9000`
|
||||||
|
|
||||||
|
</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
</Table.Body>
|
||||||
|
</Table>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { ChildDocs } from "docs-ui"
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: `File Provider Modules`,
|
||||||
|
}
|
||||||
|
|
||||||
|
# {metadata.title}
|
||||||
|
|
||||||
|
Learn how to create a file provider module in [this guide](/references/file-provider-module).
|
||||||
|
|
||||||
|
<ChildDocs type="item" filters={["Guides"]} onlyTopLevel={true} />
|
||||||
@@ -8,4 +8,4 @@ export const metadata = {
|
|||||||
|
|
||||||
This section includes documentation for official Medusa architectural modules.
|
This section includes documentation for official Medusa architectural modules.
|
||||||
|
|
||||||
<ChildDocs />
|
<ChildDocs filters={["Guides"]} />
|
||||||
@@ -48,11 +48,15 @@ const modules = {
|
|||||||
{
|
{
|
||||||
resolve: "@medusajs/payment-stripe",
|
resolve: "@medusajs/payment-stripe",
|
||||||
options: {
|
options: {
|
||||||
credentials: {
|
config: {
|
||||||
usd: {
|
stripe: {
|
||||||
apiKey: process.env.STRIPE_USD_API_KEY,
|
credentials: {
|
||||||
},
|
usd: {
|
||||||
},
|
apiKey: process.env.STRIPE_USD_API_KEY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,22 +3,46 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/admin-widget-injection-zones/page.mdx",
|
"filePath": "/www/apps/resources/app/admin-widget-injection-zones/page.mdx",
|
||||||
"pathname": "/admin-widget-injection-zones"
|
"pathname": "/admin-widget-injection-zones"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"filePath": "/www/apps/resources/app/architectural-modules/cache/create/page.mdx",
|
||||||
|
"pathname": "/architectural-modules/cache/create"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/architectural-modules/cache/in-memory/page.mdx",
|
"filePath": "/www/apps/resources/app/architectural-modules/cache/in-memory/page.mdx",
|
||||||
"pathname": "/architectural-modules/cache/in-memory"
|
"pathname": "/architectural-modules/cache/in-memory"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"filePath": "/www/apps/resources/app/architectural-modules/cache/page.mdx",
|
||||||
|
"pathname": "/architectural-modules/cache"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/architectural-modules/cache/redis/page.mdx",
|
"filePath": "/www/apps/resources/app/architectural-modules/cache/redis/page.mdx",
|
||||||
"pathname": "/architectural-modules/cache/redis"
|
"pathname": "/architectural-modules/cache/redis"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"filePath": "/www/apps/resources/app/architectural-modules/event/create/page.mdx",
|
||||||
|
"pathname": "/architectural-modules/event/create"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/architectural-modules/event/local/page.mdx",
|
"filePath": "/www/apps/resources/app/architectural-modules/event/local/page.mdx",
|
||||||
"pathname": "/architectural-modules/event/local"
|
"pathname": "/architectural-modules/event/local"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"filePath": "/www/apps/resources/app/architectural-modules/event/page.mdx",
|
||||||
|
"pathname": "/architectural-modules/event"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/architectural-modules/event/redis/page.mdx",
|
"filePath": "/www/apps/resources/app/architectural-modules/event/redis/page.mdx",
|
||||||
"pathname": "/architectural-modules/event/redis"
|
"pathname": "/architectural-modules/event/redis"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"filePath": "/www/apps/resources/app/architectural-modules/file/local/page.mdx",
|
||||||
|
"pathname": "/architectural-modules/file/local"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filePath": "/www/apps/resources/app/architectural-modules/file/page.mdx",
|
||||||
|
"pathname": "/architectural-modules/file"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/architectural-modules/page.mdx",
|
"filePath": "/www/apps/resources/app/architectural-modules/page.mdx",
|
||||||
"pathname": "/architectural-modules"
|
"pathname": "/architectural-modules"
|
||||||
@@ -2004,12 +2028,8 @@ export const filesMap = [
|
|||||||
"pathname": "/references/customer_models/classes/customer_models.CustomerGroupCustomer"
|
"pathname": "/references/customer_models/classes/customer_models.CustomerGroupCustomer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/references/file/classes/file.AbstractFileService/page.mdx",
|
"filePath": "/www/apps/resources/references/file/classes/file.AbstractFileProviderService/page.mdx",
|
||||||
"pathname": "/references/file/classes/file.AbstractFileService"
|
"pathname": "/references/file/classes/file.AbstractFileProviderService"
|
||||||
},
|
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/references/file/interfaces/file.IFileService/page.mdx",
|
|
||||||
"pathname": "/references/file/interfaces/file.IFileService"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.cancelFulfillment/page.mdx",
|
"filePath": "/www/apps/resources/references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.cancelFulfillment/page.mdx",
|
||||||
|
|||||||
@@ -6804,6 +6804,7 @@ export const generatedSidebar = [
|
|||||||
{
|
{
|
||||||
"loaded": true,
|
"loaded": true,
|
||||||
"isPathHref": true,
|
"isPathHref": true,
|
||||||
|
"path": "/architectural-modules/cache",
|
||||||
"title": "Cache Modules",
|
"title": "Cache Modules",
|
||||||
"hasTitleStyling": true,
|
"hasTitleStyling": true,
|
||||||
"children": [
|
"children": [
|
||||||
@@ -6820,12 +6821,27 @@ export const generatedSidebar = [
|
|||||||
"path": "/architectural-modules/cache/redis",
|
"path": "/architectural-modules/cache/redis",
|
||||||
"title": "Redis",
|
"title": "Redis",
|
||||||
"children": []
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"loaded": true,
|
||||||
|
"isPathHref": true,
|
||||||
|
"title": "Guides",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"loaded": true,
|
||||||
|
"isPathHref": true,
|
||||||
|
"path": "/architectural-modules/cache/create",
|
||||||
|
"title": "Create Cache Module",
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"loaded": true,
|
"loaded": true,
|
||||||
"isPathHref": true,
|
"isPathHref": true,
|
||||||
|
"path": "/architectural-modules/event",
|
||||||
"title": "Event Modules",
|
"title": "Event Modules",
|
||||||
"hasTitleStyling": true,
|
"hasTitleStyling": true,
|
||||||
"children": [
|
"children": [
|
||||||
@@ -6842,6 +6858,50 @@ export const generatedSidebar = [
|
|||||||
"path": "/architectural-modules/event/redis",
|
"path": "/architectural-modules/event/redis",
|
||||||
"title": "Redis",
|
"title": "Redis",
|
||||||
"children": []
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"loaded": true,
|
||||||
|
"isPathHref": true,
|
||||||
|
"title": "Guides",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"loaded": true,
|
||||||
|
"isPathHref": true,
|
||||||
|
"path": "/architectural-modules/event/create",
|
||||||
|
"title": "Create Event Module",
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"loaded": true,
|
||||||
|
"isPathHref": true,
|
||||||
|
"path": "/architectural-modules/file",
|
||||||
|
"title": "File Provider Modules",
|
||||||
|
"hasTitleStyling": true,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"loaded": true,
|
||||||
|
"isPathHref": true,
|
||||||
|
"path": "/architectural-modules/file/local",
|
||||||
|
"title": "Local",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"loaded": true,
|
||||||
|
"isPathHref": true,
|
||||||
|
"title": "Guides",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"loaded": true,
|
||||||
|
"isPathHref": true,
|
||||||
|
"path": "/references/file-provider-module",
|
||||||
|
"title": "Create File Module",
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -570,9 +570,9 @@ export const slugChanges = [
|
|||||||
"filePath": "/www/apps/resources/references/customer_models/classes/customer_models.CustomerGroupCustomer/page.mdx"
|
"filePath": "/www/apps/resources/references/customer_models/classes/customer_models.CustomerGroupCustomer/page.mdx"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"origSlug": "/references/file/classes/file.AbstractFileService",
|
"origSlug": "/references/file/classes/file.AbstractFileProviderService",
|
||||||
"newSlug": "/references/file-service",
|
"newSlug": "/references/file-provider-module",
|
||||||
"filePath": "/www/apps/resources/references/file/classes/file.AbstractFileService/page.mdx"
|
"filePath": "/www/apps/resources/references/file/classes/file.AbstractFileProviderService/page.mdx"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"origSlug": "/references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.cancelFulfillment",
|
"origSlug": "/references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.cancelFulfillment",
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
---
|
||||||
|
slug: /references/file-provider-module
|
||||||
|
---
|
||||||
|
|
||||||
|
import { TypeList } from "docs-ui"
|
||||||
|
|
||||||
|
# How to Create a File Provider Module
|
||||||
|
|
||||||
|
In this document, you’ll learn how to create a file provider module and the methods you must implement in it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Create Module Directory
|
||||||
|
|
||||||
|
Start by creating a new directory for your module. For example, `src/modules/my-file`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Create the File Provider Service
|
||||||
|
|
||||||
|
Create the file `src/modules/my-file/service.ts` that holds the implementation of the file service.
|
||||||
|
|
||||||
|
The File Provider Module's main service must extend the `AbstractFileProviderService` class imported from `@medusajs/utils`:
|
||||||
|
|
||||||
|
```ts title="src/modules/my-file/service.ts"
|
||||||
|
import { AbstractFileProviderService } from "@medusajs/utils"
|
||||||
|
|
||||||
|
class MyFileProviderService extends AbstractFileProviderService {
|
||||||
|
// TODO implement methods
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyFileProviderService
|
||||||
|
```
|
||||||
|
|
||||||
|
### constructor
|
||||||
|
|
||||||
|
### getIdentifier
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
<TypeList types={[{"name":"any","type":"`any`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]} sectionTitle="getIdentifier"/>
|
||||||
|
|
||||||
|
### upload
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
<TypeList types={[{"name":"file","type":"`ProviderUploadFileDTO`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="upload"/>
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
<TypeList types={[{"name":"Promise","type":"Promise<ProviderFileResultDTO>","optional":false,"defaultValue":"","description":"","expandable":false,"children":[{"name":"ProviderFileResultDTO","type":"`ProviderFileResultDTO`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} sectionTitle="upload"/>
|
||||||
|
|
||||||
|
### delete
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
<TypeList types={[{"name":"file","type":"`ProviderDeleteFileDTO`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="delete"/>
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
<TypeList types={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]} sectionTitle="delete"/>
|
||||||
|
|
||||||
|
### getPresignedDownloadUrl
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
<TypeList types={[{"name":"fileData","type":"`ProviderGetFileDTO`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="getPresignedDownloadUrl"/>
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
<TypeList types={[{"name":"Promise","type":"Promise<string>","optional":false,"defaultValue":"","description":"","expandable":false,"children":[{"name":"string","type":"`string`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} sectionTitle="getPresignedDownloadUrl"/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Create Module Definition File
|
||||||
|
|
||||||
|
Create the file `src/modules/my-file/index.ts` with the following content:
|
||||||
|
|
||||||
|
```ts title="src/modules/my-file/index.ts"
|
||||||
|
import MyFileProviderService from "./service"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
service: MyFileProviderService,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This exports the module's definition, indicating that the `MyFileProviderService` is the main service of the module.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Use Module
|
||||||
|
|
||||||
|
To use your File Provider Module, add it to the `providers` array of the File Module:
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
|
||||||
|
The File Module accepts one provider only.
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
```js title="medusa-config.js"
|
||||||
|
module.exports = {
|
||||||
|
// ...
|
||||||
|
modules: {
|
||||||
|
// ...
|
||||||
|
[Modules.FILE]: {
|
||||||
|
resolve: "@medusajs/file",
|
||||||
|
options: {
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
resolve: "./dist/modules/my-file",
|
||||||
|
options: {
|
||||||
|
config: {
|
||||||
|
"my-file": {
|
||||||
|
// provider options...
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -1,379 +0,0 @@
|
|||||||
---
|
|
||||||
slug: /references/file-service
|
|
||||||
---
|
|
||||||
|
|
||||||
import { TypeList } from "docs-ui"
|
|
||||||
|
|
||||||
# How to Create a File Service
|
|
||||||
|
|
||||||
In this document, you’ll learn how to create a file service in the Medusa backend and the methods you must implement in it.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
A file service class is defined in a TypeScript or JavaScript file that’s created in the `src/services` directory.
|
|
||||||
The class must extend the `AbstractFileService` class imported from the `@medusajs/medusa` package.
|
|
||||||
|
|
||||||
Based on services’ naming conventions, the file’s name should be the slug version of the file service’s name
|
|
||||||
without `service`, and the class’s name should be the pascal case of the file service’s name following by `Service`.
|
|
||||||
|
|
||||||
For example, create the file `src/services/local-file.ts` with the following content:
|
|
||||||
|
|
||||||
```ts title="src/services/local-file.ts"
|
|
||||||
import { AbstractFileService } from "@medusajs/medusa"
|
|
||||||
import {
|
|
||||||
DeleteFileType,
|
|
||||||
FileServiceGetUploadStreamResult,
|
|
||||||
FileServiceUploadResult,
|
|
||||||
GetUploadedFileType,
|
|
||||||
UploadStreamDescriptorType,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
async upload(
|
|
||||||
fileData: Express.Multer.File
|
|
||||||
): Promise<FileServiceUploadResult> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
async uploadProtected(
|
|
||||||
fileData: Express.Multer.File
|
|
||||||
): Promise<FileServiceUploadResult> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
async delete(fileData: DeleteFileType): Promise<void> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
async getUploadStreamDescriptor(
|
|
||||||
fileData: UploadStreamDescriptorType
|
|
||||||
): Promise<FileServiceGetUploadStreamResult> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
async getDownloadStream(
|
|
||||||
fileData: GetUploadedFileType
|
|
||||||
): Promise<NodeJS.ReadableStream> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
async getPresignedDownloadUrl(
|
|
||||||
fileData: GetUploadedFileType
|
|
||||||
): Promise<string> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LocalFileService
|
|
||||||
```
|
|
||||||
|
|
||||||
:::note[Multer Typing]
|
|
||||||
|
|
||||||
The examples implement a file service supporting local uploads.
|
|
||||||
|
|
||||||
If you’re using TypeScript and you're following along with the implementation,
|
|
||||||
you should install the Multer types package in the root of your Medusa backend to resolve errors within your file service types:
|
|
||||||
|
|
||||||
```bash npm2yarn
|
|
||||||
npm install @types/multer
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## constructor
|
|
||||||
|
|
||||||
You can use the `constructor` of your file service to access the different services in Medusa through dependency injection.
|
|
||||||
|
|
||||||
You can also use the constructor to initialize your integration with the third-party provider. For example, if you use a client to connect to the third-party provider’s APIs,
|
|
||||||
you can initialize it in the constructor and use it in other methods in the service.
|
|
||||||
|
|
||||||
Additionally, if you’re creating your file service as an external plugin to be installed on any Medusa backend and you want to access the options added for the plugin,
|
|
||||||
you can access them in the constructor.
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// ...
|
|
||||||
import { Logger } from "@medusajs/medusa"
|
|
||||||
import * as fs from "fs"
|
|
||||||
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
// can also be replaced by an environment variable
|
|
||||||
// or a plugin option
|
|
||||||
protected serverUrl = "http://localhost:9000"
|
|
||||||
protected publicPath = "uploads"
|
|
||||||
protected protectedPath = "protected-uploads"
|
|
||||||
protected logger_: Logger
|
|
||||||
|
|
||||||
constructor({ logger }: InjectedDependencies) {
|
|
||||||
// @ts-ignore
|
|
||||||
super(...arguments)
|
|
||||||
this.logger_ = logger
|
|
||||||
|
|
||||||
// for public uploads
|
|
||||||
if (!fs.existsSync(this.publicPath)) {
|
|
||||||
fs.mkdirSync(this.publicPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// for protected uploads
|
|
||||||
if (!fs.existsSync(this.protectedPath)) {
|
|
||||||
fs.mkdirSync(this.protectedPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"container","type":"`Record<string, unknown>`","description":"An instance of `MedusaContainer` that allows you to access other resources, such as services, in your Medusa backend.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"config","type":"`Record<string, unknown>`","description":"If this file service is created in a plugin, the plugin's options are passed in this parameter.","optional":true,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="new AbstractFileService"/>
|
|
||||||
|
|
||||||
___
|
|
||||||
|
|
||||||
## Methods
|
|
||||||
|
|
||||||
### upload
|
|
||||||
|
|
||||||
This method is used to upload a file to the Medusa backend.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
// ...
|
|
||||||
async upload(
|
|
||||||
fileData: Express.Multer.File
|
|
||||||
): Promise<FileServiceUploadResult> {
|
|
||||||
const filePath =
|
|
||||||
`${this.publicPath}/${fileData.originalname}`
|
|
||||||
fs.copyFileSync(fileData.path, filePath)
|
|
||||||
return {
|
|
||||||
url: `${this.serverUrl}/${filePath}`,
|
|
||||||
key: filePath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
:::tip
|
|
||||||
|
|
||||||
This example does not account for duplicate names to maintain simplicity in this guide. So, an uploaded file can replace another existing file that has the same name.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"fileData","type":"`File`","description":"A [multer file object](http://expressjs.com/en/resources/middleware/multer.html#file-information).\nThe file is uploaded to a temporary directory by default. Among the file’s details, you can access the file’s path in the `path` property of the file object.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="upload"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)>","optional":false,"defaultValue":"","description":"The details of the upload's result.","expandable":false,"children":[{"name":"FileServiceUploadResult","type":"[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)","optional":false,"defaultValue":"","description":"Details of a file upload's result.","expandable":false,"children":[{"name":"url","type":"`string`","description":"The file's URL.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"key","type":"`string`","description":"The file's key. This key is used in other operations,\nsuch as deleting a file.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]}]} sectionTitle="upload"/>
|
|
||||||
|
|
||||||
### uploadProtected
|
|
||||||
|
|
||||||
This method is used to upload a file to the Medusa backend, but to a protected storage. Typically, this would be used to store files that
|
|
||||||
shouldn’t be accessible by using the file’s URL or should only be accessible by authenticated users. For example, exported or imported
|
|
||||||
CSV files.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
// ...
|
|
||||||
async uploadProtected(
|
|
||||||
fileData: Express.Multer.File
|
|
||||||
): Promise<FileServiceUploadResult> {
|
|
||||||
const filePath =
|
|
||||||
`${this.protectedPath}/${fileData.originalname}`
|
|
||||||
fs.copyFileSync(fileData.path, filePath)
|
|
||||||
return {
|
|
||||||
url: `${this.serverUrl}/${filePath}`,
|
|
||||||
key: filePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
:::tip
|
|
||||||
|
|
||||||
This example does not account for duplicate names to maintain simplicity in this guide. So, an uploaded file can replace another existing file that has the same name.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"fileData","type":"`File`","description":"A [multer file object](http://expressjs.com/en/resources/middleware/multer.html#file-information).\nThe file is uploaded to a temporary directory by default. Among the file’s details, you can access the file’s path in the `path` property of the file object.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="uploadProtected"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)>","optional":false,"defaultValue":"","description":"The details of the upload's result.","expandable":false,"children":[{"name":"FileServiceUploadResult","type":"[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)","optional":false,"defaultValue":"","description":"Details of a file upload's result.","expandable":false,"children":[{"name":"url","type":"`string`","description":"The file's URL.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"key","type":"`string`","description":"The file's key. This key is used in other operations,\nsuch as deleting a file.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]}]} sectionTitle="uploadProtected"/>
|
|
||||||
|
|
||||||
### delete
|
|
||||||
|
|
||||||
This method is used to delete a file from storage.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
|
|
||||||
async delete(
|
|
||||||
fileData: DeleteFileType
|
|
||||||
): Promise<void> {
|
|
||||||
fs.rmSync(fileData.fileKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"fileData","type":"[DeleteFileType](../../../types/interfaces/types.DeleteFileType/page.mdx)","description":"The details of the file to remove.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"fileKey","type":"`string`","description":"The file's key. When uploading a file, the\nreturned key is used here.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]} sectionTitle="delete"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"Resolves when the file is deleted successfully.","expandable":false,"children":[]}]} sectionTitle="delete"/>
|
|
||||||
|
|
||||||
### getUploadStreamDescriptor
|
|
||||||
|
|
||||||
This method is used to retrieve a write stream to be used to upload a file.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// ...
|
|
||||||
import { Stream } from "stream"
|
|
||||||
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
// ...
|
|
||||||
async getUploadStreamDescriptor({
|
|
||||||
name,
|
|
||||||
ext,
|
|
||||||
isPrivate = true,
|
|
||||||
}: UploadStreamDescriptorType
|
|
||||||
): Promise<FileServiceGetUploadStreamResult> {
|
|
||||||
const filePath = `${isPrivate ?
|
|
||||||
this.publicPath : this.protectedPath
|
|
||||||
}/${name}.${ext}`
|
|
||||||
|
|
||||||
const pass = new Stream.PassThrough()
|
|
||||||
const writeStream = fs.createWriteStream(filePath)
|
|
||||||
|
|
||||||
pass.pipe(writeStream)
|
|
||||||
|
|
||||||
return {
|
|
||||||
writeStream: pass,
|
|
||||||
promise: Promise.resolve(),
|
|
||||||
url: `${this.serverUrl}/${filePath}`,
|
|
||||||
fileKey: filePath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"fileData","type":"[UploadStreamDescriptorType](../../../types/interfaces/types.UploadStreamDescriptorType/page.mdx)","description":"The details of the file being uploaded.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"name","type":"`string`","description":"The name of the file.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"ext","type":"`string`","description":"The extension of the file.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isPrivate","type":"`boolean`","description":"Whether the file should be uploaded to a private bucket or location. By convention, the default value of this property is `true`.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} sectionTitle="getUploadStreamDescriptor"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<[FileServiceGetUploadStreamResult](../../../types/interfaces/types.FileServiceGetUploadStreamResult/page.mdx)>","optional":false,"defaultValue":"","description":"The result of the file-stream upload.","expandable":false,"children":[{"name":"FileServiceGetUploadStreamResult","type":"[FileServiceGetUploadStreamResult](../../../types/interfaces/types.FileServiceGetUploadStreamResult/page.mdx)","optional":false,"defaultValue":"","description":"The relevant details to upload a file through a stream.","expandable":false,"children":[{"name":"writeStream","type":"`PassThrough`","description":"A [PassThrough](https://nodejs.org/api/stream.html#class-streampassthrough) write stream object to be used to write the file.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"promise","type":"Promise<any>","description":"A promise that should resolved when the writing process is done to finish the upload.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"url","type":"`string`","description":"The URL of the file once it’s uploaded.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"fileKey","type":"`string`","description":"The identifier of the file in the storage. For example, for a local file service, this can be the file's name.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]}]} sectionTitle="getUploadStreamDescriptor"/>
|
|
||||||
|
|
||||||
### getDownloadStream
|
|
||||||
|
|
||||||
This method is used to retrieve a read stream for a file, which can then be used to download the file.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
|
|
||||||
async getDownloadStream({
|
|
||||||
fileKey,
|
|
||||||
isPrivate = true,
|
|
||||||
}: GetUploadedFileType
|
|
||||||
): Promise<NodeJS.ReadableStream> {
|
|
||||||
const filePath = `${isPrivate ?
|
|
||||||
this.publicPath : this.protectedPath
|
|
||||||
}/${fileKey}`
|
|
||||||
const readStream = fs.createReadStream(filePath)
|
|
||||||
|
|
||||||
return readStream
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"fileData","type":"[GetUploadedFileType](../../../types/interfaces/types.GetUploadedFileType/page.mdx)","description":"The details of the file.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"fileKey","type":"`string`","description":"The file's key.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"isPrivate","type":"`boolean`","description":"Whether the file is private.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} sectionTitle="getDownloadStream"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<ReadableStream>","optional":false,"defaultValue":"","description":"The [read stream](https://nodejs.org/api/webstreams.html#class-readablestream) to read and download the file.","expandable":false,"children":[{"name":"ReadableStream","type":"`ReadableStream`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} sectionTitle="getDownloadStream"/>
|
|
||||||
|
|
||||||
### getPresignedDownloadUrl
|
|
||||||
|
|
||||||
This method is used to retrieve a download URL of the file. For some file services, such as S3, a presigned URL indicates a temporary URL to get access to a file.
|
|
||||||
|
|
||||||
If your file service doesn’t perform or offer a similar functionality, you can just return the URL to download the file.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
|
|
||||||
async getPresignedDownloadUrl({
|
|
||||||
fileKey,
|
|
||||||
isPrivate = true,
|
|
||||||
}: GetUploadedFileType
|
|
||||||
): Promise<string> {
|
|
||||||
// Local upload doesn't provide
|
|
||||||
// support for presigned URLs,
|
|
||||||
// so just return the file's URL.
|
|
||||||
|
|
||||||
const filePath = `${isPrivate ?
|
|
||||||
this.publicPath : this.protectedPath
|
|
||||||
}/${fileKey}`
|
|
||||||
return `${this.serverUrl}/${filePath}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"fileData","type":"[GetUploadedFileType](../../../types/interfaces/types.GetUploadedFileType/page.mdx)","description":"The details of the file.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"fileKey","type":"`string`","description":"The file's key.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"isPrivate","type":"`boolean`","description":"Whether the file is private.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} sectionTitle="getPresignedDownloadUrl"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<string>","optional":false,"defaultValue":"","description":"The presigned URL to download the file","expandable":false,"children":[{"name":"string","type":"`string`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} sectionTitle="getPresignedDownloadUrl"/>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Test Implementation
|
|
||||||
|
|
||||||
:::note
|
|
||||||
|
|
||||||
If you created your file service in a plugin, refer to [this guide on how to test plugins](https://docs.medusajs.com/development/plugins/create#test-your-plugin).
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
After finishing your file service implementation:
|
|
||||||
|
|
||||||
1\. Run the `build` command in the root of your Medusa backend:
|
|
||||||
|
|
||||||
```bash npm2yarn
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
2\. Start the backend with the `develop` command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx medusa develop
|
|
||||||
```
|
|
||||||
|
|
||||||
3\. Upload a file using the [Admin REST APIs](https://docs.medusajs.com/api/admin#uploads_postuploads) or using the Medusa admin, for example, to [upload a product's thumbnail](https://docs.medusajs.com/user-guide/products/manage#manage-thumbnails).
|
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
---
|
|
||||||
displayed_sidebar: core
|
|
||||||
---
|
|
||||||
|
|
||||||
import { TypeList } from "docs-ui"
|
|
||||||
|
|
||||||
# IFileService
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
A file service class is defined in a TypeScript or JavaScript file that’s created in the `src/services` directory.
|
|
||||||
The class must extend the `AbstractFileService` class imported from the `@medusajs/medusa` package.
|
|
||||||
|
|
||||||
Based on services’ naming conventions, the file’s name should be the slug version of the file service’s name
|
|
||||||
without `service`, and the class’s name should be the pascal case of the file service’s name following by `Service`.
|
|
||||||
|
|
||||||
For example, create the file `src/services/local-file.ts` with the following content:
|
|
||||||
|
|
||||||
```ts title="src/services/local-file.ts"
|
|
||||||
import { AbstractFileService } from "@medusajs/medusa"
|
|
||||||
import {
|
|
||||||
DeleteFileType,
|
|
||||||
FileServiceGetUploadStreamResult,
|
|
||||||
FileServiceUploadResult,
|
|
||||||
GetUploadedFileType,
|
|
||||||
UploadStreamDescriptorType,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
async upload(
|
|
||||||
fileData: Express.Multer.File
|
|
||||||
): Promise<FileServiceUploadResult> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
async uploadProtected(
|
|
||||||
fileData: Express.Multer.File
|
|
||||||
): Promise<FileServiceUploadResult> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
async delete(fileData: DeleteFileType): Promise<void> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
async getUploadStreamDescriptor(
|
|
||||||
fileData: UploadStreamDescriptorType
|
|
||||||
): Promise<FileServiceGetUploadStreamResult> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
async getDownloadStream(
|
|
||||||
fileData: GetUploadedFileType
|
|
||||||
): Promise<NodeJS.ReadableStream> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
async getPresignedDownloadUrl(
|
|
||||||
fileData: GetUploadedFileType
|
|
||||||
): Promise<string> {
|
|
||||||
throw new Error("Method not implemented.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LocalFileService
|
|
||||||
```
|
|
||||||
|
|
||||||
:::note[Multer Typing]
|
|
||||||
|
|
||||||
The examples implement a file service supporting local uploads.
|
|
||||||
|
|
||||||
If you’re using TypeScript and you're following along with the implementation,
|
|
||||||
you should install the Multer types package in the root of your Medusa backend to resolve errors within your file service types:
|
|
||||||
|
|
||||||
```bash npm2yarn
|
|
||||||
npm install @types/multer
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"manager_","type":"`EntityManager`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionManager_","type":"`undefined` \\| `EntityManager`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"__container__","type":"`any`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"__configModule__","type":"`Record<string, unknown>`","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"__moduleDeclaration__","type":"`Record<string, unknown>`","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="IFileService"/>
|
|
||||||
|
|
||||||
___
|
|
||||||
|
|
||||||
## Accessors
|
|
||||||
|
|
||||||
### activeManager\_
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"EntityManager","type":"`EntityManager`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]} sectionTitle="activeManager_"/>
|
|
||||||
|
|
||||||
___
|
|
||||||
|
|
||||||
## Methods
|
|
||||||
|
|
||||||
### upload
|
|
||||||
|
|
||||||
This method is used to upload a file to the Medusa backend.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
// ...
|
|
||||||
async upload(
|
|
||||||
fileData: Express.Multer.File
|
|
||||||
): Promise<FileServiceUploadResult> {
|
|
||||||
const filePath =
|
|
||||||
`${this.publicPath}/${fileData.originalname}`
|
|
||||||
fs.copyFileSync(fileData.path, filePath)
|
|
||||||
return {
|
|
||||||
url: `${this.serverUrl}/${filePath}`,
|
|
||||||
key: filePath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
:::tip
|
|
||||||
|
|
||||||
This example does not account for duplicate names to maintain simplicity in this guide. So, an uploaded file can replace another existing file that has the same name.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"file","type":"`File`","description":"A [multer file object](http://expressjs.com/en/resources/middleware/multer.html#file-information).\nThe file is uploaded to a temporary directory by default. Among the file’s details, you can access the file’s path in the `path` property of the file object.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="upload"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)>","optional":false,"defaultValue":"","description":"The details of the upload's result.","expandable":false,"children":[{"name":"FileServiceUploadResult","type":"[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)","optional":false,"defaultValue":"","description":"Details of a file upload's result.","expandable":false,"children":[{"name":"url","type":"`string`","description":"The file's URL.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"key","type":"`string`","description":"The file's key. This key is used in other operations,\nsuch as deleting a file.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]}]} sectionTitle="upload"/>
|
|
||||||
|
|
||||||
### uploadProtected
|
|
||||||
|
|
||||||
This method is used to upload a file to the Medusa backend, but to a protected storage. Typically, this would be used to store files that
|
|
||||||
shouldn’t be accessible by using the file’s URL or should only be accessible by authenticated users. For example, exported or imported
|
|
||||||
CSV files.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
// ...
|
|
||||||
async uploadProtected(
|
|
||||||
fileData: Express.Multer.File
|
|
||||||
): Promise<FileServiceUploadResult> {
|
|
||||||
const filePath =
|
|
||||||
`${this.protectedPath}/${fileData.originalname}`
|
|
||||||
fs.copyFileSync(fileData.path, filePath)
|
|
||||||
return {
|
|
||||||
url: `${this.serverUrl}/${filePath}`,
|
|
||||||
key: filePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
:::tip
|
|
||||||
|
|
||||||
This example does not account for duplicate names to maintain simplicity in this guide. So, an uploaded file can replace another existing file that has the same name.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"file","type":"`File`","description":"A [multer file object](http://expressjs.com/en/resources/middleware/multer.html#file-information).\nThe file is uploaded to a temporary directory by default. Among the file’s details, you can access the file’s path in the `path` property of the file object.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="uploadProtected"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)>","optional":false,"defaultValue":"","description":"The details of the upload's result.","expandable":false,"children":[{"name":"FileServiceUploadResult","type":"[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)","optional":false,"defaultValue":"","description":"Details of a file upload's result.","expandable":false,"children":[{"name":"url","type":"`string`","description":"The file's URL.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"key","type":"`string`","description":"The file's key. This key is used in other operations,\nsuch as deleting a file.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]}]} sectionTitle="uploadProtected"/>
|
|
||||||
|
|
||||||
### delete
|
|
||||||
|
|
||||||
This method is used to delete a file from storage.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
|
|
||||||
async delete(
|
|
||||||
fileData: DeleteFileType
|
|
||||||
): Promise<void> {
|
|
||||||
fs.rmSync(fileData.fileKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"fileData","type":"[DeleteFileType](../../../types/interfaces/types.DeleteFileType/page.mdx)","description":"The details of the file to remove.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"fileKey","type":"`string`","description":"The file's key. When uploading a file, the\nreturned key is used here.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]} sectionTitle="delete"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"Resolves when the file is deleted successfully.","expandable":false,"children":[]}]} sectionTitle="delete"/>
|
|
||||||
|
|
||||||
### getUploadStreamDescriptor
|
|
||||||
|
|
||||||
This method is used to retrieve a write stream to be used to upload a file.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// ...
|
|
||||||
import { Stream } from "stream"
|
|
||||||
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
// ...
|
|
||||||
async getUploadStreamDescriptor({
|
|
||||||
name,
|
|
||||||
ext,
|
|
||||||
isPrivate = true,
|
|
||||||
}: UploadStreamDescriptorType
|
|
||||||
): Promise<FileServiceGetUploadStreamResult> {
|
|
||||||
const filePath = `${isPrivate ?
|
|
||||||
this.publicPath : this.protectedPath
|
|
||||||
}/${name}.${ext}`
|
|
||||||
|
|
||||||
const pass = new Stream.PassThrough()
|
|
||||||
const writeStream = fs.createWriteStream(filePath)
|
|
||||||
|
|
||||||
pass.pipe(writeStream)
|
|
||||||
|
|
||||||
return {
|
|
||||||
writeStream: pass,
|
|
||||||
promise: Promise.resolve(),
|
|
||||||
url: `${this.serverUrl}/${filePath}`,
|
|
||||||
fileKey: filePath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"fileData","type":"[UploadStreamDescriptorType](../../../types/interfaces/types.UploadStreamDescriptorType/page.mdx)","description":"The details of the file being uploaded.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"name","type":"`string`","description":"The name of the file.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"ext","type":"`string`","description":"The extension of the file.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isPrivate","type":"`boolean`","description":"Whether the file should be uploaded to a private bucket or location. By convention, the default value of this property is `true`.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} sectionTitle="getUploadStreamDescriptor"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<[FileServiceGetUploadStreamResult](../../../types/interfaces/types.FileServiceGetUploadStreamResult/page.mdx)>","optional":false,"defaultValue":"","description":"The result of the file-stream upload.","expandable":false,"children":[{"name":"FileServiceGetUploadStreamResult","type":"[FileServiceGetUploadStreamResult](../../../types/interfaces/types.FileServiceGetUploadStreamResult/page.mdx)","optional":false,"defaultValue":"","description":"The relevant details to upload a file through a stream.","expandable":false,"children":[{"name":"writeStream","type":"`PassThrough`","description":"A [PassThrough](https://nodejs.org/api/stream.html#class-streampassthrough) write stream object to be used to write the file.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"promise","type":"Promise<any>","description":"A promise that should resolved when the writing process is done to finish the upload.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"url","type":"`string`","description":"The URL of the file once it’s uploaded.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"fileKey","type":"`string`","description":"The identifier of the file in the storage. For example, for a local file service, this can be the file's name.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]}]} sectionTitle="getUploadStreamDescriptor"/>
|
|
||||||
|
|
||||||
### getDownloadStream
|
|
||||||
|
|
||||||
This method is used to retrieve a read stream for a file, which can then be used to download the file.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
|
|
||||||
async getDownloadStream({
|
|
||||||
fileKey,
|
|
||||||
isPrivate = true,
|
|
||||||
}: GetUploadedFileType
|
|
||||||
): Promise<NodeJS.ReadableStream> {
|
|
||||||
const filePath = `${isPrivate ?
|
|
||||||
this.publicPath : this.protectedPath
|
|
||||||
}/${fileKey}`
|
|
||||||
const readStream = fs.createReadStream(filePath)
|
|
||||||
|
|
||||||
return readStream
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"fileData","type":"[GetUploadedFileType](../../../types/interfaces/types.GetUploadedFileType/page.mdx)","description":"The details of the file.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"fileKey","type":"`string`","description":"The file's key.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"isPrivate","type":"`boolean`","description":"Whether the file is private.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} sectionTitle="getDownloadStream"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<ReadableStream>","optional":false,"defaultValue":"","description":"The [read stream](https://nodejs.org/api/webstreams.html#class-readablestream) to read and download the file.","expandable":false,"children":[{"name":"ReadableStream","type":"`ReadableStream`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} sectionTitle="getDownloadStream"/>
|
|
||||||
|
|
||||||
### getPresignedDownloadUrl
|
|
||||||
|
|
||||||
This method is used to retrieve a download URL of the file. For some file services, such as S3, a presigned URL indicates a temporary URL to get access to a file.
|
|
||||||
|
|
||||||
If your file service doesn’t perform or offer a similar functionality, you can just return the URL to download the file.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class LocalFileService extends AbstractFileService {
|
|
||||||
|
|
||||||
async getPresignedDownloadUrl({
|
|
||||||
fileKey,
|
|
||||||
isPrivate = true,
|
|
||||||
}: GetUploadedFileType
|
|
||||||
): Promise<string> {
|
|
||||||
// Local upload doesn't provide
|
|
||||||
// support for presigned URLs,
|
|
||||||
// so just return the file's URL.
|
|
||||||
|
|
||||||
const filePath = `${isPrivate ?
|
|
||||||
this.publicPath : this.protectedPath
|
|
||||||
}/${fileKey}`
|
|
||||||
return `${this.serverUrl}/${filePath}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"fileData","type":"[GetUploadedFileType](../../../types/interfaces/types.GetUploadedFileType/page.mdx)","description":"The details of the file.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"fileKey","type":"`string`","description":"The file's key.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"isPrivate","type":"`boolean`","description":"Whether the file is private.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} sectionTitle="getPresignedDownloadUrl"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<string>","optional":false,"defaultValue":"","description":"The presigned URL to download the file","expandable":false,"children":[{"name":"string","type":"`string`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} sectionTitle="getPresignedDownloadUrl"/>
|
|
||||||
|
|
||||||
### withTransaction
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"transactionManager","type":"`EntityManager`","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="withTransaction"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"this","type":"`this`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]} sectionTitle="withTransaction"/>
|
|
||||||
|
|
||||||
### shouldRetryTransaction\_
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"err","type":"`Record<string, unknown>` \\| `object`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="shouldRetryTransaction_"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"boolean","type":"`boolean`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]} sectionTitle="shouldRetryTransaction_"/>
|
|
||||||
|
|
||||||
### atomicPhase\_
|
|
||||||
|
|
||||||
Wraps some work within a transactional block. If the service already has
|
|
||||||
a transaction manager attached this will be reused, otherwise a new
|
|
||||||
transaction manager is created.
|
|
||||||
|
|
||||||
#### Type Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"TResult","type":"`object`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"TError","type":"`object`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="atomicPhase_"/>
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"work","type":"(`transactionManager`: `EntityManager`) => Promise<TResult>","description":"the transactional work to be done","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationOrErrorHandler","type":"`IsolationLevel` \\| (`error`: TError) => Promise<void \\| TResult>","description":"the isolation level to be used for the work.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"maybeErrorHandlerOrDontFail","type":"(`error`: TError) => Promise<void \\| TResult>","description":"Potential error handler","optional":true,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="atomicPhase_"/>
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
<TypeList types={[{"name":"Promise","type":"Promise<TResult>","optional":false,"defaultValue":"","description":"the result of the transactional work","expandable":false,"children":[{"name":"TResult","type":"TResult","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} sectionTitle="atomicPhase_"/>
|
|
||||||
@@ -4,8 +4,4 @@ import { TypeList } from "docs-ui"
|
|||||||
|
|
||||||
## Classes
|
## Classes
|
||||||
|
|
||||||
- [AbstractFileService](../../file/classes/file.AbstractFileService/page.mdx)
|
- [AbstractFileProviderService](../../file/classes/file.AbstractFileProviderService/page.mdx)
|
||||||
|
|
||||||
## Interfaces
|
|
||||||
|
|
||||||
- [IFileService](../../file/interfaces/file.IFileService/page.mdx)
|
|
||||||
@@ -1592,6 +1592,7 @@ export const sidebar = sidebarAttachHrefCommonOptions([
|
|||||||
hasTitleStyling: true,
|
hasTitleStyling: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
path: "/architectural-modules/cache",
|
||||||
title: "Cache Modules",
|
title: "Cache Modules",
|
||||||
hasTitleStyling: true,
|
hasTitleStyling: true,
|
||||||
children: [
|
children: [
|
||||||
@@ -1603,9 +1604,19 @@ export const sidebar = sidebarAttachHrefCommonOptions([
|
|||||||
path: "/architectural-modules/cache/redis",
|
path: "/architectural-modules/cache/redis",
|
||||||
title: "Redis",
|
title: "Redis",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Guides",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/architectural-modules/cache/create",
|
||||||
|
title: "Create Cache Module",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
path: "/architectural-modules/event",
|
||||||
title: "Event Modules",
|
title: "Event Modules",
|
||||||
hasTitleStyling: true,
|
hasTitleStyling: true,
|
||||||
children: [
|
children: [
|
||||||
@@ -1617,6 +1628,35 @@ export const sidebar = sidebarAttachHrefCommonOptions([
|
|||||||
path: "/architectural-modules/event/redis",
|
path: "/architectural-modules/event/redis",
|
||||||
title: "Redis",
|
title: "Redis",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Guides",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/architectural-modules/event/create",
|
||||||
|
title: "Create Event Module",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/architectural-modules/file",
|
||||||
|
title: "File Provider Modules",
|
||||||
|
hasTitleStyling: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/architectural-modules/file/local",
|
||||||
|
title: "Local",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Guides",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/references/file-provider-module",
|
||||||
|
title: "Create File Module",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,15 +1,59 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React from "react"
|
import React, { useMemo } from "react"
|
||||||
import { Card, CardList, MDXComponents, useSidebar } from "../.."
|
import { Card, CardList, MDXComponents, useSidebar } from "../.."
|
||||||
import { SidebarItemType } from "types"
|
import { SidebarItemType } from "types"
|
||||||
|
|
||||||
type ChildDocsProps = {
|
type ChildDocsProps = {
|
||||||
onlyTopLevel?: boolean
|
onlyTopLevel?: boolean
|
||||||
|
type?: "sidebar" | "item"
|
||||||
|
filters?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChildDocs = ({ onlyTopLevel = false }: ChildDocsProps) => {
|
export const ChildDocs = ({
|
||||||
const { currentItems } = useSidebar()
|
onlyTopLevel = false,
|
||||||
|
filters = [],
|
||||||
|
type = "sidebar",
|
||||||
|
}: ChildDocsProps) => {
|
||||||
|
const { currentItems, getActiveItem } = useSidebar()
|
||||||
|
|
||||||
|
const filterItems = (items: SidebarItemType[]): SidebarItemType[] => {
|
||||||
|
return items
|
||||||
|
.filter(
|
||||||
|
(item) =>
|
||||||
|
(!item.path || !filters.includes(item.path)) &&
|
||||||
|
!filters.includes(item.title)
|
||||||
|
)
|
||||||
|
.map((item) => Object.assign({}, item))
|
||||||
|
.map((item) => {
|
||||||
|
if (item.children) {
|
||||||
|
item.children = filterItems(item.children)
|
||||||
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredItems = useMemo(() => {
|
||||||
|
const targetItems =
|
||||||
|
type === "sidebar"
|
||||||
|
? currentItems
|
||||||
|
? Object.assign({}, currentItems)
|
||||||
|
: undefined
|
||||||
|
: {
|
||||||
|
top: [...(getActiveItem()?.children || [])],
|
||||||
|
bottom: [],
|
||||||
|
}
|
||||||
|
if (!filters.length || !targetItems) {
|
||||||
|
return targetItems
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...targetItems,
|
||||||
|
top: filterItems(targetItems.top),
|
||||||
|
bottom: filterItems(targetItems.bottom),
|
||||||
|
}
|
||||||
|
}, [currentItems, type, getActiveItem])
|
||||||
|
|
||||||
const getTopLevelElms = (items?: SidebarItemType[]) => (
|
const getTopLevelElms = (items?: SidebarItemType[]) => (
|
||||||
<CardList
|
<CardList
|
||||||
@@ -58,8 +102,8 @@ export const ChildDocs = ({ onlyTopLevel = false }: ChildDocsProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{getElms(currentItems?.top)}
|
{getElms(filteredItems?.top)}
|
||||||
{getElms(currentItems?.bottom)}
|
{getElms(filteredItems?.bottom)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -16,8 +16,8 @@ const customOptions: Record<string, Partial<TypeDocOptions>> = {
|
|||||||
name: "entities",
|
name: "entities",
|
||||||
}),
|
}),
|
||||||
file: getOptions({
|
file: getOptions({
|
||||||
entryPointPath: "packages/medusa/src/interfaces/file-service.ts",
|
entryPointPath: "packages/core/utils/src/file/abstract-file-provider.ts",
|
||||||
tsConfigName: "medusa.json",
|
tsConfigName: "utils.json",
|
||||||
name: "file",
|
name: "file",
|
||||||
parentIgnore: true,
|
parentIgnore: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
+68
-21
@@ -6,42 +6,89 @@ const fileOptions: FormattingOptionsType = {
|
|||||||
displayed_sidebar: "core",
|
displayed_sidebar: "core",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"^file/.*AbstractFileService": {
|
"^file/.*AbstractFileProviderService": {
|
||||||
reflectionGroups: {
|
reflectionGroups: {
|
||||||
Properties: false,
|
Properties: false,
|
||||||
},
|
},
|
||||||
reflectionDescription: `In this document, you’ll learn how to create a file service in the Medusa backend and the methods you must implement in it.`,
|
reflectionDescription: `In this document, you’ll learn how to create a file provider module and the methods you must implement in it.`,
|
||||||
frontmatterData: {
|
frontmatterData: {
|
||||||
slug: "/references/file-service",
|
slug: "/references/file-provider-module",
|
||||||
},
|
},
|
||||||
reflectionTitle: {
|
reflectionTitle: {
|
||||||
fullReplacement: "How to Create a File Service",
|
fullReplacement: "How to Create a File Provider Module",
|
||||||
},
|
},
|
||||||
|
shouldIncrementAfterStartSections: true,
|
||||||
|
expandMembers: true,
|
||||||
|
startSections: [
|
||||||
|
`## 1. Create Module Directory
|
||||||
|
|
||||||
|
Start by creating a new directory for your module. For example, \`src/modules/my-file\`.`,
|
||||||
|
`## 2. Create the File Provider Service
|
||||||
|
|
||||||
|
Create the file \`src/modules/my-file/service.ts\` that holds the implementation of the file service.
|
||||||
|
|
||||||
|
The File Provider Module's main service must extend the \`AbstractFileProviderService\` class imported from \`@medusajs/utils\`:
|
||||||
|
|
||||||
|
\`\`\`ts title="src/modules/my-file/service.ts"
|
||||||
|
import { AbstractFileProviderService } from "@medusajs/utils"
|
||||||
|
|
||||||
|
class MyFileProviderService extends AbstractFileProviderService {
|
||||||
|
// TODO implement methods
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyFileProviderService
|
||||||
|
\`\`\``,
|
||||||
|
],
|
||||||
endSections: [
|
endSections: [
|
||||||
`## Test Implementation
|
`## 3. Create Module Definition File
|
||||||
|
|
||||||
:::note
|
Create the file \`src/modules/my-file/index.ts\` with the following content:
|
||||||
|
|
||||||
If you created your file service in a plugin, refer to [this guide on how to test plugins](https://docs.medusajs.com/development/plugins/create#test-your-plugin).
|
\`\`\`ts title="src/modules/my-file/index.ts"
|
||||||
|
import MyFileProviderService from "./service"
|
||||||
|
|
||||||
:::
|
export default {
|
||||||
|
service: MyFileProviderService,
|
||||||
After finishing your file service implementation:
|
}
|
||||||
|
|
||||||
1\\. Run the \`build\` command in the root of your Medusa backend:
|
|
||||||
|
|
||||||
\`\`\`bash npm2yarn
|
|
||||||
npm run build
|
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
2\\. Start the backend with the \`develop\` command:
|
This exports the module's definition, indicating that the \`MyFileProviderService\` is the main service of the module.`,
|
||||||
|
`## 4. Use Module
|
||||||
|
|
||||||
\`\`\`bash
|
To use your File Provider Module, add it to the \`providers\` array of the File Module:
|
||||||
npx medusa develop
|
|
||||||
|
<Note>
|
||||||
|
|
||||||
|
The File Module accepts one provider only.
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
\`\`\`js title="medusa-config.js"
|
||||||
|
module.exports = {
|
||||||
|
// ...
|
||||||
|
modules: {
|
||||||
|
// ...
|
||||||
|
[Modules.FILE]: {
|
||||||
|
resolve: "@medusajs/file",
|
||||||
|
options: {
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
resolve: "./dist/modules/my-file",
|
||||||
|
options: {
|
||||||
|
config: {
|
||||||
|
"my-file": {
|
||||||
|
// provider options...
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
`,
|
||||||
3\\. Upload a file using the [Admin REST APIs](https://docs.medusajs.com/api/admin#uploads_postuploads) or using the Medusa admin, for example, to [upload a product's thumbnail](https://docs.medusajs.com/user-guide/products/manage#manage-thumbnails).
|
|
||||||
`,
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import getDeclarationChildrenHelper from "./resources/helpers/get-declaration-ch
|
|||||||
import ifShowSeparatorForTitleLevelHelper from "./resources/helpers/if-show-separator-for-title-level"
|
import ifShowSeparatorForTitleLevelHelper from "./resources/helpers/if-show-separator-for-title-level"
|
||||||
import shouldExpandPropertiesHelper from "./resources/helpers/should-expand-properties"
|
import shouldExpandPropertiesHelper from "./resources/helpers/should-expand-properties"
|
||||||
import shouldExpandDeclarationChildrenHelper from "./resources/helpers/should-expand-declaration-children"
|
import shouldExpandDeclarationChildrenHelper from "./resources/helpers/should-expand-declaration-children"
|
||||||
|
import startSectionsHelper from "./resources/helpers/start-sections"
|
||||||
import { MarkdownTheme } from "./theme"
|
import { MarkdownTheme } from "./theme"
|
||||||
|
|
||||||
const TEMPLATE_PATH = path.join(__dirname, "resources", "templates")
|
const TEMPLATE_PATH = path.join(__dirname, "resources", "templates")
|
||||||
@@ -154,4 +155,5 @@ export function registerHelpers(theme: MarkdownTheme) {
|
|||||||
ifShowSeparatorForTitleLevelHelper(theme)
|
ifShowSeparatorForTitleLevelHelper(theme)
|
||||||
shouldExpandPropertiesHelper(theme)
|
shouldExpandPropertiesHelper(theme)
|
||||||
shouldExpandDeclarationChildrenHelper(theme)
|
shouldExpandDeclarationChildrenHelper(theme)
|
||||||
|
startSectionsHelper(theme)
|
||||||
}
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
import * as Handlebars from "handlebars"
|
||||||
|
import { MarkdownTheme } from "../../theme"
|
||||||
|
|
||||||
|
export default function (theme: MarkdownTheme) {
|
||||||
|
Handlebars.registerHelper("startSections", function () {
|
||||||
|
const { startSections, shouldIncrementAfterStartSections } =
|
||||||
|
theme.getFormattingOptionsForLocation()
|
||||||
|
|
||||||
|
if (!startSections?.length) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const lineBreaks = "\n\n"
|
||||||
|
const separator = `---${lineBreaks}`
|
||||||
|
|
||||||
|
if (shouldIncrementAfterStartSections) {
|
||||||
|
Handlebars.helpers.incrementCurrentTitleLevel()
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${separator}${startSections.join(`${lineBreaks}${separator}`)}`
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
{{incrementCurrentTitleLevel}}
|
{{incrementCurrentTitleLevel}}
|
||||||
|
|
||||||
|
{{{ startSections }}}
|
||||||
|
|
||||||
{{#with model.readme}}
|
{{#with model.readme}}
|
||||||
|
|
||||||
{{{comment this}}}
|
{{{comment this}}}
|
||||||
|
|||||||
+2
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
{{incrementCurrentTitleLevel}}
|
{{incrementCurrentTitleLevel}}
|
||||||
|
|
||||||
|
{{{ startSections }}}
|
||||||
|
|
||||||
{{#with model}}
|
{{#with model}}
|
||||||
|
|
||||||
{{#if (sectionEnabled "reflection_comment")}}
|
{{#if (sectionEnabled "reflection_comment")}}
|
||||||
|
|||||||
+2
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
{{incrementCurrentTitleLevel}}
|
{{incrementCurrentTitleLevel}}
|
||||||
|
|
||||||
|
{{{ startSections }}}
|
||||||
|
|
||||||
{{> member showSources=false}}
|
{{> member showSources=false}}
|
||||||
|
|
||||||
{{decrementCurrentTitleLevel}}
|
{{decrementCurrentTitleLevel}}
|
||||||
|
|||||||
+2
@@ -75,7 +75,9 @@ export type FormattingOptionType = {
|
|||||||
mdxImports?: string[]
|
mdxImports?: string[]
|
||||||
maxLevel?: number
|
maxLevel?: number
|
||||||
fileNameSeparator?: string
|
fileNameSeparator?: string
|
||||||
|
startSections?: string[]
|
||||||
endSections?: string[]
|
endSections?: string[]
|
||||||
|
shouldIncrementAfterStartSections?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare module "typedoc" {
|
export declare module "typedoc" {
|
||||||
|
|||||||
Reference in new issue
Block a user