chore(core/strapi): extend container in strapi class

This commit is contained in:
Alexandre Bodin 2023-10-20 10:52:33 +02:00 committed by Josh
parent aa378a1671
commit 0b5590b0eb
41 changed files with 304 additions and 282 deletions

View File

@ -2,13 +2,14 @@
title: Strapi (WIP)
slug: /api/Strapi
tags:
- class
- public
- global
- class
- public
- global
toc_min_heading_level: 2
toc_max_heading_level: 3
---
import Type from '@site/docs/api/components/type';
# Strapi
@ -34,26 +35,21 @@ An instance of Strapi class is available as a global in any Strapi project: `glo
- `serveAdminPanel`: <Type>Boolean</Type> **Default:** true
- Should the admin panel be loaded and serve as a web client
- The admin panel build will not be delivered if false
- `appDir`: <Type>String</Type> **Default:** `process.cwd()`
- `appDir`: <Type>String</Type> **Default:** `process.cwd()`
- The directory relative or absolute path where Strapi will write every file (schemas, generated APIs, controllers or services)
- `distDir`: <Type>String</Type> **Default:** appDir value
- `distDir`: <Type>String</Type> **Default:** appDir value
- The directory relative or absolute path where Strapi will read configurations, schemas and any compiled code
Instances of the Strapi class can be created using the new keyword.
Strapi extends the [Container](./container) class.
```javascript
const strapiInstance = new Strapi();
```
### `strapi.container`
- [<Type>Container</Type>](./container)
The container provides a simple and efficient way to register and manage resources, making it easy to access and use them throughout the application.
By registering a registry with the container, it can be easily retrieved by other parts of the application, making it a powerful tool for organizing and reusing code across the entire codebase.
See [Container](./container).
### `strapi.dirs`
- <Type>Object</Type>
Stored paths of file system.
@ -63,24 +59,26 @@ Stored paths of file system.
- `dirs.app`: <Type>[StrapiPathObject](#strapipathobject)</Type>
- Sources folder
- `dirs.static`: <Type>Object</Type> Define path to directories involving web client display
- `public`: <Type>String</Type> Path to the folder to serve publicly (like files, images, etc..)
- `public`: <Type>String</Type> Path to the folder to serve publicly (like files, images, etc..)
#### StrapiPathObject
- <Type>Object</Type>
A set of paths to specific Strapi project parts.
- `root`: <Type>String</Type> Root path
- `src`: <Type>String</Type> Sources route path to project files
- `api`: <Type>String</Type> Path to the folder containing project developers' API files (content-types, controllers, services, routes, etc..)
- `components`: <Type>String</Type> Path to the folder containing project developers' components
- `policies`: <Type>String</Type> Path to the folder where the Strapi project developers' policies are stored
- `api`: <Type>String</Type> Path to the folder containing project developers' API files (content-types, controllers, services, routes, etc..)
- `components`: <Type>String</Type> Path to the folder containing project developers' components
- `policies`: <Type>String</Type> Path to the folder where the Strapi project developers' policies are stored
- A set of functions that check the state of the data and prevent the access to the API accordingly
- `middlewares`: <Type>String</Type> Path to the folder where the Strapi project developers' middlewares are stored
- `middlewares`: <Type>String</Type> Path to the folder where the Strapi project developers' middlewares are stored
- A set of function that wrap around routes and requests
- `config`: <Type>String</Type> Path to the folder containing project developers' config files
- `config`: <Type>String</Type> Path to the folder containing project developers' config files
### `strapi.isLoaded`
- <Type>Boolean</Type>
- `true`: Everything (all `register` and `bootstrap` functions available in your strapi project) has been loaded
@ -95,44 +93,49 @@ Reload the app.
This function defines itself at the construction of the Strapi class.
### `strapi.server`
- [<Type>StrapiServer</Type>](./strapi-server)
Strapi server object.
### `strapi.fs`
- [<Type>StrapiFS</Type>](StrapiFS)
Wrapper around [FS NodeJS module](https://nodejs.org/docs/latest-v18.x/api/fs.html).
### `strapi.eventHub`
- [<Type>EventHub</Type>](EventHub)
The `strapi.eventHub` object is used to manipulate events within a Strapi project. It is an instance of the built-in EventEmitter class from Node.js, which provides a simple way to emit and listen for events.
The `strapi.eventHub` object is created using the `createEventHub()` function in the [EventHub](EventHub) module of the Strapi core. This function returns a new instance of the EventHub class, which extends the EventEmitter class and adds some additional functionality specific to Strapi.
#### Examples:
```javascript
// Listen for a 'user.updated' event and log the data
strapi.eventHub.on('user.updated', (data) => {
console.log(`User ${data.id} has been updated`);
console.log(`User ${data.id} has been updated`);
});
// Emit a 'user.created' event with some data
strapi.eventHub.emit('user.created', { username: 'johndoe', email: 'johndoe@example.com' });
```
In this example, we are emitting a `user.created` event with some data attached to it, and then listening for a user.updated event and logging the data. These events can be used to trigger actions within the Strapi application or to communicate with external systems.
For more information on how to use the EventEmitter class and its methods, see the [Node.js documentation](ttps://nodejs.org/docs/latest-v18.x/api/events.html#class-eventemitter).
### `strapi.startupLogger`
- [<Type>StartupLogger</Type>](StartupLogger)
Object containing predefined logger functions. Used for Strapi startup. (do not use as a logger elsewhere)
### `strapi.log`
- [<Type>Winston</Type>](https://github.com/winstonjs/winston#creating-your-own-logger)
A logger provided by Strapi that uses the Winston logging library. It is the result of calling the `winston.createLogger()` function with the configuration defined by the user of the Strapi application.
@ -140,6 +143,7 @@ A logger provided by Strapi that uses the Winston logging library. It is the res
The logger provides various methods for logging messages at different levels of severity, including error, warn, info, verbose, debug, and silly. The logging level can be set via the configuration to control which messages are logged.
#### Examples
```javascript
// Log an error message
strapi.log.error('Failed to start server', { error: err });
@ -159,16 +163,19 @@ strapi.log.debug('API request received', { method: req.method, path: req.path })
// Log a silly message
strapi.log.silly('Entered loop', { count: i });
```
In these examples, we are logging messages at different levels of severity, including error, warn, info, verbose, debug, and silly. We are also passing in metadata as an object in the second parameter of each logging method.
The messages logged by strapi.log will be output according to the logging configuration set by the user of the Strapi application. This configuration determines which messages are logged and where they are logged (e.g. console, file, etc.).
### `strapi.cron`
- [<Type>CronService</Type>](Cron)
Module to schedule cron jobs for Strapi project. It is an instance of a custom Cron object.
### `strapi.telemetry`
- [<Type>TelemetryService</Type>](Telemetry)
The `strapi.telemetry` property provides access to the telemetry service instance. This service collects anonymous usage data about your Strapi application to help the Strapi team improve the product.
@ -176,6 +183,7 @@ The `strapi.telemetry` property provides access to the telemetry service instanc
By default, the telemetry service is enabled, but you can disable it by setting the telemetryDisabled property to true in your application's package.json file, or by setting the `STRAPI_TELEMETRY_DISABLED` environment variable to true. You can also disable telemetry programmatically by setting the isDisabled property of the `strapi.telemetry` instance to true.
### `strapi.requestContext`
- <Type>Object</Type> Context Storage
- `run(store, cb)`: <Type>Function</Type>
@ -186,162 +194,185 @@ By default, the telemetry service is enabled, but you can disable it by setting
The request context stores the ctx object from KoaJS on each request. This allows users to have access to the context from anywhere through the Strapi instance.
### `strapi.customFields`
- <Type>Object</Type>
- `register(customField)`: <Type>Function</Type> Register a new custom field
This property is a shortcut to `strapi.container.get('custom-fields').add(customField)`.
This property is a shortcut to `strapi.get('custom-fields').add(customField)`.
#### Examples
```javascript
strapi.customFields.register({
name: 'color',
plugin: 'color-picker',
type: 'string',
});
strapi.customFields.register({
name: 'color',
plugin: 'color-picker',
type: 'string',
});
```
### `strapi.config`
- <Type>Object</Type>
Shortcut to `strapi.container.get('config')`.
Shortcut to `strapi.get('config')`.
See the [config container](#config).
### `strapi.services`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('services').getAll()`.
Shortcut to `strapi.get('services').getAll()`.
See the [services' container](#services).
### `strapi.service(uid)`
- `uid`: <Type>String</Type>
Shortcut to `strapi.container.get('services').get(uid)`.
Shortcut to `strapi.get('services').get(uid)`.
See the [services' container](#services).
### `strapi.controllers`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('controllers').getAll()`.
Shortcut to `strapi.get('controllers').getAll()`.
See the [controllers' container](#controllers).
### `strapi.controller(uid)`
- `uid`: <Type>String</Type>
Shortcut to `strapi.container.get('controllers').get(uid)`.
Shortcut to `strapi.get('controllers').get(uid)`.
See the [controllers' container](#controllers).
### `strapi.contentTypes`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('content-types').getAll()`.
Shortcut to `strapi.get('content-types').getAll()`.
See the [content-types' container](#content-types).
### `strapi.contentType(name)`
- `name`: <Type>String</Type>
Shortcut to `strapi.container.get('content-types').get(name)`.
Shortcut to `strapi.get('content-types').get(name)`.
See the [content-types' container](#content-types).
### `strapi.policies`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('policies').getAll()`.
Shortcut to `strapi.get('policies').getAll()`.
See the [policies' container](#policies).
### `strapi.policy(name)`
- `name`: <Type>String</Type>
Shortcut to `strapi.container.get('policies').get(name)`.
Shortcut to `strapi.get('policies').get(name)`.
See the [policies' container](#policies).
### `strapi.middlewares`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('middlewares').getAll()`.
Shortcut to `strapi.get('middlewares').getAll()`.
See the [middlewares container](#middlewares).
### `strapi.middleware(name)`
- `name`: <Type>String</Type>
Shortcut to `strapi.container.get('middlewares').get(name)`.
Shortcut to `strapi.get('middlewares').get(name)`.
See the [middlewares container](#middlewares).
### `strapi.plugins`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('plugins').getAll()`.
Shortcut to `strapi.get('plugins').getAll()`.
See the [plugins' container](#plugins).
### `strapi.plugin(name)`
- `name`: <Type>String</Type>
Shortcut to `strapi.container.get('plugins').get(name)`.
Shortcut to `strapi.get('plugins').get(name)`.
See the [plugins' container](#plugins).
### `strapi.hooks`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('hooks').getAll()`.
Shortcut to `strapi.get('hooks').getAll()`.
See the [hooks' container](#hooks).
### `strapi.hook(name)`
- `name`: <Type>String</Type>
Shortcut to `strapi.container.get('hooks').get(name)`.
Shortcut to `strapi.get('hooks').get(name)`.
See the [hooks' container](#hooks).
### `strapi.api`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('apis').getAll()`.
Shortcut to `strapi.get('apis').getAll()`.
See the [apis container](#apis).
### `strapi.auth`
- <Type>Object</Type>
Shortcut to `strapi.container.get('auth')`.
Shortcut to `strapi.get('auth')`.
See the [auth' container](#auth).
### `strapi.contentAPI`
- <Type>Object</Type>
Shortcut to `strapi.container.get('content-api')`.
Shortcut to `strapi.get('content-api')`.
See the [content-api container](#content-api).
### `strapi.sanitizers`
- <Type>Object</Type>
Shortcut to `strapi.container.get('sanitizers')`.
Shortcut to `strapi.get('sanitizers')`.
See the [sanitizers' container](#sanitizers).
### `strapi.validators`
- <Type>Object</Type>
Shortcut to `strapi.container.get('validators')`.
Shortcut to `strapi.get('validators')`.
See the [validators' container](#validators).
### `strapi.start()`
- Returns: Promise
:::info
@ -349,6 +380,7 @@ TODO
:::
### `strapi.destroy()`
- Returns: Promise
:::info
@ -362,6 +394,7 @@ TODO
:::
### `strapi.openAdmin({ isInitialized })`
- Returns: Promise
:::info
@ -369,6 +402,7 @@ TODO
:::
### `strapi.postListen()`
- Returns: Promise
:::info
@ -376,6 +410,7 @@ TODO
:::
### `strapi.listen()`
- Returns: Promise
:::info
@ -395,6 +430,7 @@ TODO
:::
### `strapi.loadAdmin()`
- Returns: Promise
:::info
@ -402,6 +438,7 @@ TODO
:::
### `strapi.loadPlugins()`
- Returns: Promise
:::info
@ -409,6 +446,7 @@ TODO
:::
### `strapi.loadPolicies()`
- Returns: Promise
:::info
@ -416,6 +454,7 @@ TODO
:::
### `strapi.loadAPIs()`
- Returns: Promise
:::info
@ -423,6 +462,7 @@ TODO
:::
### `strapi.loadComponents()`
- Returns: Promise
:::info
@ -430,6 +470,7 @@ TODO
:::
### `strapi.loadMiddlewares()`
- Returns: Promise
:::info
@ -437,6 +478,7 @@ TODO
:::
### `strapi.loadApp()`
- Returns: Promise
:::info
@ -444,6 +486,7 @@ TODO
:::
### `strapi.loadSanitizers()`
- Returns: Promise
:::info
@ -457,6 +500,7 @@ TODO
:::
### `strapi.register()`
- Returns: Promise
:::info
@ -464,6 +508,7 @@ TODO
:::
### `strapi.bootstrap()`
- Returns: Promise
:::info
@ -471,6 +516,7 @@ TODO
:::
### `strapi.load()`
- Returns: Promise
:::info
@ -478,6 +524,7 @@ TODO
:::
### `strapi.startWebhooks()`
- Returns: Promise
:::info
@ -491,6 +538,7 @@ TODO
:::
### `strapi.runLifecyclesFunctions()`
- Returns: Promise
:::info
@ -498,6 +546,7 @@ TODO
:::
### `strapi.getModel(uid)`
- `uid`: <Type>String</Type>
:::info
@ -505,6 +554,7 @@ TODO
:::
### `strapi.query(uid)`
- `uid`: <Type>String</Type>
:::info
@ -513,9 +563,10 @@ TODO
## Strapi containers
The strapi containers are accessible via `strapi.container.get('name-of-the-container')`.
The strapi containers are accessible via `strapi.get('name-of-the-container')`.
### `config`
- <Type>Object</Type>
- `get(path, defaultValue)`: <Type>Function</Type>
@ -528,7 +579,7 @@ The strapi containers are accessible via `strapi.container.get('name-of-the-cont
- `has(path)`: <Type>Function</Type>
- `path`: <Type>String</Type>
- Returns: <Type>Boolean</Type> - Does the `path` match a value stored in the config container.
- `launchedAt`: <Type>Number</Type> **Default:** `Date.now()`
- `launchedAt`: <Type>Number</Type> **Default:** `Date.now()`
Date in milliseconds when the server has started
- `serveAdminPanel`: <Type>Boolean</Type> **Default:** `true`
See [Strapi constructor](#new-strapiopts) options
@ -608,4 +659,3 @@ TODO
:::info
TODO
:::

View File

@ -2,12 +2,13 @@
title: Container
slug: /api/container
tags:
- module
- public
- module
- public
toc_min_heading_level: 2
toc_max_heading_level: 5
---
import Type from '@site/docs/api/components/type';
# Container
@ -22,40 +23,41 @@ The container module permits to generate containers.
## Module: container
### `createContainer(strapi)`
### class Container
### `new Container()`
- `strapi`: <Type>Strapi</Type> [See Strapi class documentation](Strapi.mdx)
- Returns: <Type>Container</Type>
```javascript
const container = createContainer(strapi);
const container = new Container();
container.register('config', {
get: (configName) => {},
set: (configName, value) => {}
container.add('config', {
get: (configName) => {},
set: (configName, value) => {},
});
const dbConfig = container.get('config').get('database');
```
### `container.register(name, resolver)`
### `container.add(name, resolver)`
- `name`: <Type>String</Type> UID of the content
- `resolver`: <Type>Function</Type> | <Type>Any</Type>
- As a function, the function will be executed when the first get method is called on this content. The result of this function will define the content of this UID.
- `resolver(context, args)`
- `context`: <Type>{ Strapi }</Type> [See Strapi class documentation](Strapi.mdx)
- `context`: <Type>Container</Type> The container instance
- `args`: <Type>Any</Type> Anything to be used by the resolver function
- As anything else, this value will be resolved when getting this specified content through its UID.
Register a new content to be accessed inside the container. If the name is already used, it will throw an error.
```javascript
const container = createContainer(strapi);
const container = new Container();
container.register('config', ({ strapi }, args) => {});
container.add('config', (container, args) => {});
// or
container.register('services', {});
container.add('services', {});
```
### `container.get(name, args)`
@ -66,9 +68,9 @@ container.register('services', {});
Get the value stored for a specific `name`.
```javascript
const container = createContainer(strapi);
const container = new Container();
container.register('config', { db: 'sqlite' });
container.add('config', { db: 'sqlite' });
const config = container.get('config');
// config.db === 'sqlite'
@ -79,9 +81,9 @@ const config = container.get('config');
Please pay attention that the resolver result value isn't awaited. So if resolver returns a promise, the value stored will be a promise.
```javascript
const container = createContainer(strapi);
const container = new Container();
container.register('boolean', (bool) => bool);
container.add('boolean', (bool) => bool);
// First call - The value is resolved through the resolver above "(bool) => bool"
container.get('boolean', true);
@ -95,8 +97,3 @@ container.get('boolean');
container.get('boolean', false);
// true
```
### `container.extend()`
:::info
To be developed
:::

View File

@ -7,7 +7,7 @@ module.exports = {
const { query } = ctx.request;
await validateFindMany(query);
const auditLogs = strapi.container.get('audit-logs');
const auditLogs = strapi.get('audit-logs');
const body = await auditLogs.findMany(query);
ctx.body = body;
@ -16,7 +16,7 @@ module.exports = {
async findOne(ctx) {
const { id } = ctx.params;
const auditLogs = strapi.container.get('audit-logs');
const auditLogs = strapi.get('audit-logs');
const body = await auditLogs.findOne(id);
ctx.body = body;

View File

@ -5,7 +5,7 @@ const executeCEDestroy = require('../../server/destroy');
module.exports = async ({ strapi }) => {
if (features.isEnabled('audit-logs')) {
strapi.container.get('audit-logs').destroy();
strapi.get('audit-logs').destroy();
}
await executeCEDestroy();

View File

@ -19,7 +19,7 @@ module.exports = async ({ strapi }) => {
if (auditLogsIsEnabled) {
strapi.hook('strapi::content-types.beforeSync').register(migrateAuditLogsTable);
const auditLogsService = createAuditLogsService(strapi);
strapi.container.register('audit-logs', auditLogsService);
strapi.add('audit-logs', auditLogsService);
await auditLogsService.register();
}
if (features.isEnabled('review-workflows')) {

View File

@ -43,12 +43,10 @@ describe('Audit logs service', () => {
it('should not register the audit logs service when is disabled by the user', async () => {
const eeAdminRegister = require('../../register');
const mockRegister = jest.fn();
const mockAdd = jest.fn();
const strapi = {
container: {
register: mockRegister,
},
add: mockAdd,
config: {
get: () => false,
},
@ -56,16 +54,14 @@ describe('Audit logs service', () => {
await eeAdminRegister({ strapi });
expect(mockRegister).not.toHaveBeenCalledWith('audit-logs', expect.anything());
expect(mockAdd).not.toHaveBeenCalledWith('audit-logs', expect.anything());
});
it('should not subscribe to events when the license does not allow it', async () => {
const mockSubscribe = jest.fn();
const strapi = {
container: {
register: jest.fn(),
},
add: jest.fn(),
config: {
get(key) {
switch (key) {
@ -113,7 +109,7 @@ describe('Audit logs service', () => {
});
describe('Init with audit logs enabled', () => {
const mockRegister = jest.fn();
const mockAdd = jest.fn();
const mockScheduleJob = jest.fn((rule, callback) => callback());
// Audit logs Local Provider mocks
@ -142,9 +138,7 @@ describe('Audit logs service', () => {
},
},
},
container: {
register: mockRegister,
},
add: mockAdd,
eventHub: createEventHub(),
hook: () => ({ register: jest.fn() }),
config: {
@ -193,7 +187,7 @@ describe('Audit logs service', () => {
await eeAdminRegister({ strapi });
expect(mockRegister).toHaveBeenCalledWith('audit-logs', expect.anything());
expect(mockAdd).toHaveBeenCalledWith('audit-logs', expect.anything());
});
it('should emit an event and capture it in the audit logs', async () => {

View File

@ -76,8 +76,8 @@ const reviewWorkflowsValidationMock = {
const hookMock = jest.fn().mockReturnValue({ register: jest.fn() });
const strapiMock = {
...containerMock,
contentTypes: contentTypesMock,
container: containerMock,
hook: hookMock,
query: jest.fn(() => queryMock),
service(serviceName) {

View File

@ -76,7 +76,7 @@ function extendReviewWorkflowContentTypes({ strapi }) {
[assertContentTypeCompatibility, setReviewWorkflowAttributes],
[stubTrue, incompatibleContentTypeAlert],
]);
strapi.container.get('content-types').extend(contentTypeUID, extendContentTypeIfCompatible);
strapi.get('content-types').extend(contentTypeUID, extendContentTypeIfCompatible);
};
pipe([

View File

@ -10,8 +10,8 @@ module.exports = ({ strapi }) => {
const passportMiddleware = strapi.admin.services.passport.init();
strapi.server.api('admin').use(passportMiddleware);
strapi.container.get('auth').register('admin', adminAuthStrategy);
strapi.container.get('auth').register('content-api', apiTokenAuthStrategy);
strapi.get('auth').register('admin', adminAuthStrategy);
strapi.get('auth').register('content-api', apiTokenAuthStrategy);
if (strapi.config.serveAdminPanel) {
registerAdminPanelRoute({ strapi });

View File

@ -4,28 +4,26 @@ const { ApplicationError } = require('@strapi/utils').errors;
const createFieldSizesService = require('../field-sizes');
const strapi = {
container: {
// Mock container.get('custom-fields')
get: jest.fn(() => ({
// Mock container.get('custom-fields').getAll()
getAll: jest.fn(() => ({
'plugin::mycustomfields.color': {
name: 'color',
plugin: 'mycustomfields',
type: 'string',
// Mock container.get('custom-fields')
get: jest.fn(() => ({
// Mock container.get('custom-fields').getAll()
getAll: jest.fn(() => ({
'plugin::mycustomfields.color': {
name: 'color',
plugin: 'mycustomfields',
type: 'string',
},
'plugin::mycustomfields.smallColor': {
name: 'smallColor',
plugin: 'mycustomfields',
type: 'string',
inputSize: {
default: 4,
isResizable: false,
},
'plugin::mycustomfields.smallColor': {
name: 'smallColor',
plugin: 'mycustomfields',
type: 'string',
inputSize: {
default: 4,
isResizable: false,
},
},
})),
},
})),
},
})),
};
describe('field sizes service', () => {

View File

@ -84,7 +84,7 @@ const createFieldSizesService = ({ strapi }) => {
setCustomFieldInputSizes() {
// Find all custom fields already registered
const customFields = strapi.container.get('custom-fields').getAll();
const customFields = strapi.get('custom-fields').getAll();
// If they have a custom field size, register it
Object.entries(customFields).forEach(([uid, customField]) => {

View File

@ -10,7 +10,6 @@ import { hooks } from '@strapi/utils';
import type {
Strapi as StrapiI,
Server,
Container,
EntityService,
EventHub,
StartupLogger,
@ -36,7 +35,7 @@ import * as factories from './factories';
import compile from './compile';
import * as utils from './utils';
import { createContainer } from './container';
import { Container } from './container';
import createStrapiFs from './services/fs';
import createEventHub from './services/event-hub';
import { createServer } from './services/server';
@ -141,11 +140,9 @@ const reloader = (strapi: Strapi) => {
export type LoadedStrapi = Required<Strapi>;
class Strapi implements StrapiI {
class Strapi extends Container implements StrapiI {
server: Server;
container: Container;
log: Logger;
fs: StrapiFS;
@ -189,6 +186,8 @@ class Strapi implements StrapiI {
reload: Reloader;
constructor(opts: StrapiOptions = {}) {
super();
destroyOnSignal(this);
const rootDirs = resolveWorkingDirectories(opts);
@ -197,23 +196,22 @@ class Strapi implements StrapiI {
const appConfig = loadConfiguration(rootDirs, opts);
// Instantiate the Strapi container
this.container = createContainer()
.register('config', createConfigProvider(appConfig))
.register('content-types', contentTypesRegistry())
.register('components', componentsRegistry())
.register('services', servicesRegistry(this))
.register('policies', policiesRegistry())
.register('middlewares', middlewaresRegistry())
.register('hooks', hooksRegistry())
.register('controllers', controllersRegistry(this))
.register('modules', modulesRegistry(this))
.register('plugins', pluginsRegistry(this))
.register('custom-fields', customFieldsRegistry(this))
.register('apis', apisRegistry(this))
.register('auth', createAuth())
.register('content-api', createContentAPI(this))
.register('sanitizers', sanitizersRegistry())
.register('validators', validatorsRegistry());
this.add('config', createConfigProvider(appConfig))
.add('content-types', contentTypesRegistry())
.add('components', componentsRegistry())
.add('services', servicesRegistry(this))
.add('policies', policiesRegistry())
.add('middlewares', middlewaresRegistry())
.add('hooks', hooksRegistry())
.add('controllers', controllersRegistry(this))
.add('modules', modulesRegistry(this))
.add('plugins', pluginsRegistry(this))
.add('custom-fields', customFieldsRegistry(this))
.add('apis', apisRegistry(this))
.add('auth', createAuth())
.add('content-api', createContentAPI(this))
.add('sanitizers', sanitizersRegistry())
.add('validators', validatorsRegistry());
// Create a mapping of every useful directory (for the app, dist and static directories)
this.dirs = utils.getDirs(rootDirs, { strapi: this });
@ -248,91 +246,91 @@ class Strapi implements StrapiI {
}
get config() {
return this.container.get('config');
return this.get('config');
}
get services() {
return this.container.get('services').getAll();
return this.get('services').getAll();
}
service(uid: Common.UID.Service) {
return this.container.get('services').get(uid);
return this.get('services').get(uid);
}
get controllers() {
return this.container.get('controllers').getAll();
return this.get('controllers').getAll();
}
controller(uid: Common.UID.Controller) {
return this.container.get('controllers').get(uid);
return this.get('controllers').get(uid);
}
get contentTypes(): Shared.ContentTypes {
return this.container.get('content-types').getAll();
return this.get('content-types').getAll();
}
contentType(name: Common.UID.ContentType) {
return this.container.get('content-types').get(name);
return this.get('content-types').get(name);
}
get components(): Shared.Components {
return this.container.get('components').getAll();
return this.get('components').getAll();
}
get policies() {
return this.container.get('policies').getAll();
return this.get('policies').getAll();
}
policy(name: string) {
return this.container.get('policies').get(name);
return this.get('policies').get(name);
}
get middlewares() {
return this.container.get('middlewares').getAll();
return this.get('middlewares').getAll();
}
middleware(name: string) {
return this.container.get('middlewares').get(name);
return this.get('middlewares').get(name);
}
get plugins(): Record<string, Common.Plugin> {
return this.container.get('plugins').getAll();
return this.get('plugins').getAll();
}
plugin(name: string): Common.Plugin {
return this.container.get('plugins').get(name);
return this.get('plugins').get(name);
}
get hooks() {
return this.container.get('hooks').getAll();
return this.get('hooks').getAll();
}
hook(name: string) {
return this.container.get('hooks').get(name);
return this.get('hooks').get(name);
}
// api(name) {
// return this.container.get('apis').get(name);
// return this.get('apis').get(name);
// }
get api(): Record<string, Common.Module> {
return this.container.get('apis').getAll();
return this.get('apis').getAll();
}
get auth() {
return this.container.get('auth');
return this.get('auth');
}
get contentAPI() {
return this.container.get('content-api');
return this.get('content-api');
}
get sanitizers() {
return this.container.get('sanitizers');
return this.get('sanitizers');
}
get validators() {
return this.container.get('validators');
return this.get('validators');
}
async start() {
@ -493,13 +491,9 @@ class Strapi implements StrapiI {
}
registerInternalHooks() {
this.container
.get('hooks')
.set('strapi::content-types.beforeSync', hooks.createAsyncParallelHook());
this.get('hooks').set('strapi::content-types.beforeSync', hooks.createAsyncParallelHook());
this.container
.get('hooks')
.set('strapi::content-types.afterSync', hooks.createAsyncParallelHook());
this.get('hooks').set('strapi::content-types.afterSync', hooks.createAsyncParallelHook());
this.hook('strapi::content-types.beforeSync').register(draftAndPublishSync.disable);
this.hook('strapi::content-types.afterSync').register(draftAndPublishSync.enable);
@ -638,7 +632,7 @@ class Strapi implements StrapiI {
async runLifecyclesFunctions(lifecycleName: 'register' | 'bootstrap' | 'destroy') {
// plugins
await this.container.get('modules')[lifecycleName]();
await this.get('modules')[lifecycleName]();
// admin
const adminLifecycleFunction = this.admin && this.admin[lifecycleName];
@ -684,12 +678,12 @@ interface StrapiOptions {
}
interface Init {
(options?: StrapiOptions): Strapi;
(options?: StrapiOptions): StrapiI;
factories: typeof factories;
compile: typeof compile;
}
const initFn = (options: StrapiOptions = {}) => {
const initFn = (options: StrapiOptions = {}): StrapiI => {
const strapi = new Strapi(options);
global.strapi = strapi as LoadedStrapi;
return strapi;

View File

@ -7,7 +7,7 @@ export default async () => {
const appContext = await strapi.compile();
const app = await strapi(appContext).register();
const list = app.container.get('content-types').keys();
const list = app.get('content-types').keys();
const infoTable = new CLITable({
head: [chalk.blue('Name')],

View File

@ -7,7 +7,7 @@ export default async () => {
const appContext = await strapi.compile();
const app = await strapi(appContext).register();
const list = app.container.get('controllers').keys();
const list = app.get('controllers').keys();
const infoTable = new CLITable({
head: [chalk.blue('Name')],

View File

@ -7,7 +7,7 @@ export default async () => {
const appContext = await strapi.compile();
const app = await strapi(appContext).register();
const list = app.container.get('hooks').keys();
const list = app.get('hooks').keys();
const infoTable = new CLITable({
head: [chalk.blue('Name')],

View File

@ -7,7 +7,7 @@ export default async () => {
const appContext = await strapi.compile();
const app = await strapi(appContext).register();
const list = app.container.get('middlewares').keys();
const list = app.get('middlewares').keys();
const infoTable = new CLITable({
head: [chalk.blue('Name')],

View File

@ -7,7 +7,7 @@ export default async () => {
const appContext = await strapi.compile();
const app = await strapi(appContext).register();
const list = app.container.get('policies').keys();
const list = app.get('policies').keys();
const infoTable = new CLITable({
head: [chalk.blue('Name')],

View File

@ -7,7 +7,7 @@ export default async () => {
const appContext = await strapi.compile();
const app = await strapi(appContext).register();
const list = app.container.get('services').keys();
const list = app.get('services').keys();
const infoTable = new CLITable({
head: [chalk.blue('Name')],

View File

@ -1,43 +1,37 @@
import type { Container } from '@strapi/types';
import type { Container as ContainerContract } from '@strapi/types';
export const createContainer = (): Container => {
const registered = new Map<string, unknown>();
const resolved = new Map();
export class Container implements ContainerContract {
private registerMap = new Map<string, unknown>();
return {
register<T, U extends string>(name: U, resolver: T) {
if (registered.has(name)) {
throw new Error(`Cannot register already registered service ${name}`);
private serviceMap = new Map();
add(name: string, resolver: unknown) {
if (this.registerMap.has(name)) {
throw new Error(`Cannot register already registered service ${name}`);
}
this.registerMap.set(name, resolver);
return this;
}
get(name: string, args?: unknown) {
// TODO: handle singleton vs reinstanciation everytime
if (this.serviceMap.has(name)) {
return this.serviceMap.get(name);
}
if (this.registerMap.has(name)) {
const resolver = this.registerMap.get(name);
if (typeof resolver === 'function') {
this.serviceMap.set(name, resolver(this, args));
} else {
this.serviceMap.set(name, resolver);
}
registered.set(name, resolver);
return this;
},
return this.serviceMap.get(name);
}
get(name: string, args?: unknown) {
// TODO: handle singleton vs reinstanciation everytime
if (resolved.has(name)) {
return resolved.get(name);
}
if (registered.has(name)) {
const resolver = registered.get(name);
if (typeof resolver === 'function') {
resolved.set(name, resolver(this, args));
} else {
resolved.set(name, resolver);
}
return resolved.get(name);
}
throw new Error(`Could not resolve service ${name}`);
},
// TODO: implement
extend() {
return this;
},
};
};
throw new Error(`Could not resolve service ${name}`);
}
}

View File

@ -95,52 +95,52 @@ export const createModule = (namespace: string, rawModule: RawModule, strapi: St
await (rawModule.destroy && rawModule.destroy({ strapi }));
},
load() {
strapi.container.get('content-types').add(namespace, rawModule.contentTypes);
strapi.container.get('services').add(namespace, rawModule.services);
strapi.container.get('policies').add(namespace, rawModule.policies);
strapi.container.get('middlewares').add(namespace, rawModule.middlewares);
strapi.container.get('controllers').add(namespace, rawModule.controllers);
strapi.container.get('config').set(uidToPath(namespace), rawModule.config);
strapi.get('content-types').add(namespace, rawModule.contentTypes);
strapi.get('services').add(namespace, rawModule.services);
strapi.get('policies').add(namespace, rawModule.policies);
strapi.get('middlewares').add(namespace, rawModule.middlewares);
strapi.get('controllers').add(namespace, rawModule.controllers);
strapi.get('config').set(uidToPath(namespace), rawModule.config);
},
get routes() {
return rawModule.routes ?? {};
},
config(path: string, defaultValue: unknown) {
return strapi.container.get('config').get(`${uidToPath(namespace)}.${path}`, defaultValue);
return strapi.get('config').get(`${uidToPath(namespace)}.${path}`, defaultValue);
},
contentType(ctName: Common.UID.ContentType) {
return strapi.container.get('content-types').get(`${namespace}.${ctName}`);
return strapi.get('content-types').get(`${namespace}.${ctName}`);
},
get contentTypes() {
const contentTypes = strapi.container.get('content-types').getAll(namespace);
const contentTypes = strapi.get('content-types').getAll(namespace);
return removeNamespacedKeys(contentTypes, namespace);
},
service(serviceName: Common.UID.Service) {
return strapi.container.get('services').get(`${namespace}.${serviceName}`);
return strapi.get('services').get(`${namespace}.${serviceName}`);
},
get services() {
const services = strapi.container.get('services').getAll(namespace);
const services = strapi.get('services').getAll(namespace);
return removeNamespacedKeys(services, namespace);
},
policy(policyName: Common.UID.Policy) {
return strapi.container.get('policies').get(`${namespace}.${policyName}`);
return strapi.get('policies').get(`${namespace}.${policyName}`);
},
get policies() {
const policies = strapi.container.get('policies').getAll(namespace);
const policies = strapi.get('policies').getAll(namespace);
return removeNamespacedKeys(policies, namespace);
},
middleware(middlewareName: Common.UID.Middleware) {
return strapi.container.get('middlewares').get(`${namespace}.${middlewareName}`);
return strapi.get('middlewares').get(`${namespace}.${middlewareName}`);
},
get middlewares() {
const middlewares = strapi.container.get('middlewares').getAll(namespace);
const middlewares = strapi.get('middlewares').getAll(namespace);
return removeNamespacedKeys(middlewares, namespace);
},
controller(controllerName: Common.UID.Controller) {
return strapi.container.get('controllers').get(`${namespace}.${controllerName}`);
return strapi.get('controllers').get(`${namespace}.${controllerName}`);
},
get controllers() {
const controllers = strapi.container.get('controllers').getAll(namespace);
const controllers = strapi.get('controllers').getAll(namespace);
return removeNamespacedKeys(controllers, namespace);
},
};

View File

@ -4,12 +4,12 @@ import type { Strapi } from '@strapi/types';
export default async function loadAdmin(strapi: Strapi) {
strapi.admin = require('@strapi/admin/strapi-server');
strapi.container.get('services').add(`admin::`, strapi.admin?.services);
strapi.container.get('controllers').add(`admin::`, strapi.admin?.controllers);
strapi.container.get('content-types').add(`admin::`, strapi.admin?.contentTypes);
strapi.container.get('policies').add(`admin::`, strapi.admin?.policies);
strapi.container.get('middlewares').add(`admin::`, strapi.admin?.middlewares);
strapi.get('services').add(`admin::`, strapi.admin?.services);
strapi.get('controllers').add(`admin::`, strapi.admin?.controllers);
strapi.get('content-types').add(`admin::`, strapi.admin?.contentTypes);
strapi.get('policies').add(`admin::`, strapi.admin?.policies);
strapi.get('middlewares').add(`admin::`, strapi.admin?.middlewares);
const userAdminConfig = strapi.config.get('admin');
strapi.container.get('config').set('admin', _.merge(strapi.admin?.config, userAdminConfig));
strapi.get('config').set('admin', _.merge(strapi.admin?.config, userAdminConfig));
}

View File

@ -56,7 +56,7 @@ export default async function loadAPIs(strapi: Strapi) {
validateContentTypesUnicity(apis);
for (const apiName of Object.keys(apis)) {
strapi.container.get('apis').add(apiName, apis[apiName]);
strapi.get('apis').add(apiName, apis[apiName]);
}
}

View File

@ -61,5 +61,5 @@ export default async function loadComponents(strapi: Strapi) {
return acc;
}, {} as ComponentMap);
strapi.container.get('components').add(components);
strapi.get('components').add(components);
}

View File

@ -8,8 +8,8 @@ import { middlewares as internalMiddlewares } from '../../middlewares';
export default async function loadMiddlewares(strapi: Strapi) {
const localMiddlewares = await loadLocalMiddlewares(strapi);
strapi.container.get('middlewares').add(`global::`, localMiddlewares);
strapi.container.get('middlewares').add(`strapi::`, internalMiddlewares);
strapi.get('middlewares').add(`global::`, localMiddlewares);
strapi.get('middlewares').add(`strapi::`, internalMiddlewares);
}
const loadLocalMiddlewares = async (strapi: Strapi) => {

View File

@ -121,6 +121,6 @@ export default async function loadPlugins(strapi: Strapi) {
await applyUserExtension(plugins);
for (const pluginName of Object.keys(plugins)) {
strapi.container.get('plugins').add(pluginName, plugins[pluginName]);
strapi.get('plugins').add(pluginName, plugins[pluginName]);
}
}

View File

@ -25,5 +25,5 @@ export default async function loadPolicies(strapi: Strapi) {
}
}
strapi.container.get('policies').add(`global::`, policies);
strapi.get('policies').add(`global::`, policies);
}

View File

@ -1,5 +1,5 @@
import type { Strapi } from '@strapi/types';
export default (strapi: Strapi) => {
strapi.container.get('sanitizers').set('content-api', { input: [], output: [], query: [] });
strapi.get('sanitizers').set('content-api', { input: [], output: [], query: [] });
};

View File

@ -1,5 +1,5 @@
import type { Strapi } from '@strapi/types';
export default (strapi: Strapi) => {
strapi.container.get('validators').set('content-api', { input: [], query: [] });
strapi.get('validators').set('content-api', { input: [], query: [] });
};

View File

@ -16,7 +16,7 @@ const apisRegistry = (strapi: Strapi) => {
throw new Error(`API ${apiName} has already been registered.`);
}
const api = strapi.container.get('modules').add(`api::${apiName}`, apiConfig);
const api = strapi.get('modules').add(`api::${apiName}`, apiConfig);
apis[apiName] = api;

View File

@ -19,7 +19,7 @@ const pluginsRegistry = (strapi: Strapi) => {
throw new Error(`Plugin ${name} has already been registered.`);
}
const pluginModule = strapi.container.get('modules').add(`plugin::${name}`, pluginConfig);
const pluginModule = strapi.get('modules').add(`plugin::${name}`, pluginConfig);
plugins[name] = pluginModule;
return plugins[name];

View File

@ -3,7 +3,7 @@ import type { Strapi, CustomFields } from '@strapi/types';
const createCustomFields = (strapi: Strapi): CustomFields.CustomFields => {
return {
register(customField) {
strapi.container.get('custom-fields').add(customField);
strapi.get('custom-fields').add(customField);
},
};
};

View File

@ -32,7 +32,7 @@ const createAuthorizeMiddleware =
async (ctx, next) => {
const { auth, route } = ctx.state;
const authService = strapi.container.get('auth');
const authService = strapi.get('auth');
try {
await authService.verify(auth, getAuthConfig(route));
@ -54,7 +54,7 @@ const createAuthorizeMiddleware =
const createAuthenticateMiddleware =
(strapi: Strapi): Common.MiddlewareHandler =>
async (ctx, next) => {
return strapi.container.get('auth').authenticate(ctx, next);
return strapi.get('auth').authenticate(ctx, next);
};
const returnBodyMiddleware: Common.MiddlewareHandler = async (ctx, next) => {

View File

@ -3,17 +3,16 @@ import convertCustomFieldType from '../convert-custom-field-type';
describe('format attributes', () => {
it('replaces type customField with the underlying data type', () => {
global.strapi = {
container: {
// mock container.get('custom-fields')
// mock container.get('custom-fields')
get: jest.fn(() => ({
// mock container.get('custom-fields').get(uid)
get: jest.fn(() => ({
// mock container.get('custom-fields').get(uid)
get: jest.fn(() => ({
name: 'color',
plugin: 'mycustomfields',
type: 'text',
})),
name: 'color',
plugin: 'mycustomfields',
type: 'text',
})),
},
})),
contentTypes: {
test: {
attributes: {

View File

@ -23,7 +23,7 @@ const convertCustomFieldType = (strapi: Strapi) => {
for (const schemaAttrbutes of allSchemasAttributes) {
for (const attribute of Object.values(schemaAttrbutes)) {
if (attribute.type === 'customField') {
const customField = strapi.container.get('custom-fields').get(attribute.customField);
const customField = strapi.get('custom-fields').get(attribute.customField);
attribute.type = customField.type;
}
}

View File

@ -1,5 +1,4 @@
export interface Container {
register<T, U extends string>(name: U, resolver: T): Container;
add<T, U extends string>(name: U, resolver: T): Container;
get<T = any>(name: string, args?: unknown): T;
extend(): Container;
}

View File

@ -84,9 +84,8 @@ export interface ConfigProvider {
[key: string]: any;
}
export interface Strapi {
export interface Strapi extends Container {
server: Server;
container: Container;
log: Logger;
fs: StrapiFS;
eventHub: EventHub;

View File

@ -55,22 +55,20 @@ const addSignedFileUrlsToAdmin = async () => {
return;
}
strapi.container
.get('services')
.extend('plugin::content-manager.entity-manager', (entityManager) => {
/**
* Map entity manager responses to sign private media URLs
* @param {Object} entity
* @param {string} uid
* @returns
*/
const mapEntity = async (entity, uid) => {
const mappedEntity = await entityManager.mapEntity(entity, uid);
return signEntityMedia(mappedEntity, uid);
};
strapi.get('services').extend('plugin::content-manager.entity-manager', (entityManager) => {
/**
* Map entity manager responses to sign private media URLs
* @param {Object} entity
* @param {string} uid
* @returns
*/
const mapEntity = async (entity, uid) => {
const mappedEntity = await entityManager.mapEntity(entity, uid);
return signEntityMedia(mappedEntity, uid);
};
return { ...entityManager, mapEntity };
});
return { ...entityManager, mapEntity };
});
};
module.exports = {

View File

@ -188,7 +188,7 @@ const addCreateLocalizationAction = (contentType) => {
strapi.api[apiName].routes[modelName].routes.push(localizationRoute);
strapi.container.get('controllers').extend(`api::${apiName}.${modelName}`, (controller) => {
strapi.get('controllers').extend(`api::${apiName}.${modelName}`, (controller) => {
return Object.assign(controller, {
createLocalization: createLocalizationHandler(contentType),
});

View File

@ -7,7 +7,7 @@ const authStrategy = require('./strategies/users-permissions');
const sanitizers = require('./utils/sanitize/sanitizers');
module.exports = ({ strapi }) => {
strapi.container.get('auth').register('content-api', authStrategy);
strapi.get('auth').register('content-api', authStrategy);
strapi.sanitizers.add('content-api.output', sanitizers.defaultSanitizeOutput);
if (strapi.plugin('graphql')) {

View File

@ -14,9 +14,9 @@ interface Log extends Omit<Event, 'userId'> {
export default {
async register({ strapi }: { strapi: Strapi }) {
const contentTypes = strapi.container.get('content-types');
const contentTypes = strapi.get('content-types');
if (!contentTypes.keys().includes('admin::audit-log')) {
strapi.container.get('content-types').add('admin::', { 'audit-log': auditLogContentType });
strapi.get('content-types').add('admin::', { 'audit-log': auditLogContentType });
}
// Return the provider object

View File

@ -33,7 +33,7 @@ const createStrapiInstance = async ({
const instance = strapi(options);
if (bypassAuth) {
instance.container.get('auth').register('content-api', {
instance.get('auth').register('content-api', {
name: 'test-auth',
authenticate() {
return { authenticated: true };
@ -43,7 +43,7 @@ const createStrapiInstance = async ({
}
if (bootstrap) {
const modules = instance.container.get('modules');
const modules = instance.get('modules');
const originalBootstrap = modules.bootstrap;
// decorate modules bootstrap
modules.bootstrap = async () => {