Merge branch 'main' into fix/rbac-condition-and-relations

This commit is contained in:
Marc-Roig 2023-05-24 16:32:17 +02:00
commit 2e8e5b52bc
No known key found for this signature in database
GPG Key ID: FB4E2C43A0BEE249
522 changed files with 22488 additions and 20827 deletions

View File

@ -0,0 +1,6 @@
'use strict';
module.exports = {
preset: '../../../jest-preset.unit.js',
displayName: 'Github action check-pr-status',
};

View File

@ -1,6 +1,6 @@
{
"name": "check-pr-status",
"version": "4.10.1",
"version": "4.10.5",
"main": "dist/index.js",
"license": "MIT",
"private": true,

View File

@ -0,0 +1,88 @@
########################################################################################
# "yarn install" composite action for yarn 3/4+ and "nodeLinker: node-modules" #
#--------------------------------------------------------------------------------------#
# Requirement: @setup/node should be run before #
# #
# Usage in workflows steps: #
# #
# - name: 📥 Monorepo install #
# uses: ./.github/actions/yarn-nm-install #
# with: #
# enable-corepack: false # (default) #
# cache-install-state: false # (default) #
# cache-node-modules: false # (default) #
# #
# Reference: #
# - latest: https://gist.github.com/belgattitude/042f9caf10d029badbde6cf9d43e400a #
########################################################################################
name: 'Monorepo install (yarn)'
description: 'Run yarn install with node_modules linker and cache enabled'
inputs:
enable-corepack:
description: 'Enable corepack'
required: false
default: 'false'
cache-node-modules:
description: 'Cache node_modules, might speed up link step (invalidated lock/os/node-version/branch)'
required: false
default: 'false'
cache-install-state:
description: 'Cache yarn install state, might speed up resolution step when node-modules cache is activated (invalidated lock/os/node-version/branch)'
required: false
default: 'false'
runs:
using: 'composite'
steps:
- name: ⚙️ Enable Corepack
if: ${{ inputs.enable-corepack }} == 'true'
shell: bash
run: corepack enable
- name: ⚙️ Expose yarn config as "$GITHUB_OUTPUT"
id: yarn-config
shell: bash
env:
YARN_ENABLE_GLOBAL_CACHE: "false"
run: |
echo "CACHE_FOLDER=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
echo "CURRENT_NODE_VERSION="node-$(node --version)"" >> $GITHUB_OUTPUT
echo "CURRENT_BRANCH=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's,/,-,g')" >> $GITHUB_OUTPUT
- name: ♻️ Restore yarn cache
uses: actions/cache@v3
id: yarn-download-cache
with:
path: ${{ steps.yarn-config.outputs.CACHE_FOLDER }}
key: yarn-download-cache-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
restore-keys: |
yarn-download-cache-
- name: ♻️ Restore node_modules
if: inputs.cache-node-modules == 'true'
id: yarn-nm-cache
uses: actions/cache@v3
with:
path: '**/node_modules'
key: yarn-nm-cache-${{ runner.os }}-${{ steps.yarn-config.outputs.CURRENT_NODE_VERSION }}-${{ steps.yarn-config.outputs.CURRENT_BRANCH }}-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
- name: ♻️ Restore yarn install state
if: inputs.cache-install-state == 'true' && inputs.cache-node-modules == 'true'
id: yarn-install-state-cache
uses: actions/cache@v3
with:
path: .yarn/ci-cache
key: yarn-install-state-cache-${{ runner.os }}-${{ steps.yarn-config.outputs.CURRENT_NODE_VERSION }}-${{ steps.yarn-config.outputs.CURRENT_BRANCH }}-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
- name: 📥 Install dependencies
shell: bash
run: yarn install --immutable --inline-builds
env:
# Overrides/align yarnrc.yml options (v3, v4) for a CI context
YARN_ENABLE_GLOBAL_CACHE: "false" # Use local cache folder to keep downloaded archives
YARN_NM_MODE: "hardlinks-local" # Reduce node_modules size
YARN_INSTALL_STATE_PATH: ".yarn/ci-cache/install-state.gz" # Might speed up resolutions when node_modules present
# Other environment variables
HUSKY: '0' # By default do not run HUSKY install

View File

@ -4,6 +4,9 @@ updates:
directory: /
schedule:
interval: weekly
day: sunday
time: '22:00'
open-pull-requests-limit: 10
versioning-strategy: increase
ignore:
# Only allow patch as minor babel versions need to be upgraded all together
@ -21,9 +24,12 @@ updates:
- 'source: dependencies'
- 'pr: chore'
- package-ecosystem: github-actions
open-pull-requests-limit: 10
directory: /
schedule:
interval: weekly
day: sunday
time: '22:00'
labels:
- 'source: dependencies'
- 'pr: chore'

18
.github/filters.yaml vendored Normal file
View File

@ -0,0 +1,18 @@
backend:
- '.github/actions/yarn-nm-install/*.yml'
- '.github/workflows/**'
- 'packages/**/package.json'
- 'packages/**/server/**/*.(js|ts)'
- 'packages/**/strapi-server.js'
- 'packages/{utils,generators,cli,providers}/**'
- 'packages/core/*/{lib,bin,ee}/**'
- 'api-tests/**'
frontend:
- '.github/actions/yarn-nm-install/*.yml'
- '.github/workflows/**'
- 'packages/**/package.json'
- 'packages/**/admin/src/**'
- 'packages/**/admin/ee/admin/**'
- 'packages/**/strapi-admin.js'
- 'packages/core/helper-plugin/**'
- 'packages/admin-test-utils/**'

View File

@ -1,3 +0,0 @@
module.exports = {
displayName: '.github',
};

View File

@ -27,8 +27,9 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 18
path: '**/node_modules'
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- uses: preactjs/compressed-size-action@v2
with:

View File

@ -23,7 +23,4 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- uses: ./.github/actions/security/lockfile

View File

@ -0,0 +1,36 @@
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
name: Cleanup caches for closed branches
on:
pull_request:
types:
- closed
workflow_dispatch:
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: 🧹 Cleanup
run: |
gh extension install actions/gh-actions-cache
REPO=${{ github.repository }}
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
echo "Fetching list of cache key"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeysForPR
do
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
done
echo "Done"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -33,8 +33,6 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 18
path: '**/node_modules'
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- name: Install dependencies
run: yarn install --immutable

View File

@ -23,6 +23,6 @@ jobs:
with:
node-version: 16
- run: yarn
- run: ./scripts/remove-dist-tag
- run: ./scripts/remove-dist-tag.sh
env:
DIST_TAG: ${{ github.event.inputs.dist-tag }}

View File

@ -11,12 +11,28 @@ concurrency:
cancel-in-progress: true
jobs:
changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
nonDoc: ${{ steps.filter.outputs.nonDoc }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
# For pull requests it's not necessary to checkout the code
- uses: dorny/paths-filter@v2
id: filter
with:
filters: .github/filters.yaml
lint:
name: 'lint (node: ${{ matrix.node }})'
runs-on: ubuntu-latest
strategy:
matrix:
node: [14, 16, 18]
node: [18]
steps:
- run: echo "Skipped"
@ -34,6 +50,16 @@ jobs:
name: 'unit_front (node: ${{ matrix.node }})'
needs: [lint]
runs-on: ubuntu-latest
strategy:
matrix:
node: [18]
steps:
- run: echo "Skipped"
build:
name: 'build (node: ${{ matrix.node }})'
needs: [changes, lint, unit_front]
runs-on: ubuntu-latest
strategy:
matrix:
node: [14, 16, 18]

View File

@ -18,8 +18,25 @@ permissions:
actions: read
jobs:
changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: dorny/paths-filter@v2
id: filter
with:
filters: .github/filters.yaml
lint:
name: 'lint (node: ${{ matrix.node }})'
needs: [changes]
runs-on: ubuntu-latest
strategy:
matrix:
@ -31,12 +48,9 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- uses: nrwl/nx-set-shas@v3
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- name: Run build:ts
run: yarn nx run-many --target=build:ts --nx-ignore-cycles --skip-nx-cache
- name: Run lint
@ -44,7 +58,7 @@ jobs:
unit_back:
name: 'unit_back (node: ${{ matrix.node }})'
needs: [lint]
needs: [changes, lint]
runs-on: ubuntu-latest
strategy:
matrix:
@ -56,12 +70,9 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- uses: nrwl/nx-set-shas@v3
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- name: Run build:ts
run: yarn nx run-many --target=build:ts --nx-ignore-cycles --skip-nx-cache
- name: Run tests
@ -69,7 +80,7 @@ jobs:
unit_front:
name: 'unit_front (node: ${{ matrix.node }})'
needs: [lint]
needs: [changes, lint]
runs-on: ubuntu-latest
strategy:
matrix:
@ -81,20 +92,17 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- uses: nrwl/nx-set-shas@v3
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- name: Run build:ts for admin-test-utils
run: yarn build:ts --projects=@strapi/admin-test-utils --skip-nx-cache
run: yarn build --projects=@strapi/admin-test-utils,@strapi/helper-plugin --skip-nx-cache
- name: Run test
run: yarn nx affected --target=test:front --nx-ignore-cycles
build:
name: 'build (node: ${{ matrix.node }})'
needs: [lint, unit_front]
needs: [changes, lint, unit_front]
runs-on: ubuntu-latest
strategy:
matrix:
@ -104,17 +112,15 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- name: Build
run: yarn build --projects=@strapi/admin,@strapi/helper-plugin
api_ce_pg:
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
needs: [lint, unit_back, unit_front]
needs: [changes, lint, unit_back, unit_front]
name: '[CE] API Integration (postgres, node: ${{ matrix.node }})'
strategy:
matrix:
@ -143,18 +149,16 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- uses: ./.github/actions/run-api-tests
with:
dbOptions: '--dbclient=postgres --dbhost=localhost --dbport=5432 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi'
api_ce_mysql:
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
needs: [lint, unit_back, unit_front]
needs: [changes, lint, unit_back, unit_front]
name: '[CE] API Integration (mysql:latest, client: ${{ matrix.db_client }}, node: ${{ matrix.node }})'
strategy:
matrix:
@ -182,18 +186,16 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- uses: ./.github/actions/run-api-tests
with:
dbOptions: '--dbclient=${{ matrix.db_client }} --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi'
api_ce_mysql_5:
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
needs: [lint, unit_back, unit_front]
needs: [changes, lint, unit_back, unit_front]
name: '[CE] API Integration (mysql:5, client: ${{ matrix.db_client }} , node: ${{ matrix.node }})'
strategy:
matrix:
@ -220,33 +222,28 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- uses: ./.github/actions/run-api-tests
with:
dbOptions: '--dbclient=${{ matrix.db_client }} --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi'
api_ce_sqlite:
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
needs: [lint, unit_back, unit_front]
needs: [changes, lint, unit_back, unit_front]
name: '[CE] API Integration (sqlite, client: ${{ matrix.sqlite_pkg }}, node: ${{ matrix.node }})'
strategy:
matrix:
node: [14, 16, 18]
sqlite_pkg: ['better-sqlite3', 'sqlite3', '@vscode/sqlite3']
sqlite_pkg: ['better-sqlite3', 'sqlite3']
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- uses: ./.github/actions/run-api-tests
env:
SQLITE_PKG: ${{ matrix.sqlite_pkg }}
@ -256,9 +253,9 @@ jobs:
# EE
api_ee_pg:
runs-on: ubuntu-latest
needs: [lint, unit_back, unit_front]
needs: [changes, lint, unit_back, unit_front]
name: '[EE] API Integration (postgres, node: ${{ matrix.node }})'
if: github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]')
if: needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]')
env:
STRAPI_LICENSE: ${{ secrets.strapiLicense }}
strategy:
@ -288,11 +285,8 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- uses: ./.github/actions/run-api-tests
with:
dbOptions: '--dbclient=postgres --dbhost=localhost --dbport=5432 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi'
@ -300,9 +294,9 @@ jobs:
api_ee_mysql:
runs-on: ubuntu-latest
needs: [lint, unit_back, unit_front]
needs: [changes, lint, unit_back, unit_front]
name: '[EE] API Integration (mysql:latest, client: ${{ matrix.db_client }}, node: ${{ matrix.node }})'
if: github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]')
if: needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]')
env:
STRAPI_LICENSE: ${{ secrets.strapiLicense }}
strategy:
@ -331,11 +325,8 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- uses: ./.github/actions/run-api-tests
with:
dbOptions: '--dbclient=${{ matrix.db_client }} --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi'
@ -343,25 +334,22 @@ jobs:
api_ee_sqlite:
runs-on: ubuntu-latest
needs: [lint, unit_back, unit_front]
needs: [changes, lint, unit_back, unit_front]
name: '[EE] API Integration (sqlite, client: ${{ matrix.sqlite_pkg }}, node: ${{ matrix.node }})'
if: github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]')
if: needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]')
env:
STRAPI_LICENSE: ${{ secrets.strapiLicense }}
strategy:
matrix:
node: [14, 16, 18]
sqlite_pkg: ['better-sqlite3', 'sqlite3', '@vscode/sqlite3']
sqlite_pkg: ['better-sqlite3', 'sqlite3']
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v3
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn install --immutable
- name: Monorepo install
uses: ./.github/actions/yarn-nm-install
- uses: ./.github/actions/run-api-tests
env:
SQLITE_PKG: ${{ matrix.sqlite_pkg }}

View File

@ -2,4 +2,3 @@
. "$(dirname -- "$0")/_/husky.sh"
yarn lint-staged
yarn nx affected:lint --uncommitted --nx-ignore-cycles

31
LICENSE
View File

@ -1,22 +1,15 @@
Copyright (c) 2015-present Strapi Solutions SAS
Portions of the Strapi software are licensed as follows:
* All software that resides under an "ee/" directory (the “EE Software”), if that directory exists, is licensed under the license defined in "ee/LICENSE".
* All software outside of the above-mentioned directories or restrictions above is available under the "MIT Expat" license as set forth below.
* If you are accessing or using any component of the software that resides under an "ee/" directory, then you are deemed to be using our “Enterprise Edition” of the software and you understand and agree that the software is not licensed under the "MIT Expat" license as set forth below but instead, all the software you access is licensed under the license defined in "strapi/packages/core/admin/ee/LICENSE" and located at https://github.com/strapi/strapi/blob/a76b557047e9ef1c168dbf1b6cf879bcc3022de6/packages/core/admin/ee/LICENSE, unless (a) you or the company you represent has signed an alternative agreement referencing this code, then such signed agreement applies or (b) you are using the software in connection with a subscription to our cloud offering, then the terms of the agreement relevant to the cloud offering which you have assented to apply and the software licenses included in that agreement shall apply.
* If (a) you are not accessing or using the software that resides under an “ee/” directory and therefore you are only accessing or using our “Community Edition” of the Software and (b) you have no registered account on our cloud offering, then we are providing you the software under the "MIT Expat" license as set forth below.
MIT Expat License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -279,10 +279,25 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
];
});
test("It should assign a default color to stages if they don't have one", async () => {
await requests.admin.put(`/admin/review-workflows/workflows/${testWorkflow.id}/stages`, {
body: {
data: [defaultStage, { id: secondStage.id, name: secondStage.name, color: '#000000' }],
},
});
const workflowRes = await requests.admin.get(
`/admin/review-workflows/workflows/${testWorkflow.id}?populate=*`
);
expect(workflowRes.status).toBe(200);
expect(workflowRes.body.data.stages[0].color).toBe('#4945FF');
expect(workflowRes.body.data.stages[1].color).toBe('#000000');
});
test("It shouldn't be available for public", async () => {
const stagesRes = await requests.public.put(
`/admin/review-workflows/workflows/${testWorkflow.id}/stages`,
stagesUpdateData
{ body: { data: stagesUpdateData } }
);
const workflowRes = await requests.public.get(
`/admin/review-workflows/workflows/${testWorkflow.id}`
@ -353,6 +368,19 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
expect(workflowRes.body.data).toBeUndefined();
}
});
test('It should throw an error if trying to create more than 200 stages', async () => {
const stagesRes = await requests.admin.put(
`/admin/review-workflows/workflows/${testWorkflow.id}/stages`,
{ body: { data: Array(201).fill({ name: 'new stage' }) } }
);
if (hasRW) {
expect(stagesRes.status).toBe(400);
expect(stagesRes.body.error).toBeDefined();
expect(stagesRes.body.error.name).toEqual('ValidationError');
expect(stagesRes.body.error.message).toBeDefined();
}
});
});
describe('Enabling/Disabling review workflows on a content type', () => {
@ -416,7 +444,7 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
});
});
describe('update a stage on an entity', () => {
describe('Update a stage on an entity', () => {
describe('Review Workflow is enabled', () => {
beforeAll(async () => {
await updateContentType(productUID, {

View File

@ -7,6 +7,7 @@
const { createStrapiInstance } = require('api-tests/strapi');
const { createAuthRequest } = require('api-tests/request');
const modelsUtils = require('api-tests/models');
const { createTestBuilder } = require('api-tests/builder');
let strapi;
let rq;
@ -17,8 +18,28 @@ const restart = async () => {
rq = await createAuthRequest({ strapi });
};
const builder = createTestBuilder();
const localTestData = {
models: {
dog: {
singularName: 'dog',
pluralName: 'dogs',
collectionName: 'dogs-collection',
displayName: 'Dog Display',
kind: 'collectionType',
attributes: {
name: {
type: 'string',
},
},
},
},
};
describe('Content Type Builder - Content types', () => {
beforeAll(async () => {
await builder.addContentType(localTestData.models.dog).build();
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
});
@ -40,6 +61,7 @@ describe('Content Type Builder - Content types', () => {
await modelsUtils.deleteContentTypes(modelsUIDs, { strapi });
await strapi.destroy();
await builder.cleanup();
});
describe('Collection Types', () => {
@ -92,7 +114,7 @@ describe('Content Type Builder - Content types', () => {
expect(res.body).toMatchSnapshot();
});
test('Successfull creation of a collection type with draftAndPublish enabled', async () => {
test('Successful creation of a collection type with draftAndPublish enabled', async () => {
const res = await rq({
method: 'POST',
url: '/content-type-builder/content-types',
@ -129,6 +151,54 @@ describe('Content Type Builder - Content types', () => {
expect(res.body).toMatchSnapshot();
});
test.each([
['singularName', 'singularName'],
['singularName', 'pluralName'],
['pluralName', 'singularName'],
['pluralName', 'pluralName'],
['pluralName', 'collectionName'],
])(`Cannot use %p that exists as another type's %p`, async (sourceField, matchField) => {
const body = {
contentType: {
displayName: 'Frogs Frogs Frogs',
pluralName: 'safe-plural-name',
singularName: 'safe-singular-name',
collectionName: 'safe-collection-name',
attributes: {
name: {
type: 'string',
},
},
},
};
// set the conflicting name in the given field
body.contentType[sourceField] = localTestData.models.dog[matchField];
const res = await rq({
method: 'POST',
url: '/content-type-builder/content-types',
body,
});
expect(res.statusCode).toBe(400);
expect(res.body).toEqual({
error: {
details: {
errors: [
{
message: `contentType: name \`${body.contentType[sourceField]}\` is already being used by another content type.`,
name: 'ValidationError',
path: ['contentType', sourceField],
},
],
},
message: `contentType: name \`${body.contentType[sourceField]}\` is already being used by another content type.`,
name: 'ValidationError',
},
});
});
test('Cannot use same string for singularName and pluralName', async () => {
const res = await rq({
method: 'POST',
@ -192,21 +262,11 @@ describe('Content Type Builder - Content types', () => {
name: 'ValidationError',
path: ['contentType', 'displayName'],
},
{
message: 'Content Type name `undefined` is already being used.',
name: 'ValidationError',
path: ['contentType', 'singularName'],
},
{
message: 'contentType.singularName is a required field',
name: 'ValidationError',
path: ['contentType', 'singularName'],
},
{
message: 'Content Type name `undefined` is already being used.',
name: 'ValidationError',
path: ['contentType', 'pluralName'],
},
{
message: 'contentType.pluralName is a required field',
name: 'ValidationError',
@ -219,7 +279,7 @@ describe('Content Type Builder - Content types', () => {
},
],
},
message: '6 errors occurred',
message: '4 errors occurred',
name: 'ValidationError',
},
});

View File

@ -225,6 +225,27 @@ describe('transactions', () => {
expect(end[0].key).toEqual(original[0].key);
});
test('onCommit hook works', async () => {
let count = 0;
await strapi.db.transaction(({ onCommit, onRollback }) => {
onCommit(() => count++);
});
expect(count).toEqual(1);
});
test('onRollback hook works', async () => {
let count = 0;
try {
await strapi.db.transaction(({ onRollback }) => {
onRollback(() => count++);
throw new Error('test');
});
} catch (e) {
// do nothing
}
expect(count).toEqual(1);
});
});
describe('using a transaction object', () => {

View File

@ -220,6 +220,34 @@ describe('Bulk actions for folders & files', () => {
const existingfoldersIds = resFolder.body.data.map((f) => f.id);
expect(existingfoldersIds).toEqual(expect.not.arrayContaining([folder.id]));
});
test('Can delete folders without deleting others with similar path', async () => {
// Ensure the folder algo does not only delete by folders that start with the same path (startswith /1 matches /10 too)
// Delete all previous folders, so first file path is /1
await rq
.get('/upload/folders')
.then((res) => res.body.data.map((f) => f.id))
.then((folderIds) => rq.post('/upload/actions/bulk-delete', { body: { folderIds } }));
// Create folders
const folder1 = await createFolder('folderToDelete', null);
for (let i = 0; i < 20; i++) {
await createFolder(`folderToKeep-${i}`, null);
}
// Delete folder1
await rq.post('/upload/actions/bulk-delete', {
body: { folderIds: [folder1.id] },
});
const folderIds = await rq
.get('/upload/folders')
.then((res) => res.body.data.map((f) => f.id));
// Should include all folders except the one we deleted
expect(folderIds.length).toBe(20);
expect(folderIds).toEqual(expect.not.arrayContaining([folder1.id]));
});
});
describe('move', () => {

9
docs/.eslintrc.docs.js Normal file
View File

@ -0,0 +1,9 @@
module.exports = {
parserOptions: {
sourceType: 'module',
},
plugins: ['react'],
rules: {
'react/prop-types': 0,
},
};

598
docs/docs/api/Strapi.mdx Normal file
View File

@ -0,0 +1,598 @@
---
title: Strapi (WIP)
slug: /api/Strapi
tags:
- class
- public
- global
toc_min_heading_level: 2
toc_max_heading_level: 3
---
import Type from '@site/docs/api/components/type';
# Strapi
:::info
Current state: **Stable**
:::
The Strapi class is the main object used in Strapi projects.
An instance of Strapi class is available as a global in any Strapi project: `global.strapi`.
## Class: Strapi
### `new Strapi(opts)`
- `opts`: <Type>Object</Type> Options that can be used on Strapi startup
- `autoReload`: <Type>Boolean</Type> **Default:** true
- If false, deactivate auto reload
- If you modify any file in your Strapi project, it reloads your nodejs app
- If any content-type is changed, it will reload the nodejs app
- `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()`
- 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
- 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.
```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.
- `dirs.dist`: <Type>[StrapiPathObject](#strapipathobject)</Type>
- Build folder
- `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..)
#### 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
- 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
- A set of function that wrap around routes and requests
- `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
- `false`: There is something loading
Note: `register` functions are called before the `bootstrap` functions.
### `strapi.reload()`
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`);
});
// 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.
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 });
// Log a warning message
strapi.log.warn('Server is running in development mode');
// Log an informational message
strapi.log.info(`Server started on port ${PORT}`);
// Log a verbose message
strapi.log.verbose('Application state', { user: currentUser });
// Log a debug message
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.
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>
- `store`: <Type>Any</Type> Value that should be retrieved
- `cb`: <Type>Function</Type> Callback
- `get()` <Type>Function</Type>
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)`.
#### Examples
```javascript
strapi.customFields.register({
name: 'color',
plugin: 'color-picker',
type: 'string',
});
```
### `strapi.config`
- <Type>Object</Type>
Shortcut to `strapi.container.get('config')`.
See the [config container](#config).
### `strapi.services`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('services').getAll()`.
See the [services' container](#services).
### `strapi.service(uid)`
- `uid`: <Type>String</Type>
Shortcut to `strapi.container.get('services').get(uid)`.
See the [services' container](#services).
### `strapi.controllers`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('controllers').getAll()`.
See the [controllers' container](#controllers).
### `strapi.controller(uid)`
- `uid`: <Type>String</Type>
Shortcut to `strapi.container.get('controllers').get(uid)`.
See the [controllers' container](#controllers).
### `strapi.contentTypes`
- <Type>Object[]</Type>
Shortcut to `strapi.container.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)`.
See the [content-types' container](#content-types).
### `strapi.policies`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('policies').getAll()`.
See the [policies' container](#policies).
### `strapi.policy(name)`
- `name`: <Type>String</Type>
Shortcut to `strapi.container.get('policies').get(name)`.
See the [policies' container](#policies).
### `strapi.middlewares`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('middlewares').getAll()`.
See the [middlewares container](#middlewares).
### `strapi.middleware(name)`
- `name`: <Type>String</Type>
Shortcut to `strapi.container.get('middlewares').get(name)`.
See the [middlewares container](#middlewares).
### `strapi.plugins`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('plugins').getAll()`.
See the [plugins' container](#plugins).
### `strapi.plugin(name)`
- `name`: <Type>String</Type>
Shortcut to `strapi.container.get('plugins').get(name)`.
See the [plugins' container](#plugins).
### `strapi.hooks`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('hooks').getAll()`.
See the [hooks' container](#hooks).
### `strapi.hook(name)`
- `name`: <Type>String</Type>
Shortcut to `strapi.container.get('hooks').get(name)`.
See the [hooks' container](#hooks).
### `strapi.api`
- <Type>Object[]</Type>
Shortcut to `strapi.container.get('apis').getAll()`.
See the [apis container](#apis).
### `strapi.auth`
- <Type>Object</Type>
Shortcut to `strapi.container.get('auth')`.
See the [auth' container](#auth).
### `strapi.contentAPI`
- <Type>Object</Type>
Shortcut to `strapi.container.get('content-api')`.
See the [content-api container](#content-api).
### `strapi.sanitizers`
- <Type>Object</Type>
Shortcut to `strapi.container.get('sanitizers')`.
See the [sanitizers' container](#sanitizers).
### `strapi.start()`
- Returns: Promise
:::info
TODO
:::
### `strapi.destroy()`
- Returns: Promise
:::info
TODO
:::
### `strapi.sendStartupTelemetry()`
:::info
TODO
:::
### `strapi.openAdmin({ isInitialized })`
- Returns: Promise
:::info
TODO
:::
### `strapi.postListen()`
- Returns: Promise
:::info
TODO
:::
### `strapi.listen()`
- Returns: Promise
:::info
TODO
:::
### `strapi.stopWithError()`
:::info
TODO
:::
### `strapi.stop(exitCode)`
:::info
TODO
:::
### `strapi.loadAdmin()`
- Returns: Promise
:::info
TODO
:::
### `strapi.loadPlugins()`
- Returns: Promise
:::info
TODO
:::
### `strapi.loadPolicies()`
- Returns: Promise
:::info
TODO
:::
### `strapi.loadAPIs()`
- Returns: Promise
:::info
TODO
:::
### `strapi.loadComponents()`
- Returns: Promise
:::info
TODO
:::
### `strapi.loadMiddlewares()`
- Returns: Promise
:::info
TODO
:::
### `strapi.loadApp()`
- Returns: Promise
:::info
TODO
:::
### `strapi.loadSanitizers()`
- Returns: Promise
:::info
TODO
:::
### `strapi.registerInternalHooks()`
:::info
TODO
:::
### `strapi.register()`
- Returns: Promise
:::info
TODO
:::
### `strapi.bootstrap()`
- Returns: Promise
:::info
TODO
:::
### `strapi.load()`
- Returns: Promise
:::info
TODO
:::
### `strapi.startWebhooks()`
- Returns: Promise
:::info
TODO
:::
### `strapi.reload()`
:::info
TODO
:::
### `strapi.runLifecyclesFunctions()`
- Returns: Promise
:::info
TODO
:::
### `strapi.getModel(uid)`
- `uid`: <Type>String</Type>
:::info
TODO
:::
### `strapi.query(uid)`
- `uid`: <Type>String</Type>
:::info
TODO
:::
## Strapi containers
The strapi containers are accessible via `strapi.container.get('name-of-the-container')`.
### `config`
- <Type>Object</Type>
- `get(path, defaultValue)`: <Type>Function</Type>
- `path`: <Type>String</Type>
- `defaultValue`: <Type>Any</Type>
- Returns: <Type>Any</Type> - The value located at `path` or, if undefined, `defaultValue`.
- `set(path, value)`: <Type>Function</Type>
- `path`: <Type>String</Type> - Where the value should be stored
- `value`: <Type>Any</Type>
- `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()`
Date in milliseconds when the server has started
- `serveAdminPanel`: <Type>Boolean</Type> **Default:** `true`
See [Strapi constructor](#new-strapiopts) options
- `autoReload`: <Type>Boolean</Type> **Default:** `false`
See [Strapi constructor](#new-strapiopts) options
- `environment`: <Type>String</Type> - process.env.NODE_ENV
- `uuid`: <Type>String</Type> - string extracted from `package.json` located in `strapi.uuid`
- `packageJsonStrapi`: <Type>Object</Type> - object extracted from `package.json` located in `strapi` (except uuid)
- `info`: <Type>Object</Type>
- everything stored in the `package.json`
- `strapi`: <Type>String</Type> - Current version of Strapi
Every file stored under the `config` folder will be injected in this config container object.
### `services`
:::info
TODO
:::
### `controllers`
:::info
TODO
:::
### `content-types`
:::info
TODO
:::
### `policies`
:::info
TODO
:::
### `plugins`
:::info
TODO
:::
### `hooks`
:::info
TODO
:::
### `apis`
:::info
TODO
:::
### `auth`
:::info
TODO
:::
### `content-api`
:::info
TODO
:::
### `sanitizers`
:::info
TODO
:::

29
docs/docs/api/api.mdx Normal file
View File

@ -0,0 +1,29 @@
---
title: API (WIP)
slug: /api/API
tags:
- module
- public
toc_min_heading_level: 2
toc_max_heading_level: 3
---
import Type from '@site/docs/api/components/type';
# API
:::info
Current state: **Stable**
:::
The Strapi API module permits to generate a Strapi API object that wrap all the functionalities around Strapi endpoints
## Module: API
### `createAPI(strapi, opts)`
:::info
TODO
:::

View File

@ -0,0 +1,13 @@
import React from 'react';
export default function Type({ children }) {
return (
<span
style={{
color: '#017501',
}}
>
&lt;{children}&gt;
</span>
);
}

102
docs/docs/api/container.mdx Normal file
View File

@ -0,0 +1,102 @@
---
title: Container
slug: /api/container
tags:
- module
- public
toc_min_heading_level: 2
toc_max_heading_level: 5
---
import Type from '@site/docs/api/components/type';
# Container
:::info
Current state: **Stable**
:::
The container module permits to generate containers.
## Module: container
### `createContainer(strapi)`
- `strapi`: <Type>Strapi</Type> [See Strapi class documentation](Strapi.mdx)
- Returns: <Type>Container</Type>
```javascript
const container = createContainer(strapi);
container.register('config', {
get: (configName) => {},
set: (configName, value) => {}
});
const dbConfig = container.get('config').get('database');
```
### `container.register(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)
- `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);
container.register('config', ({ strapi }, args) => {});
// or
container.register('services', {});
```
### `container.get(name, args)`
- `name`: <Type>String</Type> UID of the content
- `args`: <Type>Any</Type> Value that will be passed to the resolver (if function)
Get the value stored for a specific `name`.
```javascript
const container = createContainer(strapi);
container.register('config', { db: 'sqlite' });
const config = container.get('config');
// config.db === 'sqlite'
```
⚠️ If the **resolver**, used in the [register function](#containerregistername-resolver), is a **function**, the value will be the result of this resolver function with `args` as parameter on the first call to `get`.
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);
container.register('boolean', (bool) => bool);
// First call - The value is resolved through the resolver above "(bool) => bool"
container.get('boolean', true);
// true
// Any further call will use the previously set value
container.get('boolean');
// true
// Even if we try to push a new value
container.get('boolean', false);
// true
```
### `container.extend()`
:::info
To be developed
:::

195
docs/docs/api/cron.mdx Normal file
View File

@ -0,0 +1,195 @@
---
title: Cron Service
slug: /api/Cron
tags:
- module
- public
toc_min_heading_level: 2
toc_max_heading_level: 3
---
import Type from '@site/docs/api/components/type';
# Cron
:::info
Current state: **Stable**
:::
The Strapi Cron Service provides a way to add, remove, start, and stop cron jobs in a Strapi application.
## Module: Cron Service
### createCronService()
The `createCronService()` function returns an object that provides methods to manage cron jobs.
## Methods
### `cron.add(tasks)`
- `tasks`: <Type>Object</Type>
- Returns: `this`
Adds one or more cron tasks to the service.
- Each key of the `tasks` object is the name of the task.
- Each value of the `tasks` object can be either a function, or an object with two properties: `task` and `options`.
- If the value is a function, it is used as the task to be executed when the cron expression is met.
- The key will be considered as the cron expression
- If the value is an object, its `task` property is used as the task function, and its `options` property is used as the cron expression options.
#### Example
```javascript
const { createCronService } = require('packages/core/strapi/lib/services/cron.js');
const cron = createCronService();
const task = () => {
console.log('Task executed!');
};
cron.add({
myTask: {
task,
options: '*/5 * * * *', // Executes every 5 minutes.
},
'*/1 * * * *': () => console.log('A minute has passed.'),
});
```
### `cron.remove(name)`
- `name`: <Type>String</Type>
- Returns: `this`
Removes a cron task from the service.
- The `name` parameter is the name of the task to remove.
#### Example
```javascript
const { createCronService } = require('packages/core/strapi/lib/services/cron.js');
const cron = createCronService();
const task = () => {
console.log('Task executed!');
};
cron.add({
myTask: {
task,
options: '*/5 * * * *', // Executes every 5 minutes.
},
});
cron.remove('myTask');
```
### `cron.start()`
- Returns: `this`
Starts the cron service.
- Schedules all the cron jobs.
#### Example
```javascript
const { createCronService } = require('packages/core/strapi/lib/services/cron.js');
const cron = createCronService();
const task = () => {
console.log('Task executed!');
};
cron.add({
myTask: {
task,
options: '*/5 * * * *', // Executes every 5 minutes.
},
});
cron.start();
```
### `cron.stop()`
- Returns: `this`
Stops the cron service.
- Cancels all the scheduled jobs.
#### Example
```javascript
const { createCronService } = require('packages/core/strapi/lib/services/cron.js');
const cron = createCronService();
const task = () => {
console.log('Task executed!');
};
cron.add({
myTask: {
task,
options: '*/5 * * * *', // Executes every 5 minutes.
},
});
// Start the scheduled cron jobs
cron.start();
// Stops the cron jobs
cron.stop();
```
### `cron.destroy()`
- Returns: `this`
Destroys the cron service.
- Calls the `stop()` method.
- Clears the list of cron jobs.
#### Example
```javascript
const { createCronService } = require('packages/core/strapi/lib/services/cron.js');
const cron = createCronService();
const task = () => {
console.log('Task executed!');
};
cron.add({
myTask: {
task,
options: '*/5 * * * *', // Executes every 5 minutes.
},
});
// Start the scheduled cron jobs
cron.start();
// Stops the cron jobs and remove all scheduled tasks
cron.destroy();
```
## Properties
### `cron.jobs`
- <Type>Array</Type>
- <Type>Object</Type>
- `job`: [<Type>Job</Type>](https://github.com/node-schedule/node-schedule) - Job object by node-schedule
- `options`: <Type>String</Type> - String representing the recurrence of the job ( like '*/5 * * * *' )
- `name`: <Type>String</Type> - The name of the task associated to the job
An array of the cron jobs added to the service.

View File

@ -0,0 +1,29 @@
---
title: EventHub (WIP)
slug: /api/EventHub
tags:
- module
- public
toc_min_heading_level: 2
toc_max_heading_level: 3
---
import Type from '@site/docs/api/components/type';
# Strapi Event Hub
:::info
Current state: **Stable**
:::
Strapi Event Hub module - description to be done
## Module: EventHub
### `createEventHub()`
:::info
TODO
:::

View File

@ -1 +0,0 @@
# API

View File

@ -0,0 +1,72 @@
---
title: Startup Logger
slug: /api/StartupLogger
tags:
- module
- private
toc_min_heading_level: 2
toc_max_heading_level: 3
---
import Type from '@site/docs/api/components/type';
# API
:::info
Current state: **Stable**
:::
This module is a simple logger for starting up Strapi with some useful information.
## Module: Startup Logger
### `logStats()`
This log will display information about the instance of Strapi. The time launched, how many times it took and important configuration information.
```
Project information
┌────────────────────┬──────────────────────────────────────────────────┐
│ Time │ Wed Jan 01 2000 00:00:01 GMT+0200 (Central Euro… │
│ Launched in │ 2000 ms │
│ Environment │ development │
│ Process PID │ 42 │
│ Version │ 4.9.0 (node v18.12.1) │
│ Edition │ Enterprise │
│ Database │ postgres │
└────────────────────┴──────────────────────────────────────────────────┘
```
### `logFirstStartupMessage()`
This log will display the first time Strapi project is launched. It will ask the user to create its first admin user in the admin panel.
### `logDefaultStartupMessage()`
Default message to display when the Strapi server is started.
```
Actions available
Welcome back!
To manage your project 🚀, go to the administration panel at:
http://localhost:1337/admin
To access the server ⚡️, go to:
http://localhost:1337
```
### `logStartupMessage({ isInitialized })`
- `isInitialized`: <Type>Boolean</Type> Has the Strapi project already been initialized?
Will display the correct start-up message according to the specified boolean.
:::note
Can be disabled by setting `STRAPI_HIDE_STARTUP_MESSAGE` to `true`.
:::

View File

@ -0,0 +1,42 @@
---
title: StrapiFS (WIP)
slug: /api/StrapiFS
tags:
- module
- public
toc_min_heading_level: 2
toc_max_heading_level: 3
---
import Type from '@site/docs/api/components/type';
# Strapi File System
:::info
Current state: **Stable**
:::
The Strapi FS module is a wrapper around FS NodeJS module to manipulate local files.
## Module: StrapiFS
### `createStrapiFs(strapi)`
### `strapiFs.writeAppFile(optPath, data)`
:::caution
Deprecated
:::
### `strapiFs.writePluginFile(plugin, optPath, data)`
:::caution
Deprecated
:::
### `strapiFs.removeAppFile(optPath)`
:::caution
Deprecated
:::
### `strapiFs.appendFile(optPath, data)`

View File

@ -0,0 +1,117 @@
---
title: Strapi server (WIP)
slug: /api/strapi-server
tags:
- module
- public
toc_min_heading_level: 2
toc_max_heading_level: 5
---
import Type from '@site/docs/api/components/type';
# Strapi Server
:::info
Current state: **Stable**
:::
The Strapi server module permits to generate a Strapi http server.
## Module: Strapi server
### `createServer(strapi)`
- `strapi`: [<Type>Strapi</Type>](Strapi)
- Returns: <Type>StrapiServer</Type>
```javascript
const server = createServer(strapi);
server.listRoutes();
```
### `StrapiServer.app`
- [<Type>KoaJS</Type>](https://devdocs.io/koa/index)
Strapi projects are using KoaJS to run the NodeJS server.
### `StrapiServer.router`
- [<Type>@koa/router</Type>](https://github.com/ZijianHe/koa-router#router-)
Strapi projects are using a dependency of KoaJS called @koa/router.
### `StrapiServer.httpServer`
- [<Type>http.Server</Type>](https://nodejs.org/docs/latest-v18.x/api/http.html)
The Strapi's HTTP server.
### `StrapiServer.api(name)`
- `name`: <Type>String</Type>
- Returns: [<Type>StrapiAPIs</Type>](#strapiapis)
Getter for apis available in Strapi
### `StrapiServer.use(...args)`
- [<Type>KoaApp.use</Type>](https://devdocs.io/koa/index#appusefunction)
Shortcut for Koa `app.use(...args)` method.
### `StrapiServer.routes(routes)`
:::info
TODO
:::
### `StrapiServer.mount()`
:::info
TODO
:::
### `StrapiServer.initRouting()`
:::info
TODO
:::
### `StrapiServer.initMiddlewares()`
:::info
TODO
:::
### `StrapiServer.listRoutes()`
:::info
TODO
:::
### `StrapiServer.listen(...args)`
:::info
TODO
:::
### `StrapiServer.destroy()`
:::info
TODO
:::
### `StrapiAPIs`
- <Type>Object</Type>
- `content-api`: [<Type>API</Type>](API)
- API used by external requesters
- `admin`: [<Type>API</Type>](API)
- API used by admin panel
Strapi APIs is a map of all APIs available inside the Strapi project.

View File

@ -0,0 +1,93 @@
---
title: API Reference example
tags:
- utils
- class
- public
- global
toc_min_heading_level: 2
toc_max_heading_level: 3
---
import Type from '@site/docs/api/components/type';
# Name of Module
:::info
Current state: **Stable** | **Legacy** | **Deprecated**
:::
_**Stable** - can be use as is_
_**Legacy** - Old code that needs refactoring to match the current architecture of the code_
_**Deprecated** - Should **NOT** be used, this will be deleted anytime soon_
_Description with a general example on how to use this Class / Module_
## Class: Name of the class
### Public variable (e.g. in an EventEmitter class `Event: 'close'`)
### Static methods (e.g. `Static method: Class.default()`)
### `new Class()`
Instances of the Class class can be created using the new keyword.
```javascript
const myClass = new Class();
```
### `class.method(param1, param2)`
- `param1`: <Type>String</Type> (can be linked to other API doc page).
- `param2`: <Type>Object</Type>
- `options1`: <Type>Number</Type>
The `class.method()` method display the `param1` and then skip `param2` lines.
#### Examples
```javascript
const { Class } = require('pathToClassFile');
const textLines = ['Welcome', "That's all", 'Thanks'];
const classInstance = new Class();
for (const text of textLines) {
classInstance.method(text, 1);
}
// Prints:
// Welcome
// That's all
// Thanks
```
## Function: `name_of_the_function(param1, param2)`
- `param1`: <Type>String</Type> (can be linked to other API doc page)
- `param2`: <Type>Object</Type>
- `options1`: <Type>Number</Type>
The `name_of_the_function()` method display the `param1` and then skip `param2` lines.
#### Examples
```javascript
const { name_of_the_function } = require('pathToFunctionFile');
const textLines = ['Welcome', "That's all", 'Thanks'];
for (const text of textLines) {
name_of_the_function(text, 1);
}
// Prints:
// Welcome
// That's all
// Thanks
```
This structure is highly based on NodeJS API reference documentation. https://nodejs.org/api

102
docs/docs/api/telemetry.mdx Normal file
View File

@ -0,0 +1,102 @@
---
title: Telemetry Service
slug: /api/Telemetry
tags:
- module
- public
toc_min_heading_level: 2
toc_max_heading_level: 3
---
import Type from '@site/docs/api/components/type';
# Telemetry
:::info
Current state: **Stable**
:::
The telemetry service is responsible for collecting and sending anonymous usage data to Strapi. This service is disabled by default, but can be enabled or disabled via configuration.
## Usage Information
The collected usage data is used to help Strapi improve the product by identifying areas of improvement, tracking feature adoption, and measuring performance. You can learn more about the usage data that is collected by visiting the following link:
https://docs.strapi.io/developer-docs/latest/getting-started/usage-information.html
## Module: Telemetry Service
### createTelemetryInstance()
- strapi: [<Type>Strapi</Type>](Strapi) - A strapi instance.
The `createTelemetryInstance()` function returns an instance of the Telemetry service.
#### Examples
```javascript
const createTelemetryInstance = require('path/to/telemetry');
const telemetry = createTelemetryInstance(strapi);
```
## Methods
### `telemetry.register()`
Registers the telemetry instance.
#### Examples
```javascript
telemetry.register();
```
### `telemetry.bootstrap()`
Bootstraps the telemetry instance.
#### Examples
```javascript
telemetry.bootstrap();
```
### `telemetry.destroy()`
Destroys the telemetry instance.
#### Examples
```javascript
telemetry.destroy();
```
### `telemetry.send(event, payload)`
- `event`: <Type>String</Type> - The event to be sent.
- `payload`: [<Type>TelemetryPayload</Type>](#telemetrypayload) - The payload to be sent with the event.
- Returns: Promise
Sends telemetry event with the given payload.
#### Examples
```javascript
telemetry.send('event_name', { key: 'value' });
```
## Types
### `TelemetryPayload`
- <Type>Object</Type>
- `eventProperties`: <Type>Object</Type> An object that contains additional information about the event.
- `userProperties`: <Type>Object</Type> An object that defines the identity of the user who triggered the event.
- `groupProperties`: <Type>Object</Type> An object that defines properties of the application or environment in which the event occurred.
Examples of event properties in Strapi include model, containsRelationalFields, displayedFields, kind, and hasDraftAndPublish. These properties are specific to the event and are used to provide additional context about what happened.
User properties can include information such as the user's operating system, node version, and hostname. These properties are typically used to group events by user or to filter events based on certain user characteristics.
Group properties can include information such as the language(s) used in the application, the database being used, and the number of locales. These properties are typically used to group events by application version, environment, or other characteristics.

View File

@ -0,0 +1,20 @@
---
title: Introduction
---
Hello & welcome to the contributor documentation of the Strapi Monorepo! Here you'll find both technical and conceptual documentation on the codebase.
Generally speaking the documentation structure is as follows:
```shell
core/
├─ admin/
├─ content-manager/
│ ├─ documentation-file.mdx
├─ content-type-builder/
plugins/
├─ documentation/
├─ i18n/
```
This helps keep the documentation organised according to the file structure of the `packages` folder within the monorepo. From there however, is dependant on the documentation written, it will most likely change over time when the documentation grows and develops.

View File

@ -0,0 +1,6 @@
{
"position": 1,
"label": "Core",
"collapsible": true,
"collapsed": false
}

View File

@ -0,0 +1,16 @@
---
title: Introduction
tags:
- admin
---
# Admin
This section is an overview of all the features related to admin:
```mdx-code-block
import DocCardList from '@theme/DocCardList';
import { useCurrentSidebarCategory } from '@docusaurus/theme-common';
<DocCardList items={useCurrentSidebarCategory().items} />
```

View File

@ -0,0 +1,16 @@
---
title: Introduction
tags:
- enterprise-edition
---
# Admin Enterprise Edition
This section is an overview of all the features related to the Enterprise Edition in Admin:
```mdx-code-block
import DocCardList from '@theme/DocCardList';
import { useCurrentSidebarCategory } from '@docusaurus/theme-common';
<DocCardList items={useCurrentSidebarCategory().items} />
```

View File

@ -0,0 +1,139 @@
---
title: Review Workflows
description: Review workflow technical design
tags:
- review-workflows
- implementation
- tech design
---
# Review Workflows
## Summary
The review workflow feature is only available in the Enterprise Edition.
That is why, in part, it is completely decoupled from the code of the Community Edition.
The purpose of this feature is to allow users to assign a tag to the various entities of their Strapi project. This tag is called a 'stage' and is available within what we will call a workflow.
## Detailed backend design
The Review Workflow feature have been built with one main consideration, to be decoupled from the Community Edition. As so, the implementation can relate a lot to how a plugin would be built.
All the backend code related to Review Workflow can be found in `packages/core/admin/ee`.
This code is separated into several elements:
- Two content-types
- _strapi_workflows_: `packages/core/admin/ee/server/content-types/workflow/index.js`
- _strapi_workflows_stages_: `packages/core/admin/ee/server/content-types/workflow-stage/index.js`
- Two controllers
- _workflows_: `packages/core/admin/ee/server/controllers/workflows/index.js`
- _stages_: `packages/core/admin/ee/server/controllers/workflows/stages/index.js`
- One middleware
- _contentTypeMiddleware_: `packages/core/admin/ee/server/middlewares/review-workflows.js`
- Routes
- `packages/core/admin/ee/server/routes/index.js`
- Four services
- _review-workflows_: `packages/core/admin/ee/server/services/review-workflows/review-workflows.js`
- _workflows_: `packages/core/admin/ee/server/services/review-workflows/workflows.js`
- _stages_: `packages/core/admin/ee/server/services/review-workflows/stages.js`
- _metrics_: `packages/core/admin/ee/server/services/review-workflows/metrics.js`
- One decorator
- _EntityService_ decorator: `packages/core/admin/ee/server/services/review-workflows/entity-service-decorator.js`
- One utils file
- _Review workflows utils_: `packages/core/admin/ee/server/utils/review-workflows.js`
- A bootstrap and a register part
- `packages/core/admin/ee/server/bootstrap.js`
- `packages/core/admin/ee/server/register.js`
### Content types
#### strapi_workflows
This content type stores the workflow information and is responsible for holding all the information about stages and their order. In MVP, only one workflow is stored inside the Strapi database.
#### strapi_workflows_stages
This content type store the stage information such as its name.
### Controllers
#### workflows
Used to interact with the `strapi_workflows` content-type.
#### stages
Used to interact with the `strapi_workflows_stages` content-type.
### Middlewares
#### contentTypeMiddleware
In order to properly manage the options for content-type in the root level of the object, it is necessary to relocate the `reviewWorkflows` option within the `options` object located inside the content-type data. By doing so, we can ensure that all options are consistently organized and easily accessible within their respective data structures. This will also make it simpler to maintain and update the options as needed, providing a more streamlined and efficient workflow for developers working with the system. Therefore, it is recommended to move the reviewWorkflows option to its appropriate location within the options object inside the content-type data before sending it to the admin API.
### Routes
The Admin API of the Enterprise Edition includes several routes related to the Review Workflow feature. Here is a list of those routes:
#### GET `/review-workflows/workflows`
This route returns a list of all workflows.
#### GET `/review-workflows/workflows/:id`
This route returns the details of a specific workflow identified by the id parameter.
#### GET `/review-workflows/workflows/:workflow_id/stages`
This route returns a list of all stages associated with a specific workflow identified by the workflow_id parameter.
#### GET `/review-workflows/workflows/:workflow_id/stages/:id`
This route returns the details of a specific stage identified by the id parameter and associated with the workflow identified by the workflow_id parameter.
#### PUT `/review-workflows/workflows/:workflow_id/stages`
This route updates the stages associated with a specific workflow identified by the workflow_id parameter. The updated stages are passed in the request body.
#### PUT `/content-manager/(collection|single)-types/:model_uid/:id/stage`
This route updates the stage of a specific entity identified by the id parameter and belonging to a specific collection identified by the model_uid parameter. The new stage value is passed in the request body.
### Services
The Review Workflow feature of the Enterprise Edition includes several services to manipulate workflows and stages. Here is a list of those services:
#### review-workflows
This service is used during the bootstrap and register phases of Strapi. Its primary responsibility is to migrate data on entities as needed and add the stage field to the entity schemas.
#### workflows
This service is used to manipulate the workflows entities. It provides functionalities to create, retrieve, and update workflows.
#### stages
This service is used to manipulate the stages entities and to update stages on other entities. It provides functionalities to create, retrieve, update, and delete stages.
#### metrics
This is the telemetry service used to gather information on the usage of this feature. It provides information on the number of workflows and stages created, as well as the frequency of stage updates on entities.
### Decorators
#### Entity Service
The entity service is decorated so that entities can be linked to a default stage upon creation. This allows the entities to be automatically associated with a specific workflow stage when they are created.
## Alternatives
The Review Workflow feature is currently included as a core feature within the Strapi repository. However, there has been discussion about potentially moving it to a plugin in the future. While no decision has been made on this subject yet, it is possible that it may happen at some point in the future.
## Resources
- https://docs.strapi.io/user-docs/settings/review-workflows
- https://docs.strapi.io/user-docs/content-type-builder/creating-new-content-type#creating-a-new-content-type
- https://docs.strapi.io/user-docs/users-roles-permissions/configuring-administrator-roles#plugins-and-settings
- [Content manager](/content-manager/review-workflows)
- [Content type builder](/content-type-builder/review-workflows)

View File

@ -0,0 +1,5 @@
{
"label": "EE",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,5 @@
{
"label": "Frontend",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,5 @@
{
"label": "Permissions (RBAC)",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,5 @@
{
"label": "Settings",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,5 @@
{
"label": "Admin",
"collapsible": true,
"collapsed": true
}

View File

@ -1,6 +1,5 @@
---
title: Introduction
slug: /content-manager
tags:
- content-manager
---

View File

@ -1,6 +1,5 @@
---
title: Relations
slug: /content-manager/relations
description: Conceptual guide to relations in the Content Manager focussing on the technical decisions taken.
tags:
- content-manager

View File

@ -1,6 +1,5 @@
---
title: Review Workflows
slug: /content-manager/review-workflows
description: Guide for review workflows in the content-manager.
tags:
- content-manager
@ -90,6 +89,6 @@ Assigns a stage to an entity.
```ts
data: {
id: int // assigned stage id
id: int; // assigned stage id
}
```

View File

@ -0,0 +1,5 @@
{
"label": "Content Manager",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,5 @@
{
"label": "Hooks",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,25 @@
---
title: useContentTypes
description: API reference for the useContentTypes hook in Strapi's Content Manager
tags:
- content-manager
- hooks
- fetch
- content-types
- components
---
An abstraction around `react-query` to fetch content-types and components. It returns the raw API response
for components. `collectionTypes` and `singleTypes` are filtered by `isDisplayed=true`.
## Usage
```jsx
import { useContentTypes } from 'path/to/hooks';
const MyComponent = () => {
const { isLoading, collectionTypes, singleTypes, components } = useContentTypes();
return (/* ... */);
};
```

View File

@ -1,6 +1,5 @@
---
title: useDragAndDrop
slug: /content-manager/hooks/use-drag-and-drop
description: API reference for the useDragAndDrop hook in Strapi's Content Manager
tags:
- content-manager

View File

@ -1,6 +1,5 @@
---
title: Review Workflows
slug: /content-type-builder/review-workflows
description: Guide for review workflows in the content-type-builder.
tags:
- content-type-builder
@ -16,8 +15,8 @@ modal.
Similar to draft & publish review-workflows registers a new input component type called `toggle-review-workflows`
which is used to render the checkbox component.
**Note**: *Ideally the code should have been placed in the `ee` folder to be
under the enterprise license, but neither the content-type-builder nor the babel-plugin to transpile the ee code had support for this.*
**Note**: _Ideally the code should have been placed in the `ee` folder to be
under the enterprise license, but neither the content-type-builder nor the babel-plugin to transpile the ee code had support for this._
## Endpoints

View File

@ -0,0 +1,5 @@
{
"label": "Content Type Builder",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,5 @@
{
"label": "Database",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,5 @@
{
"label": "Relations",
"collapsible": true,
"collapsed": true
}

View File

@ -1,6 +1,5 @@
---
title: Relations
slug: /database/relations/reordering
title: Reordering
description: Conceptual guide to relations reordering in the Database
tags:
- database

View File

@ -0,0 +1,5 @@
{
"label": "Helper Plugin",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,5 @@
{
"label": "Hooks",
"collapsible": true,
"collapsed": true
}

View File

@ -17,7 +17,7 @@ Borrowed from [`@radix-ui/react-use-callback-ref`](https://www.npmjs.com/package
## Usage
```jsx
import { useCallbackRef } from 'path/to/hooks';
import { useCallbackRef } from '@strapi/helper-plugin';
const MyComponent = ({ callbackFromSomewhere }) => {
const mySafeCallback = useCallbackRef(callbackFromSomewhere);

View File

@ -0,0 +1,37 @@
---
title: useClipboard
description: API reference for the useClipboard hook in Strapi
tags:
- hooks
- helper-plugin
---
A small abstraction around the [`navigation.clipboard`](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard) API.
Currently we only expose a `copy` method which abstracts the `writeText` method of the clipboard API.
## Usage
```jsx
import { useClipboard } from '@strapi/helper-plugin';
const MyComponent = () => {
const { copy } = useClipboard();
const handleClick = async () => {
const didCopy = await copy('hello world');
if (didCopy) {
alert('copied!');
}
};
return <button onClick={handleClick}>Copy text</button>;
};
```
## Typescript
```ts
function useClipboard(): {
copy: (text: string) => Promise<boolean>;
};
```

View File

@ -40,7 +40,7 @@ const Component = () => {
```
:::tip
Remember to use a relative path for your requestURL, following this format `/{yourRelativePath}`
The expected URL style includes either a protocol (such as HTTP or HTTPS) or a relative URL. The URLs with domain and path but not protocol are not allowed (ex: `www.example.com`).
:::
## Methods

View File

@ -0,0 +1,5 @@
{
"label": "Strapi",
"collapsible": true,
"collapsed": true
}

View File

@ -1,5 +1,5 @@
---
title: Introduction
title: Providers
slug: /upload
tags:
- upload

View File

@ -0,0 +1,5 @@
{
"label": "Backend",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,5 @@
{
"label": "Upload",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,5 @@
{
"label": "Utils",
"collapsible": true,
"collapsed": true
}

View File

@ -1,5 +1,5 @@
---
title: Contributing
title: Contributing to Strapi
hide_title: true
---

View File

@ -1,4 +1,8 @@
# Linking the Strapi Design System
---
title: Working with the Design System
---
## Linking the Strapi Design System
Follow these steps to use a local version of the Strapi design system with the Strapi monorepo

View File

@ -0,0 +1,5 @@
---
title: Typescript
---
🚧 coming soon 🚧

View File

@ -4,3 +4,23 @@ sidebar_label: Introduction
---
# Strapi contributor documentation
Welcome to the Strapi Contributor documentation.
This documentation site is a constant WIP so please, continue to add and improve these docs. The general layout focusses on 4 key areas:
## Guides
This is where you'll probably want to start off. We have our contributing guides & code of conduct which are very important to read. There are also useful guides on common situations whilst developing such as ["Working with the Design System"](/guides/working-with-the-design-system) and higher-level guides such as best practices for frontend development (coming soon).
## Docs
Within the docs section we have a multitude of both technical and conceptual documentation diving deep into particular parts of the Strapi monorepo that may not make as much sense as just reading the code, like ["Relations reordering in the database"](/docs/core/database/relations/reordering). There's also usage documentation for various pieces of code such as the [useDragAndDrop](/docs/core/content-manager/hooks/use-drag-and-drop) hook.
## API Reference
An advanced deep dive into some of the core driving classes of Strapi with explanations on the methods & parameters available on commonly exposed classes as well as examples to compliment them for easier understanding.
## RFCs
A growing section we intend to populate over time with public-facing RFCs once approved to maintain as a record. These assist in understanding the design direction of features and code to understand the contextual "whys" that may not be apparent.

View File

@ -0,0 +1,5 @@
---
title: Introduction
---
This section of the contributor docs is a collection of public facing RFCs.

View File

@ -1,6 +1,5 @@
---
title: Custom fields
slug: /custom-fields
tags:
- content-type-builder
- plugins

View File

@ -1,11 +1,15 @@
---
title: Example
title: RFC Example Doc
description: Short description
tags:
- content-manager
---
# Example doc
# RFC Example Doc
Interested in submitting your own public RFC? Use this template as your basis but feel free to expand on it should your needs require it.
---
## Summary

View File

@ -67,22 +67,28 @@ const config = {
},
items: [
{
type: 'doc',
type: 'docSidebar',
position: 'left',
docId: 'index',
sidebarId: 'guides',
label: 'Guides',
},
{
type: 'docSidebar',
position: 'left',
sidebarId: 'docs',
label: 'Docs',
},
{
type: 'docSidebar',
position: 'left',
sidebarId: 'api',
label: 'API',
label: 'API Reference',
},
{
type: 'docSidebar',
position: 'left',
sidebarId: 'community',
label: 'Community',
sidebarId: 'rfcs',
label: 'RFCs',
},
],
},

View File

@ -14,218 +14,10 @@
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
// By default, Docusaurus generates a sidebar from the docs folder structure
docs: [
'index',
{
type: 'category',
label: 'Admin',
items: [
{
type: 'doc',
label: 'Link Strapi Design System',
id: 'core/admin/link-strapi-design-system',
},
],
},
{
type: 'category',
label: 'Core',
link: {
type: 'generated-index',
},
collapsible: false,
items: [
{
type: 'category',
label: 'Admin',
items: [
{
type: 'doc',
label: 'Link Strapi Design System',
id: 'core/admin/link-strapi-design-system',
},
],
},
{
type: 'category',
label: 'Content Manager',
link: {
type: 'doc',
id: 'core/content-manager/intro',
},
items: [
{
type: 'category',
label: 'Hooks',
items: [
{
type: 'doc',
label: 'useDragAndDrop',
id: 'core/content-manager/hooks/use-drag-and-drop',
},
],
},
{
type: 'doc',
label: 'Relations',
id: 'core/content-manager/relations',
},
{
type: 'doc',
label: 'Review Workflows',
id: 'core/content-manager/review-workflows',
},
],
},
{
type: 'category',
label: 'Content Type Builder',
link: {
type: 'doc',
id: 'core/content-type-builder/intro',
},
items: [
{
type: 'doc',
label: 'Review Workflows',
id: 'core/content-type-builder/review-workflows',
},
],
},
{
type: 'category',
label: 'Database',
link: {
type: 'doc',
id: 'core/database/intro',
},
items: [
{
type: 'category',
label: 'Relations',
items: [
{
type: 'doc',
label: 'Reordering',
id: 'core/database/relations/reordering',
},
],
},
],
},
{
type: 'category',
label: 'Helper Plugin',
items: [
{
type: 'category',
label: 'Hooks',
items: [
{
type: 'doc',
label: 'useAPIErrorHandler',
id: 'core/helper-plugin/hooks/use-api-error-handler',
},
{
type: 'doc',
label: 'useCallbackRef',
id: 'core/helper-plugin/hooks/use-callback-ref',
},
{
type: 'doc',
label: 'useCollator',
id: 'core/helper-plugin/hooks/use-collator',
},
{
type: 'doc',
label: 'useFetchClient',
id: 'core/helper-plugin/hooks/use-fetch-client',
},
{
type: 'doc',
label: 'useFilter',
id: 'core/helper-plugin/hooks/use-filter',
},
],
},
],
},
{
type: 'category',
label: 'Permissions (RBAC)',
link: {
type: 'doc',
id: 'core/permissions/intro',
},
items: [
{
type: 'doc',
label: 'How Permissions Work',
id: 'core/permissions/how-they-work',
},
{
type: 'category',
label: 'RBAC on the frontend',
items: [
{
type: 'doc',
label: 'Fetching Permissions',
id: 'core/permissions/frontend/fetching-permissions',
},
{
type: 'doc',
label: 'Using Permissions',
id: 'core/permissions/frontend/using-permissions',
},
],
},
],
},
{
type: 'category',
label: 'Settings',
link: {
type: 'doc',
id: 'core/settings/intro',
},
items: [
{
type: 'doc',
label: 'Review Workflows',
id: 'core/settings/review-workflows',
},
],
},
{
type: 'category',
label: 'Utils',
items: [
{
type: 'doc',
label: 'Async',
id: 'core/utils/async',
},
{
type: 'doc',
label: 'Event Hub',
id: 'core/utils/event-hub',
},
],
},
],
},
{
type: 'category',
label: 'Custom Fields',
link: {
type: 'doc',
id: 'custom-fields',
},
items: [],
},
],
docs: [{ type: 'autogenerated', dirName: 'docs' }],
api: [{ type: 'autogenerated', dirName: 'api' }],
community: [{ type: 'autogenerated', dirName: 'community' }],
guides: [{ type: 'autogenerated', dirName: 'guides' }],
rfcs: [{ type: 'autogenerated', dirName: 'rfcs' }],
};
module.exports = sidebars;

View File

@ -1,7 +1,7 @@
{
"name": "getstarted",
"private": true,
"version": "4.10.1",
"version": "4.10.5",
"description": "A Strapi application.",
"scripts": {
"develop": "strapi develop",
@ -12,26 +12,25 @@
"strapi": "strapi"
},
"dependencies": {
"@strapi/icons": "1.6.6",
"@strapi/plugin-color-picker": "4.10.1",
"@strapi/plugin-documentation": "4.10.1",
"@strapi/plugin-graphql": "4.10.1",
"@strapi/plugin-i18n": "4.10.1",
"@strapi/plugin-sentry": "4.10.1",
"@strapi/plugin-users-permissions": "4.10.1",
"@strapi/provider-email-mailgun": "4.10.1",
"@strapi/provider-upload-aws-s3": "4.10.1",
"@strapi/provider-upload-cloudinary": "4.10.1",
"@strapi/strapi": "4.10.1",
"@vscode/sqlite3": "5.1.2",
"better-sqlite3": "8.0.1",
"@strapi/icons": "1.7.7",
"@strapi/plugin-color-picker": "4.10.5",
"@strapi/plugin-documentation": "4.10.5",
"@strapi/plugin-graphql": "4.10.5",
"@strapi/plugin-i18n": "4.10.5",
"@strapi/plugin-sentry": "4.10.5",
"@strapi/plugin-users-permissions": "4.10.5",
"@strapi/provider-email-mailgun": "4.10.5",
"@strapi/provider-upload-aws-s3": "4.10.5",
"@strapi/provider-upload-cloudinary": "4.10.5",
"@strapi/strapi": "4.10.5",
"better-sqlite3": "8.3.0",
"lodash": "4.17.21",
"mysql": "2.18.1",
"mysql2": "3.2.0",
"mysql2": "3.3.0",
"passport-google-oauth2": "0.2.0",
"pg": "8.8.0",
"react": "^17.0.2",
"react-intl": "6.3.2",
"react-intl": "6.4.1",
"sqlite3": "5.1.2"
},
"strapi": {

View File

@ -1,7 +1,7 @@
{
"name": "kitchensink-ts",
"private": true,
"version": "4.10.1",
"version": "4.10.5",
"description": "A Strapi application",
"scripts": {
"develop": "strapi develop",
@ -10,10 +10,10 @@
"strapi": "strapi"
},
"dependencies": {
"@strapi/plugin-i18n": "4.10.1",
"@strapi/plugin-users-permissions": "4.10.1",
"@strapi/strapi": "4.10.1",
"better-sqlite3": "8.0.1"
"@strapi/plugin-i18n": "4.10.5",
"@strapi/plugin-users-permissions": "4.10.5",
"@strapi/strapi": "4.10.5",
"better-sqlite3": "8.3.0"
},
"author": {
"name": "A Strapi developer"

View File

@ -1,7 +1,7 @@
{
"name": "kitchensink",
"private": true,
"version": "4.10.1",
"version": "4.10.5",
"description": "A Strapi application.",
"scripts": {
"develop": "strapi develop",
@ -12,10 +12,10 @@
"strapi": "strapi"
},
"dependencies": {
"@strapi/provider-email-mailgun": "4.10.1",
"@strapi/provider-upload-aws-s3": "4.10.1",
"@strapi/provider-upload-cloudinary": "4.10.1",
"@strapi/strapi": "4.10.1",
"@strapi/provider-email-mailgun": "4.10.5",
"@strapi/provider-upload-aws-s3": "4.10.5",
"@strapi/provider-upload-cloudinary": "4.10.5",
"@strapi/strapi": "4.10.5",
"lodash": "4.17.21",
"mysql": "2.18.1",
"passport-google-oauth2": "0.2.0",

View File

@ -30,7 +30,9 @@ const moduleNameMapper = {
module.exports = {
rootDir: __dirname,
moduleNameMapper,
testPathIgnorePatterns: ['/node_modules/', '__tests__'],
/* Tells jest to ignore duplicated manual mock files, such as index.js */
modulePathIgnorePatterns: ['.*__mocks__.*'],
testPathIgnorePatterns: ['node_modules/', '__tests__'],
globalSetup: '@strapi/admin-test-utils/global-setup',
setupFiles: ['@strapi/admin-test-utils/environment'],
setupFilesAfterEnv: ['@strapi/admin-test-utils/after-env'],
@ -60,7 +62,7 @@ module.exports = {
transformIgnorePatterns: [
'node_modules/(?!(react-dnd|dnd-core|react-dnd-html5-backend|@strapi/design-system|@strapi/icons|fractional-indexing)/)',
],
testMatch: ['/**/tests/**/?(*.)+(spec|test).[jt]s?(x)'],
testMatch: ['**/tests/**/?(*.)+(spec|test).[jt]s?(x)'],
testEnvironmentOptions: {
url: 'http://localhost:1337/admin',
},

View File

@ -4,7 +4,10 @@ module.exports = {
setupFilesAfterEnv: [__dirname + '/test/unit.setup.js'],
modulePathIgnorePatterns: ['.cache', 'dist'],
testPathIgnorePatterns: ['.testdata.js', '.test.utils.js'],
testMatch: ['/**/__tests__/**/*.[jt]s?(x)'],
testMatch: ['**/__tests__/**/*.{js,ts,jsx,tsx}'],
transform: {
'^.+\\.(t|j)sx?$': ['@swc/jest'],
},
// Use `jest-watch-typeahead` version 0.6.5. Newest version 1.0.0 does not support jest@26
// Reference: https://github.com/jest-community/jest-watch-typeahead/releases/tag/v1.0.0
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],

12
jest.config.front.js Normal file
View File

@ -0,0 +1,12 @@
'use strict';
/** @type {import('jest').Config} */
const config = {
projects: [
'<rootDir>/packages/plugins/*/jest.config.front.js',
'<rootDir>/packages/core/*/jest.config.front.js',
'<rootDir>/scripts/*/jest.config.front.js',
],
};
module.exports = config;

15
jest.config.js Normal file
View File

@ -0,0 +1,15 @@
'use strict';
/** @type {import('jest').Config} */
const config = {
projects: [
'<rootDir>/packages/plugins/*/jest.config.js',
'<rootDir>/packages/utils/*/jest.config.js',
'<rootDir>/packages/generators/*/jest.config.js',
'<rootDir>/packages/core/*/jest.config.js',
'<rootDir>/packages/providers/*/jest.config.js',
'<rootDir>/.github/actions/*/jest.config.js',
],
};
module.exports = config;

View File

@ -1,5 +1,5 @@
{
"version": "4.10.1",
"version": "4.10.5",
"packages": ["packages/*", "examples/*"],
"npmClient": "yarn",
"useWorkspaces": true,

50
lint-staged.config.js Normal file
View File

@ -0,0 +1,50 @@
'use strict';
const path = require('path');
const fs = require('fs');
const findUp = require('find-up');
const includes = ['packages', '.github'];
const root = path.resolve(__dirname);
function extractPackageName(pkgJsonPath) {
return JSON.parse(fs.readFileSync(pkgJsonPath).toString()).name;
}
function getLintCommand(files) {
const affectedFolders = new Set();
for (const file of files) {
const r = findUp.sync('package.json', { cwd: file });
const relPath = path.relative(root, r);
if (includes.some((incl) => relPath.startsWith(incl))) {
affectedFolders.add(r);
}
}
const affectedPackages = [...affectedFolders].map(extractPackageName);
if (affectedPackages.length === 0) {
return null;
}
return `nx run-many -t lint -p ${affectedPackages.join()}`;
}
function getCodeCommands(files) {
const lintCmd = getLintCommand(files);
const prettierCmd = `prettier --write ${files.join(' ')}`;
if (lintCmd) {
return [lintCmd, prettierCmd];
}
return [prettierCmd];
}
module.exports = {
'*.{js,ts}': getCodeCommands,
'*.{md,css,scss,yaml,yml}': ['prettier --write'],
};

View File

@ -29,12 +29,10 @@
"dependsOn": ["^build:ts"]
},
"test:unit": {
"inputs": ["default", "{workspaceRoot}/jest-preset.unit.js"],
"dependsOn": ["build:ts"]
"inputs": ["default", "{workspaceRoot}/jest-preset.unit.js"]
},
"test:front": {
"inputs": ["default", "{workspaceRoot}/jest-preset.front.js"],
"dependsOn": ["^build"]
"inputs": ["default", "{workspaceRoot}/jest-preset.front.js"]
},
"lint": {
"inputs": [
@ -44,8 +42,7 @@
"{projectRoot}/.eslintignore",
"{projectRoot}/tsconfig.eslint.json",
"{workspaceRoot}/packages/utils/eslint-config-custom/**/*"
],
"dependsOn": ["build:ts"]
]
}
},
"tasksRunnerOptions": {

View File

@ -45,23 +45,21 @@
"format:other": "yarn prettier:other --write",
"prettier:code": "prettier --cache --cache-strategy content \"**/*.{js,ts}\"",
"prettier:other": "prettier --cache --cache-strategy content \"**/*.{md,css,scss,yaml,yml}\"",
"test:front": "cross-env IS_EE=true nx run-many --target=test:front --nx-ignore-cycles",
"test:front:watch": "cross-env IS_EE=true nx run-many --target=test:front:watch --nx-ignore-cycles",
"test:front:update": "yarn test:front -u",
"test:front:ce": "cross-env IS_EE=false nx run-many --target=test:front --nx-ignore-cycles",
"test:front:watch:ce": "cross-env IS_EE=false nx run-many --target=test:front:watch --nx-ignore-cycles",
"test:front:all": "cross-env IS_EE=true nx run-many --target=test:front --nx-ignore-cycles",
"test:front": "cross-env IS_EE=true jest --config jest.config.front.js",
"test:front:watch": "cross-env IS_EE=true run test:front --watch",
"test:front:update": "run test:front -u",
"test:front:all:ce": "cross-env IS_EE=false nx run-many --target=test:front:ce --nx-ignore-cycles",
"test:front:ce": "cross-env IS_EE=false run test:front",
"test:front:watch:ce": "cross-env IS_EE=false run test:front --watch",
"test:front:update:ce": "yarn test:front:ce -u",
"test:unit": "nx run-many --target=test:unit --nx-ignore-cycles",
"test:unit:watch": "nx run-many --target=test:unit:watch --nx-ignore-cycles",
"test:unit:all": "nx run-many --target=test:unit --nx-ignore-cycles",
"test:unit": "jest --config jest.config.js",
"test:unit:watch": "run test:unit --watch",
"test:api": "node test/api.js",
"test:generate-app": "node test/create-test-app.js",
"doc:api": "node scripts/open-api/serve.js"
},
"lint-staged": {
"*.{js,ts,md,css,scss,yaml,yml}": [
"prettier --write"
]
},
"devDependencies": {
"@babel/core": "^7.20.12",
"@babel/eslint-parser": "^7.19.1",
@ -69,17 +67,18 @@
"@strapi/admin-test-utils": "workspace:*",
"@strapi/eslint-config": "0.1.2",
"@swc/cli": "0.1.62",
"@swc/core": "1.3.37",
"@swc/jest": "0.2.24",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "5.43.0",
"@swc/core": "1.3.58",
"@swc/helpers": "0.5.1",
"@swc/jest": "0.2.26",
"@typescript-eslint/eslint-plugin": "5.59.1",
"@typescript-eslint/parser": "5.59.1",
"babel-eslint": "10.1.0",
"chalk": "4.1.2",
"chokidar": "3.5.3",
"core-js": "3.28.0",
"core-js": "3.30.1",
"cross-env": "7.0.3",
"dotenv": "14.2.0",
"eslint": "8.27.0",
"eslint": "8.41.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.0.0",
@ -91,6 +90,7 @@
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"execa": "1.0.0",
"find-up": "5.0.0",
"fs-extra": "10.1.0",
"get-port": "5.1.1",
"glob": "7.2.3",
@ -98,13 +98,13 @@
"inquirer": "8.2.5",
"jest": "29.0.3",
"jest-circus": "29.0.3",
"jest-cli": "29.0.3",
"jest-cli": "29.5.0",
"jest-environment-jsdom": "29.0.3",
"jest-watch-typeahead": "2.2.2",
"lerna": "6.5.1",
"lint-staged": "13.0.3",
"lint-staged": "13.2.2",
"lodash": "4.17.21",
"nx": "15.8.3",
"nx": "15.9.4",
"plop": "2.7.6",
"prettier": "2.8.4",
"qs": "6.11.1",

View File

@ -10,6 +10,7 @@ declare global {
isEnabled: (featureName?: string) => boolean;
};
projectType: string;
telemetryDisabled: boolean;
};
}
}

Some files were not shown because too many files have changed in this diff Show More