mirror of
https://github.com/strapi/strapi.git
synced 2025-12-12 15:32:42 +00:00
Merge branch 'master' into fix/up-fixes
This commit is contained in:
commit
720f05ad48
2
.github/actions/check-pr-status/package.json
vendored
2
.github/actions/check-pr-status/package.json
vendored
@ -11,6 +11,6 @@
|
||||
"devDependencies": {
|
||||
"@actions/core": "1.8.1",
|
||||
"@actions/github": "5.0.0",
|
||||
"@vercel/ncc": "0.33.3"
|
||||
"@vercel/ncc": "0.34.0"
|
||||
}
|
||||
}
|
||||
|
||||
17
.github/workflows/addToProject.yml
vendored
Normal file
17
.github/workflows/addToProject.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
name: Add bugs to bugs project
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
- transferred
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add issue to Support Team project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
project-url: https://github.com/orgs/strapi/projects/15
|
||||
github-token: ${{ secrets.PROJECT_TRANSFER_TOKEN }}
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -65,7 +65,7 @@ jobs:
|
||||
- name: Build
|
||||
run: yarn build
|
||||
- name: Run test
|
||||
run: yarn run -s test:front && codecov -C -F front
|
||||
run: yarn run -s test:front --coverage && codecov -C -F front
|
||||
|
||||
e2e_ce_pg:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
2
examples/getstarted/.gitignore
vendored
2
examples/getstarted/.gitignore
vendored
@ -100,7 +100,7 @@ node_modules
|
||||
############################
|
||||
|
||||
testApp
|
||||
coverage
|
||||
/coverage/
|
||||
|
||||
############################
|
||||
# Strapi
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { assoc, has, prop, omit } = require('lodash/fp');
|
||||
const { assoc, has, prop, omit, merge } = require('lodash/fp');
|
||||
const strapiUtils = require('@strapi/utils');
|
||||
const { ApplicationError } = require('@strapi/utils').errors;
|
||||
|
||||
@ -40,15 +40,11 @@ const findCreatorRoles = entity => {
|
||||
};
|
||||
|
||||
// TODO: define when we use this one vs basic populate
|
||||
const getDeepPopulate = (uid, populate, depth = 0) => {
|
||||
const getDeepPopulate = (uid, populate) => {
|
||||
if (populate) {
|
||||
return populate;
|
||||
}
|
||||
|
||||
if (depth > 2) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const { attributes } = strapi.getModel(uid);
|
||||
|
||||
return Object.keys(attributes).reduce((populateAcc, attributeName) => {
|
||||
@ -60,7 +56,7 @@ const getDeepPopulate = (uid, populate, depth = 0) => {
|
||||
|
||||
if (attribute.type === 'component') {
|
||||
populateAcc[attributeName] = {
|
||||
populate: getDeepPopulate(attribute.component, null, depth + 1),
|
||||
populate: getDeepPopulate(attribute.component, null),
|
||||
};
|
||||
}
|
||||
|
||||
@ -71,7 +67,7 @@ const getDeepPopulate = (uid, populate, depth = 0) => {
|
||||
if (attribute.type === 'dynamiczone') {
|
||||
populateAcc[attributeName] = {
|
||||
populate: (attribute.components || []).reduce((acc, componentUID) => {
|
||||
return Object.assign(acc, getDeepPopulate(componentUID, null, depth + 1));
|
||||
return merge(acc, getDeepPopulate(componentUID, null));
|
||||
}, {}),
|
||||
};
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
"react-redux": "7.2.3",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "5.2.0",
|
||||
"sharp": "0.30.4"
|
||||
"sharp": "0.30.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.22.0 <=16.x.x",
|
||||
|
||||
97
packages/core/utils/lib/__tests__/validators.test.js
Normal file
97
packages/core/utils/lib/__tests__/validators.test.js
Normal file
@ -0,0 +1,97 @@
|
||||
'use strict';
|
||||
|
||||
const { yup } = require('../validators');
|
||||
|
||||
describe('validators', () => {
|
||||
describe('strapiID', () => {
|
||||
test.each([
|
||||
[0, true],
|
||||
['0', true],
|
||||
[1, true],
|
||||
['1', true],
|
||||
[undefined, true], // because it's not required
|
||||
[{}, false],
|
||||
[[], false],
|
||||
[null, false],
|
||||
])('yup.strapiID(): %s => %s', async (value, expectedResult) => {
|
||||
let result = true;
|
||||
try {
|
||||
await yup.strapiID().validate(value);
|
||||
} catch (e) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
expect(result).toBe(expectedResult);
|
||||
});
|
||||
|
||||
test.each([
|
||||
[0, true],
|
||||
['0', true],
|
||||
[1, true],
|
||||
['1', true],
|
||||
[undefined, false],
|
||||
[{}, false],
|
||||
[[], false],
|
||||
[null, false],
|
||||
])('yup.strapiID().required(): %s => %s', async (value, expectedResult) => {
|
||||
let result = true;
|
||||
try {
|
||||
await yup
|
||||
.strapiID()
|
||||
.required()
|
||||
.validate(value);
|
||||
} catch (e) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
expect(result).toBe(expectedResult);
|
||||
});
|
||||
|
||||
test.each([
|
||||
[0, true],
|
||||
['0', true],
|
||||
[1, true],
|
||||
['1', true],
|
||||
[undefined, true],
|
||||
[{}, false],
|
||||
[[], false],
|
||||
[null, true],
|
||||
])('yup.strapiID().nullable(): %s => %s', async (value, expectedResult) => {
|
||||
let result = true;
|
||||
try {
|
||||
await yup
|
||||
.strapiID()
|
||||
.nullable()
|
||||
.validate(value);
|
||||
} catch (e) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
expect(result).toBe(expectedResult);
|
||||
});
|
||||
|
||||
test.each([
|
||||
[0, true],
|
||||
['0', true],
|
||||
[1, true],
|
||||
['1', true],
|
||||
[undefined, false],
|
||||
[{}, false],
|
||||
[[], false],
|
||||
[null, true],
|
||||
])('yup.strapiID().nullable().defined(): %s => %s', async (value, expectedResult) => {
|
||||
let result = true;
|
||||
try {
|
||||
await yup
|
||||
.strapiID()
|
||||
.nullable()
|
||||
.defined()
|
||||
.validate(value);
|
||||
} catch (e) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
expect(result).toBe(expectedResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -7,7 +7,7 @@ const utils = require('./string-formatting');
|
||||
const { YupValidationError } = require('./errors');
|
||||
const printValue = require('./print-value');
|
||||
|
||||
const MixedSchemaType = yup.mixed;
|
||||
const MixedSchemaType = yup.MixedSchema;
|
||||
|
||||
const isNotNilTest = value => !_.isNil(value);
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ describe('Build Component Schema', () => {
|
||||
const [pluginResponseValue, apiResponseValue] = Object.values(schemas);
|
||||
|
||||
const expectedShape = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'object',
|
||||
@ -82,6 +83,7 @@ describe('Build Component Schema', () => {
|
||||
const apiListResponseValue = schemas['RestaurantListResponse'];
|
||||
|
||||
const expectedShape = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'array',
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
"react-router-dom": "5.2.0",
|
||||
"redux": "^4.0.1",
|
||||
"reselect": "^4.0.0",
|
||||
"swagger-ui-dist": "3.47.1",
|
||||
"swagger-ui-dist": "4.11.1",
|
||||
"yaml": "1.10.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@ -95,6 +95,7 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
|
||||
schemas = {
|
||||
...schemas,
|
||||
[`${pascalCase(uniqueName)}ListResponse`]: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'array',
|
||||
@ -128,6 +129,7 @@ const getAllSchemasForContentType = ({ routeInfo, attributes, uniqueName }) => {
|
||||
schemas = {
|
||||
...schemas,
|
||||
[`${pascalCase(uniqueName)}Response`]: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
data: {
|
||||
type: 'object',
|
||||
|
||||
@ -26,13 +26,15 @@ npm install @strapi/provider-email-mailgun --save
|
||||
| Variable | Type | Description | Required | Default |
|
||||
| ----------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- |
|
||||
| provider | string | The name of the provider you use | yes | |
|
||||
| providerOptions | object | Will be directly given to the `require('mailgun-js')`. Please refer to [mailgun-js](https://www.npmjs.com/package/mailgun-js) doc. | yes | |
|
||||
| providerOptions | object | Will be directly given to the `require('mailgun.js')`. Please refer to [mailgun.js](https://www.npmjs.com/package/mailgun.js) doc. | yes | |
|
||||
| settings | object | Settings | no | {} |
|
||||
| settings.defaultFrom | string | Default sender mail address | no | undefined |
|
||||
| settings.defaultReplyTo | string \| array<string> | Default address or addresses the receiver is asked to reply to | no | undefined |
|
||||
|
||||
> :warning: The Shipper Email (or defaultfrom) may also need to be changed in the `Email Templates` tab on the admin panel for emails to send properly
|
||||
|
||||
Since [mailgun-js](https://www.npmjs.com/package/mailgun-js) has been deprecated, this package now uses `mailgun.js` instead. In an effort to avoid breaking changes methods were added to convert existing configuration objects to work with the new package.
|
||||
|
||||
### Example
|
||||
|
||||
**Path -** `config/plugins.js`
|
||||
@ -44,9 +46,9 @@ module.exports = ({ env }) => ({
|
||||
config: {
|
||||
provider: 'mailgun',
|
||||
providerOptions: {
|
||||
apiKey: env('MAILGUN_API_KEY'),
|
||||
domain: env('MAILGUN_DOMAIN'), //Required if you have an account with multiple domains
|
||||
host: env('MAILGUN_HOST', 'api.mailgun.net'), //Optional. If domain region is Europe use 'api.eu.mailgun.net'
|
||||
key: env('MAILGUN_API_KEY'), // Required
|
||||
domain: env('MAILGUN_DOMAIN'), // Required
|
||||
url: env('MAILGUN_URL', 'https://api.mailgun.net'), //Optional. If domain region is Europe use 'https://api.eu.mailgun.net'
|
||||
},
|
||||
settings: {
|
||||
defaultFrom: 'myemail@protonmail.com',
|
||||
|
||||
@ -0,0 +1,90 @@
|
||||
'use strict';
|
||||
|
||||
const formData = require('form-data');
|
||||
const Mailgun = require('mailgun.js');
|
||||
|
||||
const provider = require('../index');
|
||||
|
||||
describe('@strapi/provider-email-mailgun', () => {
|
||||
describe('.convertProviderOptions()', () => {
|
||||
it('returns an empty object', () => {
|
||||
expect(provider.convertProviderOptions({})).toEqual({});
|
||||
});
|
||||
|
||||
it('returns the correct key', () => {
|
||||
expect(
|
||||
provider.convertProviderOptions({
|
||||
apiKey: 'foo',
|
||||
})
|
||||
).toEqual({
|
||||
key: 'foo',
|
||||
});
|
||||
});
|
||||
|
||||
it('passes through unknown options', () => {
|
||||
expect(
|
||||
provider.convertProviderOptions({
|
||||
username: 'foo',
|
||||
})
|
||||
).toEqual({
|
||||
username: 'foo',
|
||||
});
|
||||
});
|
||||
|
||||
it('correctly merges options objects with defaults', () => {
|
||||
const defaults = {
|
||||
username: 'api',
|
||||
};
|
||||
const providerOptions = {
|
||||
key: 'foo',
|
||||
username: 'bar',
|
||||
domain: 'baz.example.com',
|
||||
};
|
||||
expect({ ...defaults, ...provider.convertProviderOptions(providerOptions) }).toEqual(
|
||||
providerOptions
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mailgun', () => {
|
||||
it('successfully creates a new Mailgun client', () => {
|
||||
const defaults = {
|
||||
username: 'api',
|
||||
};
|
||||
const providerOptions = {
|
||||
key: 'foo',
|
||||
username: 'bar',
|
||||
domain: 'baz.example.com',
|
||||
};
|
||||
const mailgun = new Mailgun(formData);
|
||||
const mg = mailgun.client({
|
||||
...defaults,
|
||||
...provider.convertProviderOptions(providerOptions),
|
||||
});
|
||||
expect(mg).toMatchObject({
|
||||
messages: {
|
||||
request: {
|
||||
headers: {},
|
||||
key: providerOptions.key,
|
||||
url: 'https://api.mailgun.net',
|
||||
username: providerOptions.username,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('fails to create a new Mailgun client due to missing key', () => {
|
||||
const defaults = {
|
||||
username: 'api',
|
||||
};
|
||||
const providerOptions = {
|
||||
username: 'bar',
|
||||
domain: 'baz.example.com',
|
||||
};
|
||||
const mailgun = new Mailgun(formData);
|
||||
expect(() => {
|
||||
mailgun.client({ ...defaults, ...provider.convertProviderOptions(providerOptions) });
|
||||
}).toThrowError('Parameter "key" is required');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,40 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
const mailgunFactory = require('mailgun-js');
|
||||
const formData = require('form-data');
|
||||
const Mailgun = require('mailgun.js');
|
||||
const { removeUndefined } = require('@strapi/utils');
|
||||
|
||||
const optionsMap = {
|
||||
apiKey: { field: 'key', fn: value => value },
|
||||
host: { field: 'url', fn: value => `https://${value || 'api.mailgun.net'}` },
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
convertProviderOptions(providerOptions = {}) {
|
||||
const newOptions = {};
|
||||
if (typeof providerOptions === 'object') {
|
||||
Object.keys(providerOptions).forEach(key => {
|
||||
if (Object.keys(optionsMap).includes(key)) {
|
||||
newOptions[optionsMap[key].field] = optionsMap[key].fn(providerOptions[key]);
|
||||
} else {
|
||||
newOptions[key] = providerOptions[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
return newOptions;
|
||||
},
|
||||
|
||||
init(providerOptions = {}, settings = {}) {
|
||||
const mailgun = mailgunFactory({
|
||||
mute: false,
|
||||
...providerOptions,
|
||||
});
|
||||
const defaults = {
|
||||
username: 'api',
|
||||
};
|
||||
const mailgun = new Mailgun(formData);
|
||||
const mg = mailgun.client({ ...defaults, ...this.convertProviderOptions(providerOptions) });
|
||||
|
||||
return {
|
||||
send(options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { from, to, cc, bcc, replyTo, subject, text, html, ...rest } = options;
|
||||
const { from, to, cc, bcc, replyTo, subject, text, html, ...rest } = options;
|
||||
|
||||
let msg = {
|
||||
from: from || settings.defaultFrom,
|
||||
to,
|
||||
cc,
|
||||
bcc,
|
||||
'h:Reply-To': replyTo || settings.defaultReplyTo,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
...rest,
|
||||
};
|
||||
let data = {
|
||||
from: from || settings.defaultFrom,
|
||||
to,
|
||||
cc,
|
||||
bcc,
|
||||
'h:Reply-To': replyTo || settings.defaultReplyTo,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
...rest,
|
||||
};
|
||||
|
||||
mailgun.messages().send(removeUndefined(msg), function(err) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
return mg.messages.create(providerOptions.domain, removeUndefined(data));
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@ -37,7 +37,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@strapi/utils": "4.1.12",
|
||||
"mailgun-js": "0.22.0"
|
||||
"form-data": "^4.0.0",
|
||||
"mailgun.js": "5.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.22.0 <=16.x.x",
|
||||
|
||||
@ -63,16 +63,16 @@ const addMissingKeys = async lang => {
|
||||
translationFiles.forEach(addMissingKeyForSingleFile);
|
||||
};
|
||||
|
||||
if (process.argv.length < 3) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
'Please provide a language. For example:\nnode scripts/front/add-missing-keys-to-other-language.js vi'
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
if (process.argv.length < 3) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
'Please provide a language. For example:\nnode scripts/front/add-missing-keys-to-other-language.js vi'
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
addMissingKeys(process.argv[2]).catch(err => console.error(err));
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user