Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Add the DynamicPayloadModal to post the dyanmic_payload #393

Merged
merged 4 commits into from
Apr 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions openapi/v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1840,6 +1840,8 @@ components:
type: string
payload:
type: string
dynamic_payload:
$ref: '#/components/schemas/DynamicPayload'
production_environment:
type: boolean
review:
Expand All @@ -1861,6 +1863,39 @@ components:
- auto_merge
- payload
- production_environment
DynamicPayload:
type: object
properties:
enabled:
type: boolean
inputs:
# Dictionaries
type: object
additionalProperties:
type: object
properties:
type:
type: string
enum:
- select
- number
- string
- boolean
required:
type: boolean
default:
anyOf:
- type: number
- type: string
- type: boolean
description:
type: string
options:
type: array
items:
type: string
required:
- type
Reviews:
type: array
items:
Expand Down
10 changes: 9 additions & 1 deletion ui/src/apis/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ interface ConfigData {
interface EnvData {
name: string
required_contexts?: string[]
dynamic_payload?: {
enabled: boolean,
inputs: any,
}
review?: {
enabled: boolean
reviewers: string[]
Expand All @@ -19,11 +23,15 @@ interface EnvData {

const mapDataToConfig = (data: ConfigData): Config => {
const envs: Env[] = data.envs.map((e: EnvData) => {
const { review } = e
const { dynamic_payload, review } = e

return {
name: e.name,
requiredContexts: e.required_contexts,
dynamicPayload: (dynamic_payload)? {
enabled: dynamic_payload?.enabled,
inputs: dynamic_payload?.inputs,
} : undefined,
review,
}
})
Expand Down
6 changes: 4 additions & 2 deletions ui/src/apis/deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,13 @@ export const getDeployment = async (namespace: string, name: string, number: num
return deployment
}

export const createDeployment = async (namespace: string, name: string, type: DeploymentType = DeploymentType.Commit, ref: string, env: string): Promise<Deployment> => {
// eslint-disable-next-line
export const createDeployment = async (namespace: string, name: string, type: DeploymentType = DeploymentType.Commit, ref: string, env: string, payload?: any): Promise<Deployment> => {
const body = JSON.stringify({
type,
ref,
env
env,
dynamic_payload: payload
})
const response = await _fetch(`${instance}/api/v1/repos/${namespace}/${name}/deployments`, {
headers,
Expand Down
3 changes: 2 additions & 1 deletion ui/src/components/DeployForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ export default function DeployForm(props: DeployFormProps): JSX.Element {
return (
<Form
onFinish={onClickFinish}
name="deploy">
name="deploy"
>
<Form.Item
{...selectLayout}
rules={[{required: true}]}
Expand Down
127 changes: 127 additions & 0 deletions ui/src/components/DynamicPayloadModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { Modal, Form, Select, Input, InputNumber, Checkbox } from "antd"

import { Env, DynamicPayloadInput, DynamicPayloadInputTypeEnum } from "../models"

export interface DynamicPayloadModalProps {
visible: boolean
env: Env
onClickOk(values: any): void
onClickCancel(): void
}

export default function DynamicPayloadModal(props: DynamicPayloadModalProps): JSX.Element {
const [ form ] = Form.useForm()

const onClickOk = () => {
form.validateFields()
.then(values => {
props.onClickOk(values)
})
.catch(info => {
console.log(info)
})
}

const onClickCancel = () => {
props.onClickCancel()
}

// Build items dynamically
const items = new Array<JSX.Element>()
if (props.env.dynamicPayload) {
// Object.entries(props.env.dynamicPayload.inputs).forEach()
Object.entries(props.env.dynamicPayload.inputs).forEach((entry) => {
const [name, input] = entry
items.push(<DynamicItem key={name} name={name} input={input}/>)
})
}

// Build the initialValues
const initialValues: any = {}
if (props.env.dynamicPayload) {
Object.entries(props.env.dynamicPayload.inputs).forEach((entry) => {
const [name, input] = entry
initialValues[name] = input.default
})
}

return (
<Modal
visible={props.visible}
onOk={onClickOk}
onCancel={onClickCancel}
>
<Form
form={form}
layout="vertical"
name="dynamic_payload"
initialValues={initialValues}
>
{items.map(item => item)}
</Form>
</Modal>
)
}

interface DynamicItemProps {
name: string
input: DynamicPayloadInput
}

function DynamicItem({name, input}: DynamicItemProps): JSX.Element {
// Capitalize the first character.
const label = name.charAt(0).toUpperCase() + name.slice(1)
const description = input.description
const rules = (input.required)? [{required: true}] : []

switch (input.type) {
case DynamicPayloadInputTypeEnum.Select:
return (
<Form.Item
label={label}
name={name}
tooltip={description}
rules={rules}
>
<Select >
{input.options?.map((option: any, idx: any) =>
<Select.Option key={idx} value={option}>{option}</Select.Option>
)}
</Select>
</Form.Item>
)
case DynamicPayloadInputTypeEnum.String:
return (
<Form.Item
label={label}
name={name}
tooltip={description}
rules={rules}
>
<Input />
</Form.Item>
)
case DynamicPayloadInputTypeEnum.Number:
return (
<Form.Item
label={label}
name={name}
tooltip={description}
rules={rules}
>
<InputNumber />
</Form.Item>
)
case DynamicPayloadInputTypeEnum.Boolean:
return (
<Form.Item
label={label}
name={name}
tooltip={description}
rules={rules}
>
<Checkbox />
</Form.Item>
)
}
}
23 changes: 23 additions & 0 deletions ui/src/models/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,31 @@ export default interface Config {
export interface Env {
name: string
requiredContexts?: string[]
dynamicPayload?: DynamicPayload
review?: {
enabled: boolean
reviewers: string[]
}
}

export interface DynamicPayload {
enabled: boolean
inputs: {
[key: string]: DynamicPayloadInput
}
}

export interface DynamicPayloadInput {
type: DynamicPayloadInputTypeEnum
required?: boolean
default?: any
description?: string
options?: string[]
}

export enum DynamicPayloadInputTypeEnum {
Select = "select",
String = "string",
Number = "number",
Boolean = "boolean"
}
10 changes: 9 additions & 1 deletion ui/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import Deployment, {
DeploymentType,
DeploymentStatus,
} from "./Deployment"
import Config, { Env } from "./Config"
import Config, {
Env,
DynamicPayload,
DynamicPayloadInput,
DynamicPayloadInputTypeEnum
} from "./Config"
import Commit, { Author, Status, StatusState } from "./Commit"
import Branch from "./Branch"
import Tag from "./Tag"
Expand Down Expand Up @@ -33,6 +38,8 @@ export type {
DeploymentStatus,
Config,
Env,
DynamicPayload,
DynamicPayloadInput,
Commit,
Author,
Status,
Expand All @@ -58,6 +65,7 @@ export {
HttpUnprocessableEntityError,
DeploymentStatusEnum,
DeploymentType,
DynamicPayloadInputTypeEnum,
StatusState,
RequestStatus,
ReviewStatusEnum,
Expand Down
10 changes: 5 additions & 5 deletions ui/src/redux/repoDeploy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,9 @@ export const fetchUser = createAsyncThunk<User, void, { state: {repoDeploy: Repo
}
)

export const deploy = createAsyncThunk<void, void, { state: {repoDeploy: RepoDeployState}}> (
export const deploy = createAsyncThunk<void, any, { state: {repoDeploy: RepoDeployState}}> (
"repoDeploy/deploy",
async (_ , { getState, rejectWithValue, requestId }) => {
async (payload, { getState, rejectWithValue, requestId }) => {
const { namespace, name, env, type, branch, commit, tag, deploying, deployId } = getState().repoDeploy
if (!env) {
throw new Error("The env is undefined.")
Expand All @@ -271,11 +271,11 @@ export const deploy = createAsyncThunk<void, void, { state: {repoDeploy: RepoDep
try {
let deployment: Deployment
if (type === DeploymentType.Commit && commit) {
deployment = await createDeployment(namespace, name, type, commit.sha, env.name)
deployment = await createDeployment(namespace, name, type, commit.sha, env.name, payload)
} else if (type === DeploymentType.Branch && branch) {
deployment = await createDeployment(namespace, name, type, branch.name, env.name)
deployment = await createDeployment(namespace, name, type, branch.name, env.name, payload)
} else if (type === DeploymentType.Tag && tag) {
deployment = await createDeployment(namespace, name, type, tag.name, env.name)
deployment = await createDeployment(namespace, name, type, tag.name, env.name, payload)
} else {
throw new Error("The type should be one of them: commit, branch, and tag.")
}
Expand Down
35 changes: 30 additions & 5 deletions ui/src/views/RepoDeploy.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { shallowEqual } from "react-redux";
import { useParams } from "react-router-dom";
import { PageHeader, Result, Button } from "antd";
Expand All @@ -20,9 +20,11 @@ import {
addTagManually,
searchCandidates,
fetchUser,
deploy} from "../redux/repoDeploy"
deploy
} from "../redux/repoDeploy"

import DeployForm, {Option} from "../components/DeployForm"
import DynamicPayloadModal from "../components/DynamicPayloadModal";

const { actions } = repoDeploySlice

Expand All @@ -37,6 +39,7 @@ export default function RepoDeploy(): JSX.Element {
display,
config,
envs,
env,
currentDeployment,
branches,
branchStatuses,
Expand All @@ -47,6 +50,8 @@ export default function RepoDeploy(): JSX.Element {
deploying } = useAppSelector(state => state.repoDeploy, shallowEqual)
const dispatch = useAppDispatch()

const [payloadModalVisible, setPayloadModalVisible] = useState(false);

useEffect(() => {
const f = async () => {
await dispatch(actions.init({namespace, name}))
Expand Down Expand Up @@ -99,10 +104,20 @@ export default function RepoDeploy(): JSX.Element {
}

const onClickDeploy = () => {
const f = async () => {
await dispatch(deploy())
if (env?.dynamicPayload?.enabled) {
setPayloadModalVisible(true)
} else {
dispatch(deploy(null))
}
f()
}

const onClickDeployWithPayload = (values: any) => {
dispatch(deploy(values))
setPayloadModalVisible(false)
}

const onClickCancel = () => {
setPayloadModalVisible(false)
}

if (!display) {
Expand Down Expand Up @@ -154,6 +169,16 @@ export default function RepoDeploy(): JSX.Element {
deploying={deploying === RequestStatus.Pending}
onClickDeploy={onClickDeploy}
/>
{(env)?
<DynamicPayloadModal
visible={payloadModalVisible}
env={env}
onClickOk={onClickDeployWithPayload}
onClickCancel={onClickCancel}
/>
:
<></>
}
</div>
</div>
)
Expand Down