diff --git a/dispatch.js b/dispatch.js index ea29657..5b9ab68 100644 --- a/dispatch.js +++ b/dispatch.js @@ -13,9 +13,10 @@ cluster.setupMaster({ }) if (cluster.isMaster) { - require('os').cpus().forEach((cpu, index) => { + const maxSize = +process.env.SIGMA_MAX_PROCESSORS_LIMIT || +process.env.AJDK_MAX_PROCESSORS_LIMIT || require('os').cpus().length + for (let i = 0; i < maxSize; i++) { cluster.fork() - }) + } cluster.on('listening', (worker, address) => { console.error(`[${now()}] master#${process.pid} worker#${worker.process.pid} is now connected to ${address.address}:${address.port}.`) }) diff --git a/package.json b/package.json index f21072f..9c5a9ab 100644 --- a/package.json +++ b/package.json @@ -23,54 +23,54 @@ "author": "mozhi.gyy@alibaba-inc.com", "license": "ISC", "dependencies": { - "@material-ui/core": "^4.2.0", - "@material-ui/icons": "^4.2.1", - "@material-ui/pickers": "^3.1.2", - "@material-ui/styles": "^4.2.0", + "@material-ui/core": "^4.4.3", + "@material-ui/icons": "^4.4.3", + "@material-ui/pickers": "^3.2.6", + "@material-ui/styles": "^4.4.3", "@types/json5": "^0.0.30", "animate.css": "3.7.2", "awesome-debounce-promise": "^2.1.0", "chart.js": "^2.8.0", "classnames": "^2.2.6", "clipboard-copy": "^3.1.0", - "codemirror": "5.48.0", - "connected-react-router": "^6.5.0", + "codemirror": "5.49.0", + "connected-react-router": "^6.5.2", "debounce-promise": "^3.1.2", - "formik": "^1.5.7", - "formik-material-ui": "^0.0.19", + "formik": "^1.5.8", + "formik-material-ui": "^0.0.22", "graceful": "1.0.2", - "history": "^4.9.0", + "history": "^4.10.1", "jquery": "^3.4.1", "json5": "^2.1.0", - "koa": "2.7.0", + "koa": "2.8.1", "koa-router": "7.4.0", - "koa-session": "5.12.2", + "koa-session": "5.12.3", "koa-static": "5.0.0", - "lodash": "4.17.13", - "mockjs": "1.0.1-beta3", + "lodash": "4.17.15", + "mockjs": "1.1.0", "moment": "2.24.0", "node-fetch": "2.6.0", - "normalizr": "^3.4.0", - "notistack": "^0.8.9", + "normalizr": "^3.4.1", + "notistack": "^0.9.2", "nprogress": "0.2.0", "parsleyjs": "^2.9.1", "popper.js": "^1.15.0", "prop-types": "15.7.2", "rc-tooltip": "^3.7.3", - "react": "^16.8.6", - "react-dom": "^16.8.6", + "react": "^16.9.0", + "react-dom": "^16.9.0", "react-hotkeys": "^2.0.0", "react-icons": "3.7.0", - "react-modal": "3.8.2", - "react-redux": "^7.1.0", - "react-router": "5.0.1", - "react-router-config": "5.0.1", - "react-router-dom": "5.0.1", - "react-select": "^3.0.4", - "redux": "4.0.3", - "redux-saga": "1.0.5", + "react-modal": "3.10.1", + "react-redux": "^7.1.1", + "react-router": "5.1.0", + "react-router-config": "5.1.0", + "react-router-dom": "5.1.0", + "react-select": "^3.0.5", + "redux": "4.0.4", + "redux-saga": "1.1.1", "reselect": "^4.0.0", - "sortablejs": "1.9.0", + "sortablejs": "1.10.0", "urijs": "1.19.1", "yup": "^0.27.0" }, @@ -84,36 +84,36 @@ ] }, "devDependencies": { - "@types/chart.js": "^2.7.55", + "@types/chart.js": "^2.8.5", "@types/classnames": "^2.2.9", - "@types/codemirror": "^0.0.76", - "@types/history": "^4.7.2", - "@types/jest": "^24.0.15", - "@types/jquery": "^3.3.30", - "@types/lodash": "^4.14.136", + "@types/codemirror": "^0.0.77", + "@types/history": "^4.7.3", + "@types/jest": "^24.0.18", + "@types/jquery": "^3.3.31", + "@types/lodash": "^4.14.140", "@types/mockjs": "^1.0.2", - "@types/node": "^12.6.2", + "@types/node": "^12.7.8", "@types/nprogress": "^0.2.0", "@types/rc-tooltip": "^3.7.1", - "@types/react": "^16.8.23", - "@types/react-dom": "^16.8.4", - "@types/react-modal": "^3.8.2", - "@types/react-redux": "^7.1.1", - "@types/react-router-config": "^5.0.0", - "@types/react-router-dom": "^4.3.4", - "@types/react-select": "^3.0.0", + "@types/react": "^16.9.3", + "@types/react-dom": "^16.9.1", + "@types/react-modal": "^3.8.3", + "@types/react-redux": "^7.1.4", + "@types/react-router-config": "^5.0.1", + "@types/react-router-dom": "^5.1.0", + "@types/react-select": "^3.0.4", "@types/sortablejs": "^1.7.2", - "@types/urijs": "^1.19.3", - "@types/yup": "^0.26.21", + "@types/urijs": "^1.19.4", + "@types/yup": "^0.26.24", "sass": "^1.23.2", "npm-run-all": "4.1.5", "pre-commit": "^1.2.2", - "react-scripts": "^3.0.1", - "standard": "12.0.1", - "tslint": "^5.18.0", - "tslint-react": "^4.0.0", - "tslint-react-hooks": "^2.1.1", - "typescript": "^3.5.3" + "react-scripts": "^3.1.2", + "standard": "14.3.1", + "tslint": "^5.20.0", + "tslint-react": "^4.1.0", + "tslint-react-hooks": "^2.2.1", + "typescript": "^3.6.3" }, "pre-commit": [ "lint" diff --git a/scripts/app.js b/scripts/app.js index fc234de..50a09c5 100644 --- a/scripts/app.js +++ b/scripts/app.js @@ -15,7 +15,6 @@ app.use(async (ctx, next) => { const ms = new Date() - start console.log(`${ctx.method} ${ctx.url} - ${ms}ms`) }) - app.use(async (ctx, next) => { await next() if (ctx.response.body && ctx.response.body.url) { diff --git a/src/actions/repository.ts b/src/actions/repository.ts index 06d700b..a1dfb94 100644 --- a/src/actions/repository.ts +++ b/src/actions/repository.ts @@ -1,3 +1,5 @@ +import { IDefaultVal } from 'components/editor/DefaultValueModal' + export const addRepository = (repository: any, onResolved: any) => ({ type: 'REPOSITORY_ADD', repository, onResolved }) export const addRepositorySucceeded = (repository: any) => ({ type: 'REPOSITORY_ADD_SUCCEEDED', repository }) export const addRepositoryFailed = (message: any) => ({ type: 'REPOSITORY_ADD_FAILED', message }) @@ -26,7 +28,7 @@ export const fetchRepositoryCount = () => ({ type: 'REPOSITORY_COUNT_FETCH' }) export const fetchRepositoryCountSucceeded = (count: any) => ({ type: 'REPOSITORY_COUNT_FETCH_SUCCEEDED', count }) export const fetchRepositoryCountFailed = (message: any) => ({ type: 'REPOSITORY_COUNT_FETCH_FAILED', message }) -export const fetchRepositoryList = ({ user, organization, name, cursor, limit } = {user: '', organization: '', name: '', cursor: '', limit: ''}) => ({ type: 'REPOSITORY_LIST_FETCH', user, organization, name, cursor, limit }) +export const fetchRepositoryList = ({ user, organization, name, cursor, limit } = { user: '', organization: '', name: '', cursor: '', limit: '' }) => ({ type: 'REPOSITORY_LIST_FETCH', user, organization, name, cursor, limit }) export const fetchRepositoryListSucceeded = (repositories: any) => ({ type: 'REPOSITORY_LIST_FETCH_SUCCEEDED', repositories }) export const fetchRepositoryListFailed = (message: any) => ({ type: 'REPOSITORY_LIST_FETCH_FAILED', message }) export const fetchOwnedRepositoryList = ( @@ -40,3 +42,15 @@ export const fetchJoinedRepositoryList = ( ) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH', user, name }) export const fetchJoinedRepositoryListSucceeded = (repositories: any) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH_SUCCEEDED', repositories }) export const fetchJoinedRepositoryListFailed = (message: any) => ({ type: 'JOINED_REPOSITORY_LIST_FETCH_FAILED', message }) + +export const fetchDefaultVals = (id: number) => ({ type: 'DEFAULT_VALS_FETCH', payload: { id } }) +export const fetchDefaultValsSucceeded = (data: IDefaultVal[]) => ({ type: 'DEFAULT_VALS_SUCCEEDED', payload: { data } }) +export const fetchDefaultValsFailed = (payload: { message: string }) => ({ type: 'DEFAULT_VALS_FAILED', payload }) + +export type IFetchDefaultValsAction = ReturnType + +export const updateDefaultVals = (id: number, data: IDefaultVal[]) => ({ type: 'UPDATE_DEFAULT_VALS_FETCH', payload: { id, data } }) +export const updateDefaultValsSucceeded = () => ({ type: 'UPDATE_DEFAULT_VALS_SUCCEEDED' }) +export const updateDefaultValsFailed = (payload: { message: string }) => ({ type: 'UPDATE_DEFAULT_VALS_FAILED', payload }) + +export type IUpdateDefaultValsAction = ReturnType diff --git a/src/components/editor/DefaultValueModal.tsx b/src/components/editor/DefaultValueModal.tsx new file mode 100644 index 0000000..12d8182 --- /dev/null +++ b/src/components/editor/DefaultValueModal.tsx @@ -0,0 +1,164 @@ +import React, { useState, useEffect } from 'react' +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' +import Button from '@material-ui/core/Button' +import Dialog from '@material-ui/core/Dialog' +import AppBar from '@material-ui/core/AppBar' +import Toolbar from '@material-ui/core/Toolbar' +import IconButton from '@material-ui/core/IconButton' +import Typography from '@material-ui/core/Typography' +import CloseIcon from '@material-ui/icons/Close' +import Slide from '@material-ui/core/Slide' +import TextField from '@material-ui/core/TextField' +import { TransitionProps } from '@material-ui/core/transitions' +import { Table, TableHead, TableRow, TableCell, TableBody, DialogContent } from '@material-ui/core' +import Delete from '@material-ui/icons/Delete' +import { useDispatch, useSelector } from 'react-redux' +import { fetchDefaultVals, updateDefaultVals } from 'actions/repository' +import { withSnackbar, WithSnackbarProps } from 'notistack' + +export interface IDefaultVal { + name: string + rule: string + value: string +} + +const useStyles = makeStyles(({ spacing }: Theme) => + createStyles({ + appBar: { + position: 'relative', + }, + title: { + marginLeft: spacing(2), + flex: 1, + }, + btn: { + marginBottom: spacing(2), + marginTop: spacing(2), + }, + topHint: { + marginBottom: spacing(2), + marginTop: spacing(2), + }, + }) +) + +const Transition = React.forwardRef((props, ref) => { + return +}) + +function DefaultValueModal({ open, handleClose, repositoryId, enqueueSnackbar }: + { open: boolean, handleClose: () => void, repositoryId: number } & WithSnackbarProps) { + const classes = useStyles() + const [editingData, setEditingData] = useState([]) + const dispatch = useDispatch() + + useEffect(() => { + dispatch(fetchDefaultVals(repositoryId)) + }, [repositoryId]) + const defaultVals: IDefaultVal[] = useSelector((state: any) => state.defaultVals) + + useEffect(() => { + setEditingData(defaultVals) + }, [defaultVals]) + + const addNewLine = () => { + setEditingData([...editingData, { + name: '', + rule: '', + value: '', + }]) + } + + const onChange = (key: keyof IDefaultVal, i: number, val: string) => { + setEditingData(editingData.map((x, j) => { + if (j === i) { + return { + ...x, + [key]: val, + } + } + return x + })) + } + + const onDelete = (i: number) => { + setEditingData(editingData.filter((_, j) => i !== j)) + } + + const onSubmit = () => { + dispatch(updateDefaultVals(repositoryId, editingData)) + handleClose() + enqueueSnackbar(`更新默认值成功`, { + variant: 'success', + autoHideDuration: 1000, + }) + } + + return ( + + + + + + + 默认值配置 + + + + +
+ 系统将根据变量名进行搜索,如果生成规则和初始值均未填写,系统才会对同名参数的Mock规则进行覆盖。 +
+
+ +
+ + + + 变量名 + 生成规则 + 初始值 + + + + + {editingData.map((row, i) => ( + + + onChange('name', i, e.target.value)} + placeholder="请输入变量名" + /> + + + onChange('rule', i, e.target.value)} + placeholder="请输入生成规则" + /> + + + onChange('value', i, e.target.value)} + placeholder="请输入初始值" + /> + + + onDelete(i)} + /> + + + ))} + +
+
+
+ ) +} + +export default withSnackbar(DefaultValueModal) diff --git a/src/components/editor/InterfaceForm.tsx b/src/components/editor/InterfaceForm.tsx index 12a38b6..2855ee3 100644 --- a/src/components/editor/InterfaceForm.tsx +++ b/src/components/editor/InterfaceForm.tsx @@ -182,7 +182,6 @@ function InterfaceForm(props: Props) { label="简介" component={TextField} multiline={true} - rows={4} fullWidth={true} /> diff --git a/src/components/editor/InterfaceSummary.css.map b/src/components/editor/InterfaceSummary.css.map index 0b2948c..c426380 100644 --- a/src/components/editor/InterfaceSummary.css.map +++ b/src/components/editor/InterfaceSummary.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["InterfaceSummary.sass"],"names":[],"mappings":"AAAA;EACE;;;AACF;EACE;;;AACF;EACE;EACA;EACA;EAMA;EACE;EACA;;AAPF;EACE;EACA;;AACF;EACE;;AAIF;EACE;;AACF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AACF;EACE;;AACF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;AACF;EACE;;AACF;EACE;EACA;;AACA;EACE;;AACF;EACE;;AACJ;EACE;;AACA;EACE;EACA;EACA;EACA;;AACJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;;AACA;EACE;;AACF;EACE;EACA;EACA;;AAEJ;EACE;;AACA;EACE;;AACF;EACE;EACA;EACA;;AACA;EACE;;AACF;EACE;EACA;EACA;;AACF;EACE;EACA;EACA;;AACJ;EACE;EACA;EACA;;AACF;EACE;EACA;EACA","file":"InterfaceSummary.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["InterfaceSummary.sass"],"names":[],"mappings":"AAAA;EACE;;;AACF;EACE;;;AAEA;EACE;;AACF;EACE;;AACF;EACE;EACA;;AACA;EACE;;AACF;EACE;;AACJ;EACE;;AACA;EACE;EACA;EACA;EACA;;AACJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEF;EACE;EACA;EACA;;AACA;EACE;;AACF;EACE;EACA;EACA;;AAEJ;EACE;;AACA;EACE;;AACF;EACE;EACA;EACA;;AACA;EACE;;AACF;EACE;EACA;EACA;;AACF;EACE;EACA;EACA;;AACJ;EACE;EACA;EACA;;AACF;EACE;EACA;EACA","file":"InterfaceSummary.css"} \ No newline at end of file diff --git a/src/components/editor/InterfaceSummary.sass b/src/components/editor/InterfaceSummary.sass index de70596..baa6e0f 100644 --- a/src/components/editor/InterfaceSummary.sass +++ b/src/components/editor/InterfaceSummary.sass @@ -2,41 +2,6 @@ float: left .ovh overflow: hidden -.SyncRoomDialog - min-width: 400px - min-height: 300px - padding: 1.64rem - ul - margin-top: 2rem - padding: 0 - li - display: block - border-radius: 5px - padding: 1rem - border: 1px solid #CCC - li + li - margin-top: 1rem - i - font-style: normal - color: #FFF - padding: 0 10px - background: #CCC - margin-left: 1rem - border-radius: 5px - zoom: .7 - p - margin-bottom: 0 - .button - float: right - margin-top: 7px - font-size: 14px - background: #28a745 - color: #FFF - line-height: 1.5 - user-select: none - padding: .375rem 2rem - border-radius: .25rem - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out .InterfaceSummary .dropdown display: inline-block diff --git a/src/components/editor/ModuleForm.tsx b/src/components/editor/ModuleForm.tsx index 78bd892..a2f58d6 100644 --- a/src/components/editor/ModuleForm.tsx +++ b/src/components/editor/ModuleForm.tsx @@ -115,7 +115,6 @@ function ModuleForm(props: Props) { label="模块简介" component={TextField} multiline={true} - rows={5} fullWidth={true} /> diff --git a/src/components/editor/RepositoryEditor.tsx b/src/components/editor/RepositoryEditor.tsx index ce5ad80..fb1731f 100644 --- a/src/components/editor/RepositoryEditor.tsx +++ b/src/components/editor/RepositoryEditor.tsx @@ -40,20 +40,38 @@ import { GoDatabase, GoJersey, GoLinkExternal, - GoPencil + GoPencil, + GoEllipsis } from 'react-icons/go' import './RepositoryEditor.css' import ExportPostmanForm from '../repository/ExportPostmanForm' import { RootState } from 'actions/types' +import DefaultValueModal from './DefaultValueModal' // DONE 2.1 import Spin from '../utils/Spin' // TODO 2.2 缺少测试器 // DONE 2.2 各种空数据下的视觉效果:空仓库、空模块、空接口、空属性 // TODO 2.1 大数据测试,含有大量模块、接口、属性的仓库 +interface Props { + auth: any + repository: any + location: any + onClearRepository: any + room: any + replace: any + router: any +} + +interface States { + defaultValuesModalOpen: boolean + update: boolean + exportPostman: boolean +} + // 展示组件 -class RepositoryEditor extends Component { +class RepositoryEditor extends Component { static propTypes = { auth: PropTypes.object.isRequired, repository: PropTypes.object.isRequired, @@ -84,6 +102,7 @@ class RepositoryEditor extends Component { this.state = { update: false, exportPostman: false, + defaultValuesModalOpen: false, } } getChildContext() { @@ -117,12 +136,12 @@ class RepositoryEditor extends Component { const mod = repository && repository.modules && repository.modules.length ? repository.modules.find((item: any) => item.id === +params.mod) || - repository.modules[0] + repository.modules[0] : {} const itf = mod.interfaces && mod.interfaces.length ? mod.interfaces.find((item: any) => item.id === +params.itf) || - mod.interfaces[0] + mod.interfaces[0] : {} const properties = itf.properties || [] @@ -205,6 +224,17 @@ class RepositoryEditor extends Component { repoId={repository.id} onClose={() => this.setState({ exportPostman: false })} /> + this.setState({ defaultValuesModalOpen: true })} + > + 默认值 + + this.setState({ defaultValuesModalOpen: false })} + repositoryId={repository.id} + />
{repository.description}
diff --git a/src/components/repository/RepositoryForm.tsx b/src/components/repository/RepositoryForm.tsx index 453ad35..adaa81c 100644 --- a/src/components/repository/RepositoryForm.tsx +++ b/src/components/repository/RepositoryForm.tsx @@ -172,7 +172,6 @@ function RepositoryForm(props: Props) { name="description" label="简介" multiline={true} - rows={3} component={TextField} fullWidth={true} /> diff --git a/src/relatives/effects/repository.ts b/src/relatives/effects/repository.ts index a121ce0..6143f00 100644 --- a/src/relatives/effects/repository.ts +++ b/src/relatives/effects/repository.ts @@ -2,6 +2,7 @@ import { call, put, select } from 'redux-saga/effects' import * as RepositoryAction from '../../actions/repository' import RepositoryService from '../services/Repository' import { RootState } from 'actions/types' +import { IFetchDefaultValsAction, fetchDefaultValsFailed, IUpdateDefaultValsAction } from '../../actions/repository' // export function* fetchRepositoryCount(action: any) { @@ -48,7 +49,7 @@ export function* updateRepository(action: any) { }) yield call(RepositoryService.updateRepository, params) yield put(RepositoryAction.updateRepositorySucceeded(params)) - yield put(RepositoryAction.fetchRepository({id: params.id})) + yield put(RepositoryAction.fetchRepository({ id: params.id })) if (action.onResolved) { action.onResolved() } } catch (e) { yield put(RepositoryAction.updateRepositoryFailed(e.message)) @@ -102,3 +103,21 @@ export function* fetchJoinedRepositoryList(action: any) { yield put(RepositoryAction.fetchJoinedRepositoryListFailed(e.message)) } } + +export function* fetchDefaultVals(action: IFetchDefaultValsAction) { + try { + const result = yield call(RepositoryService.fetchDefaultVals, action.payload) + yield put(RepositoryAction.fetchDefaultValsSucceeded(result)) + } catch (e) { + yield put(fetchDefaultValsFailed({ message: e.message })) + } +} + +export function* updateDefaultVals(action: IUpdateDefaultValsAction) { + try { + yield call(RepositoryService.updateDefaultVals, action.payload) + yield put(RepositoryAction.updateDefaultValsSucceeded()) + } catch (e) { + yield put(RepositoryAction.updateDefaultValsFailed({ message: e.message })) + } +} diff --git a/src/relatives/services/Repository.ts b/src/relatives/services/Repository.ts index 37afb56..5171b64 100644 --- a/src/relatives/services/Repository.ts +++ b/src/relatives/services/Repository.ts @@ -1,4 +1,5 @@ import { CREDENTIALS, serve } from './constant' +import { IDefaultVal } from 'components/editor/DefaultValueModal' // 仓库 export default { @@ -10,17 +11,17 @@ export default { fetchRepositoryList({ user = '', organization = '', name = '', cursor = 1, limit = 100 }: any = {}) { return fetch(`${serve}/repository/list?user=${user}&organization=${organization}&name=${name}&cursor=${cursor}&limit=${limit}`, { ...CREDENTIALS }) .then(res => res.json()) - // .then(json => json.data) + // .then(json => json.data) }, fetchOwnedRepositoryList({ user = '', name = '' }: any = {}) { return fetch(`${serve}/repository/owned?user=${user}&name=${name}`, { ...CREDENTIALS }) .then(res => res.json()) - // .then(json => json.data) + // .then(json => json.data) }, fetchJoinedRepositoryList({ user = '', name = '' }: any = {}) { return fetch(`${serve}/repository/joined?user=${user}&name=${name}`, { ...CREDENTIALS }) .then(res => res.json()) - // .then(json => json.data) + // .then(json => json.data) }, fetchRepository(id: any) { return fetch(`${serve}/repository/get?id=${id}`, { ...CREDENTIALS }) @@ -61,4 +62,18 @@ export default { .then(res => res.json()) .then(json => json.data) }, + fetchDefaultVals({ id }: { id: number }) { + return fetch(`${serve}/repository/defaultVal/get/${id}`, { ...CREDENTIALS }) + .then(res => res.json()) + .then(json => json.data) + }, + updateDefaultVals({ id, data }: { id: number, data: IDefaultVal[] }) { + return fetch(`${serve}/repository/defaultVal/update/${id}`, { + ...CREDENTIALS, + method: 'POST', + body: JSON.stringify({ list: data }), + headers: { 'Content-Type': 'application/json' }, + }) + .then(res => res.json()) + }, }