Skip to content
This repository was archived by the owner on Dec 26, 2023. It is now read-only.

Commit c2adf15

Browse files
authored
🔨 ✨ [Chore][ISSUE-3] Add CMS Seed Data generation (#5)
- use strapi bootstrap for development seeding - add generateSeedData to bootstrap function - add _seed data with implementation for: projects & contributors / users / partners - add yarn seed to package.json - update README.md with relevant info for seeding - update .env.example with dummy values for api keys and tokens - add DEV_* value defaults for seed data generation
1 parent a6c0572 commit c2adf15

File tree

14 files changed

+380
-28
lines changed

14 files changed

+380
-28
lines changed

cms/.env.example

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
HOST=0.0.0.0
22
PORT=1337
33
APP_KEYS=value_1,value_2,value_3
4-
API_TOKEN_SALT=
5-
ADMIN_JWT_SECRET=
6-
JWT_SECRET=
4+
API_TOKEN_SALT=API_TOKEN_SALT_VALUE
5+
ADMIN_JWT_SECRET=ADMIN_JWT_SECRET_VALUE
6+
JWT_SECRET=__SKIP_FOR_LOCAL_ENVIRONMENT--GENERATED__
77

88
DATABASE_HOST=
99
DATABASE_PORT=
1010
DATABASE_NAME=
1111
DATABASE_USERNAME=
1212
DATABASE_PASSWORD=
13+
14+
15+
DEV_SEED_DATA_PROJECTS=8
16+
DEV_SEED_DATA_CONTRIBUTORS=15
17+
DEV_SEED_DATA_PARTNERS=5

cms/README.md

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,50 @@
1-
# 🚀 Getting started with Strapi
1+
# DeveloperDAO.com CMS
22

3-
Strapi comes with a full featured [Command Line Interface](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html) (CLI) which lets you scaffold and manage your project in seconds.
3+
## Development
4+
5+
### Prerequisites
6+
7+
create `.env` from `.env.example` and remove `JWT_SECRET` such as it would be generated and added to `.env` file by Strapi
48

59
### `develop`
610

711
Start your Strapi application with autoReload enabled. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-develop)
812

913
```
10-
npm run develop
11-
# or
1214
yarn develop
1315
```
1416

17+
### `seed`
18+
19+
By default, on the first application _DEVELOPMENT_ start(with `yarn develop`), **seed data** would be generated. In case you need to re-create it, run
20+
21+
```
22+
yarn seed
23+
```
24+
25+
> ⚠️ This will clear all data from all available collections
26+
27+
#### How it works?
28+
29+
Strapi runs **bootstrap** function from `/src/index.js` [every time on server start](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/functions.html#bootstrap).
30+
31+
On the initial run, we check if the current mode is _development_ to run `generateSeedData` function. Only if we previously haven't run seeding - we run it.
32+
33+
When manual seeding happens with `FORCE_APP_BOOTSTRAP_ONLY` in _development_ mode, we run `strapi start` and exit right after **bootstrap** is finished.
34+
In this case, we could have our main app running and not worry about the "port is used" error.
35+
36+
There are default values of how much data we want to generate - feel free to increase or make them 0.
37+
```
38+
DEV_SEED_DATA_PROJECTS=8
39+
DEV_SEED_DATA_CONTRIBUTORS=15
40+
DEV_SEED_DATA_PARTNERS=5
41+
```
42+
1543
### `start`
1644

1745
Start your Strapi application with autoReload disabled. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-start)
1846

1947
```
20-
npm run start
21-
# or
2248
yarn start
2349
```
2450

@@ -27,11 +53,15 @@ yarn start
2753
Build your admin panel. [Learn more](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html#strapi-build)
2854

2955
```
30-
npm run build
31-
# or
3256
yarn build
3357
```
3458

59+
---
60+
61+
# Strapi
62+
63+
Strapi comes with a full featured [Command Line Interface](https://docs.strapi.io/developer-docs/latest/developer-resources/cli/CLI.html) (CLI) which lets you scaffold and manage your project in seconds.
64+
3565
## ⚙️ Deployment
3666

3767
Strapi gives you many possible deployment options for your project. Find the one that suits you on the [deployment section of the documentation](https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/deployment.html).
@@ -51,7 +81,3 @@ Feel free to check out the [Strapi GitHub repository](https://github.com/strapi/
5181
- [Discord](https://discord.strapi.io) - Come chat with the Strapi community including the core team.
5282
- [Forum](https://forum.strapi.io/) - Place to discuss, ask questions and find answers, show your Strapi project and get feedback or just talk with other Community members.
5383
- [Awesome Strapi](https://github.com/strapi/awesome-strapi) - A curated list of awesome things related to Strapi.
54-
55-
---
56-
57-
<sub>🤫 Psst! [Strapi is hiring](https://strapi.io/careers).</sub>

cms/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
"develop-pg": "NODE_ENV=pg strapi develop",
99
"start": "NODE_ENV=production strapi start",
1010
"build": "NODE_ENV=production strapi build",
11+
"seed": "FORCE_APP_BOOTSTRAP_ONLY=true NODE_ENV=development strapi start",
1112
"strapi": "strapi"
1213
},
13-
"devDependencies": {},
14+
"devDependencies": {
15+
"@faker-js/faker": "^7.3.0"
16+
},
1417
"dependencies": {
1518
"@strapi/plugin-graphql": "4.2.2",
1619
"@strapi/plugin-i18n": "4.2.2",

cms/src/_seed/constants.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
SEED_USERNAME: 'seed_user',
3+
APPLICATION_COLLECTION_TYPE_UIDS: [
4+
'api::contributor.contributor',
5+
'api::partner.partner',
6+
'api::project.project',
7+
],
8+
}

cms/src/_seed/helpers.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
const { APPLICATION_COLLECTION_TYPE_UIDS } = require('./constants');
2+
3+
/**
4+
* @param {Strapi} strapi
5+
*/
6+
const ensureSQLite = (strapi) => {
7+
console.log('verifying db as local SQLite')
8+
if (strapi.db.config.connection.client !== 'sqlite') {
9+
throw new Error('strapi is NOT using local SQLite! Please, verify usage of SQLite before clearing data')
10+
}
11+
}
12+
13+
const randomBoolean = () => Math.random() < 0.5
14+
15+
/**
16+
* @param {Strapi} strapi
17+
* @returns {Promise<void>}
18+
*/
19+
const clearData = async (strapi) => {
20+
ensureSQLite(strapi)
21+
22+
const collectionTypeUids = [...APPLICATION_COLLECTION_TYPE_UIDS, 'plugin::users-permissions.user']
23+
const bulkClears = []
24+
25+
for (const collectionTypeUid of collectionTypeUids) {
26+
const collectionClear = strapi.query(collectionTypeUid).deleteMany({
27+
where: {
28+
id: {
29+
$notNull: true,
30+
},
31+
},
32+
});
33+
34+
bulkClears.push(collectionClear)
35+
}
36+
37+
await Promise.all(bulkClears)
38+
}
39+
40+
const sampleSize = (arr, num = Math.floor(Math.random() * arr.length)) => {
41+
if (!arr || arr.length === 0) {
42+
throw new Error('[sampleSize] valid arr is mandatory!')
43+
}
44+
45+
const shuffled = [...arr].sort(() => 0.5 - Math.random());
46+
47+
return shuffled.slice(0, num);
48+
}
49+
50+
module.exports = {
51+
ensureSQLite,
52+
randomBoolean,
53+
clearData,
54+
sampleSize
55+
}

cms/src/_seed/index.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const { seedUserExists, generateUserData } = require('./user');
2+
const { clearData } = require('./helpers');
3+
const { generatePartnerData } = require('./partner');
4+
const { generateProjectAndContributorData } = require('./project-and-contributor');
5+
6+
/**
7+
* @param {Strapi} strapi
8+
* @returns {Promise<void>}
9+
*/
10+
const generateSeedData = async (strapi) => {
11+
const dataExists = await seedUserExists(strapi)
12+
const forceBootstrap = process.env.FORCE_APP_BOOTSTRAP_ONLY === 'true'
13+
14+
const skipGeneration = dataExists && !forceBootstrap
15+
16+
if (skipGeneration) {
17+
console.log('skipping seed data generation...')
18+
return
19+
}
20+
21+
if (forceBootstrap) {
22+
console.log('forcing seed data re-creation...')
23+
await clearData(strapi)
24+
console.log('existing data has been cleaned!')
25+
}
26+
27+
console.log('generating seed data...')
28+
29+
await Promise.all([
30+
generateProjectAndContributorData(strapi),
31+
generatePartnerData(strapi),
32+
generateUserData(strapi)
33+
]).catch(e => {
34+
console.error('error during generating seed data! Stopping the application...')
35+
throw new Error(e)
36+
})
37+
38+
console.log('generating seed data has been finished successfully!')
39+
}
40+
41+
module.exports = {
42+
generateSeedData
43+
}

cms/src/_seed/partner.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const { faker } = require('@faker-js/faker');
2+
const { randomBoolean } = require('./helpers');
3+
4+
/**
5+
* @param {Strapi} strapi
6+
* @returns {Promise<void>}
7+
*/
8+
const generatePartnerData = async (strapi) => {
9+
console.log('generating partners')
10+
11+
const { DEV_SEED_DATA_PARTNERS } = process.env
12+
const partnersSize = DEV_SEED_DATA_PARTNERS ? parseInt(DEV_SEED_DATA_PARTNERS) : 5
13+
14+
const bulkPartnerPromises = []
15+
const randomPartnersData = new Array(partnersSize).fill(null).map(_randomPartner)
16+
17+
for (const randomPartnerData of randomPartnersData) {
18+
const randomPartnerPromise = strapi.entityService.create('api::partner.partner', { data: randomPartnerData })
19+
bulkPartnerPromises.push(randomPartnerPromise)
20+
}
21+
22+
await Promise.all(bulkPartnerPromises)
23+
}
24+
25+
const _randomPartner = () => ({
26+
name: faker.company.companyName(),
27+
website: faker.internet.url(),
28+
twitter_handle: faker.internet.userName().toLowerCase(),
29+
publishedAt: randomBoolean() ? new Date().toISOString() : null
30+
/**
31+
* "Media Library Upload" is only working via RestAPI, but when we generate seed data, no server is running
32+
* Skipping logos generation so far
33+
*/
34+
// logo_dark
35+
// logo_light
36+
})
37+
38+
module.exports = {
39+
generatePartnerData
40+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
const { faker } = require('@faker-js/faker');
2+
const { randomBoolean, sampleSize } = require('./helpers');
3+
/**
4+
* @param {Strapi} strapi
5+
* @returns {Promise<void>}
6+
*/
7+
const generateProjectAndContributorData = async (strapi) => {
8+
console.log(`generating projects and contributors`)
9+
10+
const { DEV_SEED_DATA_PROJECTS, DEV_SEED_DATA_CONTRIBUTORS } = process.env
11+
const projectsSize = DEV_SEED_DATA_PROJECTS ? parseInt(DEV_SEED_DATA_PROJECTS) : 8
12+
const contributorsSize = DEV_SEED_DATA_CONTRIBUTORS ? parseInt(DEV_SEED_DATA_CONTRIBUTORS) : 15
13+
14+
const bulkProjectPromises = []
15+
const bulkContributorPromises = []
16+
17+
const randomProjectsData = new Array(projectsSize).fill(null).map(_randomProject)
18+
const randomContributorsData = new Array(contributorsSize).fill(null).map(_randomContributor)
19+
20+
for (const randomProjectData of randomProjectsData) {
21+
const randomProjectPromise = strapi.entityService.create('api::project.project', { data: randomProjectData })
22+
bulkProjectPromises.push(randomProjectPromise)
23+
}
24+
25+
const projects = await Promise.all(bulkProjectPromises)
26+
27+
for (const randomContributorData of randomContributorsData) {
28+
const addProject = randomBoolean()
29+
30+
const randomContributorPromise = strapi.entityService.create('api::contributor.contributor', {
31+
data: {
32+
...randomContributorData,
33+
...addProject ? ({ projects: sampleSize(projects).map(project => project.id) }) : {}
34+
}
35+
})
36+
bulkContributorPromises.push(randomContributorPromise)
37+
}
38+
39+
await Promise.all(bulkContributorPromises)
40+
}
41+
42+
const _randomProject = () => {
43+
const username = faker.internet.userName().toLowerCase()
44+
45+
return {
46+
name: faker.company.companyName(),
47+
hero_image: faker.image.avatar(),
48+
description_short: faker.company.bs(),
49+
description_long: faker.lorem.paragraph(),
50+
ens_name: `${ username }.eth`,
51+
website: faker.internet.url(),
52+
twitter_handle: username,
53+
publishedAt: randomBoolean() ? new Date().toISOString() : null
54+
}
55+
}
56+
57+
const _randomContributor = () => {
58+
const username = faker.internet.userName().toLowerCase()
59+
60+
return {
61+
name: faker.name.findName(),
62+
ens_name: `${ username }.eth`,
63+
twitter_handle: username,
64+
discord_handle: `${ username }#${ faker.random.numeric(4) }`,
65+
publishedAt: randomBoolean() ? new Date().toISOString() : null
66+
}
67+
}
68+
69+
module.exports = {
70+
generateProjectAndContributorData
71+
}

0 commit comments

Comments
 (0)