docs: added file module docs (#7278)

This commit is contained in:
Shahed Nasser
2024-05-13 18:54:59 +03:00
committed by GitHub
parent 8b2429d24f
commit 803e4aad02
32 changed files with 745 additions and 3448 deletions
@@ -12,8 +12,6 @@ An architectural module implements features and mechanisms related to the Medusa
Since modules are interchangeable, you have more control over Medusas 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>
Refer to the [Architectural Modules reference](!resources!/architectural-modules) for a list of Medusas architectural modules.
@@ -26,4 +24,4 @@ Refer to the [Architectural Modules reference](!resources!/architectural-modules
You create an architectural module like youve been creating custom modules: create a module with a service and export that service in the modules 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.
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.
This makes the event architecture customizable, as you can either choose one of Medusas Event Modules or create your own.
This makes the event architecture customizable, as you can either choose one of Medusas 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 Nodes EventEmitter to implement the publish/subscribe system.
This is useful for development. However, for production, its 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, youll 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, its 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, youll learn more about customizable aspects of Medusa
By the end of these chapters, youll learn about:
- What an architectural module is.
- The cache architectural module and how to create one.
- The events architectural module and how to create one.
- The different types of architectural modules, including cache and event modules.
+4 -12
View File
@@ -253,22 +253,14 @@ export const sidebar = sidebarAttachHrefCommonOptions(
{
path: "/architectural-concepts/cache-module",
title: "Cache Module",
children: [
{
path: "/architectural-concepts/cache-module/create",
title: "Create Cache Module",
},
],
},
{
path: "/architectural-concepts/event-module",
title: "Event Module",
children: [
{
path: "/architectural-concepts/event-module/create",
title: "Create Event Module",
},
],
},
{
path: "/architectural-concepts/file-module",
title: "File Module",
},
],
},
@@ -1,10 +1,10 @@
export const metadata = {
title: `${pageNumber} How to Create a Cache Module`,
title: `How to Create a Cache Module`,
}
# {metadata.title}
In this chapter, youll learn how to create a Cache Module.
In this guide, youll learn how to create a Cache Module.
## 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} />
@@ -1,10 +1,10 @@
export const metadata = {
title: `${pageNumber} How to Create an Event Module`,
title: `How to Create an Event Module`,
}
# {metadata.title}
In this chapter, youll learn how to create an Event Module.
In this guide, youll learn how to create an Event Module.
## 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.
<ChildDocs />
<ChildDocs filters={["Guides"]} />
@@ -48,11 +48,15 @@ const modules = {
{
resolve: "@medusajs/payment-stripe",
options: {
credentials: {
usd: {
apiKey: process.env.STRIPE_USD_API_KEY,
},
},
config: {
stripe: {
credentials: {
usd: {
apiKey: process.env.STRIPE_USD_API_KEY,
},
},
}
}
},
},
],
+26 -6
View File
@@ -3,22 +3,46 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/admin-widget-injection-zones/page.mdx",
"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",
"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",
"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",
"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",
"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",
"pathname": "/architectural-modules"
@@ -2004,12 +2028,8 @@ export const filesMap = [
"pathname": "/references/customer_models/classes/customer_models.CustomerGroupCustomer"
},
{
"filePath": "/www/apps/resources/references/file/classes/file.AbstractFileService/page.mdx",
"pathname": "/references/file/classes/file.AbstractFileService"
},
{
"filePath": "/www/apps/resources/references/file/interfaces/file.IFileService/page.mdx",
"pathname": "/references/file/interfaces/file.IFileService"
"filePath": "/www/apps/resources/references/file/classes/file.AbstractFileProviderService/page.mdx",
"pathname": "/references/file/classes/file.AbstractFileProviderService"
},
{
"filePath": "/www/apps/resources/references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.cancelFulfillment/page.mdx",
+60
View File
@@ -6804,6 +6804,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"path": "/architectural-modules/cache",
"title": "Cache Modules",
"hasTitleStyling": true,
"children": [
@@ -6820,12 +6821,27 @@ export const generatedSidebar = [
"path": "/architectural-modules/cache/redis",
"title": "Redis",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"title": "Guides",
"children": [
{
"loaded": true,
"isPathHref": true,
"path": "/architectural-modules/cache/create",
"title": "Create Cache Module",
"children": []
}
]
}
]
},
{
"loaded": true,
"isPathHref": true,
"path": "/architectural-modules/event",
"title": "Event Modules",
"hasTitleStyling": true,
"children": [
@@ -6842,6 +6858,50 @@ export const generatedSidebar = [
"path": "/architectural-modules/event/redis",
"title": "Redis",
"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"
},
{
"origSlug": "/references/file/classes/file.AbstractFileService",
"newSlug": "/references/file-service",
"filePath": "/www/apps/resources/references/file/classes/file.AbstractFileService/page.mdx"
"origSlug": "/references/file/classes/file.AbstractFileProviderService",
"newSlug": "/references/file-provider-module",
"filePath": "/www/apps/resources/references/file/classes/file.AbstractFileProviderService/page.mdx"
},
{
"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, youll 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&#60;ProviderFileResultDTO&#62;","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&#60;void&#62;","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&#60;string&#62;","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, youll 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 thats 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 files name should be the slug version of the file services name
without `service`, and the classs name should be the pascal case of the file services 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 youre 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 providers APIs,
you can initialize it in the constructor and use it in other methods in the service.
Additionally, if youre 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 files details, you can access the files path in the `path` property of the file object.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="upload"/>
#### Returns
<TypeList types={[{"name":"Promise","type":"Promise&#60;[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)&#62;","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
shouldnt be accessible by using the files 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 files details, you can access the files path in the `path` property of the file object.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="uploadProtected"/>
#### Returns
<TypeList types={[{"name":"Promise","type":"Promise&#60;[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)&#62;","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&#60;void&#62;","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&#60;[FileServiceGetUploadStreamResult](../../../types/interfaces/types.FileServiceGetUploadStreamResult/page.mdx)&#62;","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&#60;any&#62;","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 its 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&#60;ReadableStream&#62;","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 doesnt 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&#60;string&#62;","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 thats 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 files name should be the slug version of the file services name
without `service`, and the classs name should be the pascal case of the file services 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 youre 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 files details, you can access the files path in the `path` property of the file object.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="upload"/>
#### Returns
<TypeList types={[{"name":"Promise","type":"Promise&#60;[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)&#62;","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
shouldnt be accessible by using the files 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 files details, you can access the files path in the `path` property of the file object.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="uploadProtected"/>
#### Returns
<TypeList types={[{"name":"Promise","type":"Promise&#60;[FileServiceUploadResult](../../../medusa/interfaces/medusa.FileServiceUploadResult/page.mdx)&#62;","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&#60;void&#62;","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&#60;[FileServiceGetUploadStreamResult](../../../types/interfaces/types.FileServiceGetUploadStreamResult/page.mdx)&#62;","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&#60;any&#62;","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 its 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&#60;ReadableStream&#62;","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 doesnt 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&#60;string&#62;","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&#60;TResult&#62;","description":"the transactional work to be done","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationOrErrorHandler","type":"`IsolationLevel` \\| (`error`: TError) => Promise&#60;void \\| TResult&#62;","description":"the isolation level to be used for the work.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"maybeErrorHandlerOrDontFail","type":"(`error`: TError) => Promise&#60;void \\| TResult&#62;","description":"Potential error handler","optional":true,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="atomicPhase_"/>
#### Returns
<TypeList types={[{"name":"Promise","type":"Promise&#60;TResult&#62;","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
- [AbstractFileService](../../file/classes/file.AbstractFileService/page.mdx)
## Interfaces
- [IFileService](../../file/interfaces/file.IFileService/page.mdx)
- [AbstractFileProviderService](../../file/classes/file.AbstractFileProviderService/page.mdx)
+40
View File
@@ -1592,6 +1592,7 @@ export const sidebar = sidebarAttachHrefCommonOptions([
hasTitleStyling: true,
children: [
{
path: "/architectural-modules/cache",
title: "Cache Modules",
hasTitleStyling: true,
children: [
@@ -1603,9 +1604,19 @@ export const sidebar = sidebarAttachHrefCommonOptions([
path: "/architectural-modules/cache/redis",
title: "Redis",
},
{
title: "Guides",
children: [
{
path: "/architectural-modules/cache/create",
title: "Create Cache Module",
},
],
},
],
},
{
path: "/architectural-modules/event",
title: "Event Modules",
hasTitleStyling: true,
children: [
@@ -1617,6 +1628,35 @@ export const sidebar = sidebarAttachHrefCommonOptions([
path: "/architectural-modules/event/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"
import React from "react"
import React, { useMemo } from "react"
import { Card, CardList, MDXComponents, useSidebar } from "../.."
import { SidebarItemType } from "types"
type ChildDocsProps = {
onlyTopLevel?: boolean
type?: "sidebar" | "item"
filters?: string[]
}
export const ChildDocs = ({ onlyTopLevel = false }: ChildDocsProps) => {
const { currentItems } = useSidebar()
export const ChildDocs = ({
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[]) => (
<CardList
@@ -58,8 +102,8 @@ export const ChildDocs = ({ onlyTopLevel = false }: ChildDocsProps) => {
return (
<>
{getElms(currentItems?.top)}
{getElms(currentItems?.bottom)}
{getElms(filteredItems?.top)}
{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",
}),
file: getOptions({
entryPointPath: "packages/medusa/src/interfaces/file-service.ts",
tsConfigName: "medusa.json",
entryPointPath: "packages/core/utils/src/file/abstract-file-provider.ts",
tsConfigName: "utils.json",
name: "file",
parentIgnore: true,
}),
@@ -6,42 +6,89 @@ const fileOptions: FormattingOptionsType = {
displayed_sidebar: "core",
},
},
"^file/.*AbstractFileService": {
"^file/.*AbstractFileProviderService": {
reflectionGroups: {
Properties: false,
},
reflectionDescription: `In this document, youll learn how to create a file service in the Medusa backend and the methods you must implement in it.`,
reflectionDescription: `In this document, youll learn how to create a file provider module and the methods you must implement in it.`,
frontmatterData: {
slug: "/references/file-service",
slug: "/references/file-provider-module",
},
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: [
`## 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"
:::
After finishing your file service implementation:
1\\. Run the \`build\` command in the root of your Medusa backend:
\`\`\`bash npm2yarn
npm run build
export default {
service: MyFileProviderService,
}
\`\`\`
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
npx medusa develop
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...
},
},
},
},
],
},
},
},
}
\`\`\`
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 shouldExpandPropertiesHelper from "./resources/helpers/should-expand-properties"
import shouldExpandDeclarationChildrenHelper from "./resources/helpers/should-expand-declaration-children"
import startSectionsHelper from "./resources/helpers/start-sections"
import { MarkdownTheme } from "./theme"
const TEMPLATE_PATH = path.join(__dirname, "resources", "templates")
@@ -154,4 +155,5 @@ export function registerHelpers(theme: MarkdownTheme) {
ifShowSeparatorForTitleLevelHelper(theme)
shouldExpandPropertiesHelper(theme)
shouldExpandDeclarationChildrenHelper(theme)
startSectionsHelper(theme)
}
@@ -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}}
{{{ startSections }}}
{{#with model.readme}}
{{{comment this}}}
@@ -4,6 +4,8 @@
{{incrementCurrentTitleLevel}}
{{{ startSections }}}
{{#with model}}
{{#if (sectionEnabled "reflection_comment")}}
@@ -6,6 +6,8 @@
{{incrementCurrentTitleLevel}}
{{{ startSections }}}
{{> member showSources=false}}
{{decrementCurrentTitleLevel}}
+2
View File
@@ -75,7 +75,9 @@ export type FormattingOptionType = {
mdxImports?: string[]
maxLevel?: number
fileNameSeparator?: string
startSections?: string[]
endSections?: string[]
shouldIncrementAfterStartSections?: boolean
}
export declare module "typedoc" {