Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Jira and PR creation logic with submitRequest button #132

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions components/release/openshift_version_select.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {getReleaseBranchesFromOcpBuildData} from "../api_calls/release_calls";

const {Option} = Select;

function OpenshiftVersionSelect({ onVersionChange, initialVersion, redirectOnSelect=false }) {
function OpenshiftVersionSelect({ onVersionChange, initialVersion, alignment='right', padding='30px', redirectOnSelect=false }) {
const [data, setData] = useState([]);

const setDataFunc = () => {
Expand Down Expand Up @@ -47,7 +47,7 @@ function OpenshiftVersionSelect({ onVersionChange, initialVersion, redirectOnSel

// Simplify the render function by removing the if-else
return (
<div align={"right"} style={{padding: "30px"}}>
<div align={alignment} style={{padding: {padding}}}>
<Select defaultValue={initialVersion} placeholder={<div style={{color: "black"}}>Openshift Version</div>} onChange={onChangeFunc}>
{generateSelectOptionFromStateDate(data)}
</Select>
Expand Down
165 changes: 123 additions & 42 deletions components/self-service/new-content-done.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import Box from '@mui/material/Box'
import { Button, Typography } from '@mui/material'
import { Button, Typography, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material';
import * as React from 'react';
import { useState } from 'react';
import { useNewContentState } from './new-content-state'
import YAML from 'yaml'
import { makeApiCall } from '../api_calls/api_calls';

export default function NewContentDone() {
const { activeStep, handleBack, handleReset, inputs } = useNewContentState();

const [dialogOpen, setDialogOpen] = useState(false);
const [dialogContent, setDialogContent] = useState([]);
const [dialogTitle, setDialogTitle] = useState('');
const [isSubmitted, setIsSubmitted] = useState(false);

const distgit_ns = inputs.componentType == 'rpm' ? 'rpms' : 'containers';
let web_url = inputs.sourceRepo;
if (web_url?.endsWith('.git'))
Expand Down Expand Up @@ -95,6 +103,7 @@ export default function NewContentDone() {
const generateYaml = () => {
const result: Record<string, any> = {
meta: {
release: inputs.imageReleaseVersion,
payload_name: inputs.payloadName,
component_type: inputs.componentType,
distgit_repo: `${distgit_ns}/${distgitName}`,
Expand All @@ -112,52 +121,93 @@ export default function NewContentDone() {
return YAML.stringify(result);
};

// safeEncodeURIComponent wraps the library function, and additionally encodes
// square brackets. Square brackets are NOT unsafe per RFC1738, but Google and
// others mishandle them.
const safeEncodeURIComponent = (value) => {
return encodeURIComponent(value)
.replace('[', '%5B')
.replace(']', '%5D')
.replace('{', '%7B')
.replace('}', '%7D')
}

// Create jira summary.
const jiraSummary = `[BuildAuto] Add OCP component - ${distgit_ns}/${distgitName}`
const jiraSummaryEncoded = safeEncodeURIComponent(jiraSummary);

// Combine YAMLs as Jira description
const jiraDescription = `${generateYaml()}\n\n${generateHBYaml()}`;
const jiraDescriptionEncoded = safeEncodeURIComponent(jiraDescription);
const handleSubmitRequest = async () => {
const imageName = web_url?.substring(web_url.lastIndexOf('/')+ 1)
const jiraDescription = `${generateYaml()}\n\n${generateHBYaml()}`;
const ARTProjectID = "ART"
const ARTStoryTypeID = "Story"
const component = "Release work";
const priority = "Normal";

const releaseWork = safeEncodeURIComponent("Release work")
const fileContent = `content:
source:
dockerfile: Dockerfile.openshift
git:
branch:
target: release-{MAJOR}.{MINOR}
url: ${repo_url}
web: ${web_url}
ci_alignment:
streams_prs:
ci_build_root:
stream: rhel-9-golang-ci-build-root
distgit:
branch: rhaos-{MAJOR}.{MINOR}-rhel-9
component: ${imageName}-container
enabled_repos:
- rhel-9-appstream-rpms
- rhel-9-baseos-rpms
for_payload: false
from:
builder:
- stream: rhel-9-golang
member: openshift-enterprise-base-rhel9
name: openshift/${imageName}-rhel9
owners:
- ${inputs.deliveryRepoImageOwner}@redhat.com
`;

const ARTProjectID = "12323120"
const ARTStoryTypeID = 17
const params = {
image_name: imageName,
release_for_image: inputs.imageReleaseVersion,
file_content: fileContent,
jira_summary: jiraSummary,
jira_description: jiraDescription,
jira_project_id: ARTProjectID,
jira_story_type_id: ARTStoryTypeID,
jira_component: component,
jira_priority: priority,

// the default mode is test mode (i.e., create fake PR and Jira) so you can easily
// test the UI and API and be intentional about actually creating the PR and Jira.
git_test_mode: "false",
jira_test_mode: "false"
}

// Build the Jira URL
let jiraUrl = `https://issues.redhat.com/secure/CreateIssueDetails!init.jspa?priority=10200`
jiraUrl += `&pid=${ARTProjectID}`
jiraUrl += `&issuetype=${ARTStoryTypeID}`
jiraUrl += `&summary=${jiraSummaryEncoded}`
jiraUrl += `&description=${jiraDescriptionEncoded}`
jiraUrl += `&component=${releaseWork}`
try {
const response = await makeApiCall('/api/v1/git_jira_api', 'GET', {}, {}, params, false);

const handleCreateJira = () => {
setDialogTitle('Error occurred');
if (response.status === "success") {
// Track if submitted successfully so we can suppress the SubmitRequest button.
// Override the dialog title and content to show Jira and PR URLs.
setIsSubmitted(true);
setDialogTitle('Jira and PR created successfully:');
setDialogContent([response.jira_url, response.pr_url]);
} else {
setDialogContent([`ART UI server return status: ${response.status}`, `Error message: ${response.error}`]);
}
setDialogOpen(true);
} catch (error) {
setDialogContent(['Error in call to ART UI server', `ART UI server error: ${error}`]);
setDialogOpen(true);
}
};

// Open the Jira creation page in a new tab with pre-filled data
window.open(jiraUrl, '_blank');
const handleCloseDialog = () => {
setDialogOpen(false);
};

return (<Box
component="div"
sx={{
'& > :not(style)': { m: 2},
'& > :not(style)': { m: 2 },
}}
>
<Box>
<Typography sx={{ mb: 3 }}>About to make a Jira in the <strong>ART</strong> project using the <strong>Summary</strong> and <strong>Description</strong> below:</Typography>
<Typography sx={{ mb: 3 }}>About to make a Jira in the <strong>ART</strong> project using the <strong>Summary</strong> and <strong>Description</strong> below:</Typography>
<hr />
<Typography component="h6" sx={{ mt: 2 }}><b>Summary</b></Typography>
<Typography>{jiraSummary}</Typography>
Expand All @@ -174,19 +224,20 @@ export default function NewContentDone() {
Follow these steps:
</Typography>
<ul>
<li>If the above looks good, login to Jira <a href="https://issues.redhat.com/login.jsp?os_destination=%2Fdefault.jsp" target="_blank" rel="noopener noreferrer">here</a> (a separate tab will open) then click the "Create Jira" button below</li>
<li>In the "Create Issue" page, set the "Reporter" field as your UserId and click the "Create" button at the bottom</li>
<li>Share the ticket to <strong>@release-artists</strong> on <strong>#forum-ocp-art</strong> on Slack</li>
<li>If the above looks good, click SubmitRequest and a Jira and PR will be created</li>
<li>Inform <strong>@release-artists</strong> on <strong>#forum-ocp-art</strong> on Slack about the Jira and PR</li>
</ul>
</Box>
<Box sx={{ py: 2 }}>
<Button
variant="contained"
onClick={handleCreateJira}
sx={{ mt: 1, mr: 1 }}
>
Create Jira
</Button>
{!isSubmitted && (
<Button
variant="contained"
onClick={handleSubmitRequest}
sx={{ mt: 1, mr: 1 }}
>
SubmitRequest
</Button>
)}
<Button
variant="contained"
onClick={handleReset}
Expand All @@ -201,6 +252,36 @@ export default function NewContentDone() {
>
Back
</Button>
<Dialog
open={dialogOpen}
onClose={handleCloseDialog}
>
<DialogTitle>{dialogTitle}</DialogTitle>
<DialogContent>
<DialogContentText>
{dialogContent.map((text, index) => {
return (
<span key={index}>
{text.startsWith('https://') ? (
<a href={text} target="_blank" rel="noopener noreferrer">
{text}
</a>
) : (
text
)}
<br />
</span>
);
})}
</DialogContentText>
</DialogContent>
<DialogActions sx={{ justifyContent: 'center' }}>
<Button onClick={handleCloseDialog} color="primary">
OK
</Button>
</DialogActions>
</Dialog>

</Box>
</Box>)
}
28 changes: 26 additions & 2 deletions components/self-service/new-content-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Inputs, useNewContentState } from './new-content-state'
import HelpIcon from '@mui/icons-material/Help';
import frontendConfig from "../../frontend.config.json"
import { useState } from 'react';

import OpenshiftVersionSelect from "../release/openshift_version_select";

const applicationCategories = [
'API Management',
Expand Down Expand Up @@ -116,7 +116,12 @@ export default function NewContentForm({ onSubmit, defaultValues }: { onSubmit?:
setValue("deliveryRepoApplicationCategories", new Set(newCategories));
setSelectedAppCategories(newCategories);
}
};
};

const [userImageReleaseVersion, setUserImageReleaseVersion] = useState('openshift-4.17')
const handleImageReleaseChange = (version: string) => {
setUserImageReleaseVersion(version);
}

const values = watch();

Expand All @@ -129,6 +134,25 @@ export default function NewContentForm({ onSubmit, defaultValues }: { onSubmit?:
}}
>
<Typography component="h1" variant="h5" sx={{ my: 2 }}>General</Typography>
<Box>
<FormLabel id="openshift-image-version">Onboard image to this OpenShift Version</FormLabel>
<Controller
control={control}
name="imageReleaseVersion"
defaultValue={userImageReleaseVersion}
render={({ field: { onChange, value } }) => (
<OpenshiftVersionSelect
initialVersion={value}
alignment='left'
padding='0px'
onVersionChange={(version) => {
onChange(version);
setUserImageReleaseVersion(version);
}}
/>
)}
/>
</Box>
<Box>
<FormControl>
<FormLabel id="component-type-group-label">Component Type</FormLabel>
Expand Down
1 change: 1 addition & 0 deletions components/self-service/new-content-state.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { createContext, useContext, ReactNode, useMemo, useReducer, useCallback } from "react";

export type Inputs = {
imageReleaseVersion?: string,
componentType?: string,
distgit?: string,
productManager?: string,
Expand Down
2 changes: 1 addition & 1 deletion components/self-service/new-content-wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function NewContentStep({ activeStep }: { activeStep: number }) {
return (
<Box sx={{ m: 4 }}>
<Typography component="h1" variant="h4" sx={{ my: 2 }}>Step 2: Fill the onboarding form</Typography>
<NewContentForm onSubmit={(data: any) => console.log(data)} />
<NewContentForm onSubmit={(data: any) => console.log('Fill onboarding form: ', data)} />
</Box>)
case 2:
return (
Expand Down