Hi,
I’m new to Forge UI and developing a app , to get text from openai and save it a CF . As per logs it is fetching the data but it is automatically invoking updateACfield() method and throwing below error from the modaldialog on the submit button. Can you please help me here
ERROR 2023-09-22T09:21:36.623Z 0461760a-7b4d-4274-8bcc-14d168bdb770 #<Promise> could not be cloned.
TypeError: #<Promise> could not be cloned.
at s.wrapValue (bootstrap.js:1:2144)
at s.wrap (bootstrap.js:1:1504)
at bootstrap.js:1:2270
at Array.map (<anonymous>)
at s.wrapArray (bootstrap.js:1:2257)
at s.wrap (bootstrap.js:1:1574)
at bootstrap.js:1:797
at Array.map (<anonymous>)
at s.applyIgnored (bootstrap.js:1:784)
at bootstrap.js:1:9341
Below is the complete code
import ForgeUI, {IssueAction, ModalDialog,useAction ,render, AdminPage, IssuePanel,Form,Fragment,Text,TextField, useState,Button, useProductContext, useEffect, ProjectSettingsPage } from '@forge/ui';
//import ForgeUI, { render, Fragment, Text, IssuePanel,Button, ButtonSet, useProductContext, useState, Component, useEffect} from "@forge/ui";
import api, { route } from "@forge/api";
import { storage, fetch } from '@forge/api';
const cancel = () => { setOpen(false)};
const actionButtons = [
<Button text="Cancel" onClick={cancel} />
// , <Button text="Save" onClick={updateACfield()} />
];
const AdminPageStoreOpenAIKey = () => {
let strOpenAIKey="OpenAIKey";
const { platformContext } = useProductContext();
const [formState, setFormState] = useState(undefined);
const getOpenAIKeyfromStorage = async()=>{
const data = await storage.get(strOpenAIKey);
console.log("getopenAIKeyfromStorage ="+data);
// setOpenAIKeyValue(dataFromStorage);
return data || " ";
};
const [originalAIKey] = useAction(value => value, async () => await getOpenAIKeyfromStorage());
const [openAIKeyValue, setOpenAIKeyValue] = useState(originalAIKey);
console.log(" ************* Starting ****************")
// let dataFromStorage=getOpenAIKeyfromStorage();
// setOpenAIKeyValue(dataFromStorage);
console.log("In App from Storage APIKey dataFromStorage ="+originalAIKey);
const handleOpenAIKeySubmit = async (formState) => {
let openAIkey=formState[strOpenAIKey];
console.log(" In handleOpenAIKeySubmit : openAIKey "+openAIkey);
// Store the text in storage
await storage.set(strOpenAIKey, formState[strOpenAIKey]);
setFormState(openAIkey);
setOpenAIKeyValue(openAIkey);
const data = await storage.get(strOpenAIKey);
console.log("New Key from Storage ="+data);
return data;
};
const handleOpenAIKeyChange = async (newValue) => {
setFormState(newValue);
setOpenAIKeyValue(newValue);
try{
await storage.set(strOpenAIKey,newValue);
}catch(error){
console.log("ERROR while setting to storage in handleOpenAIKeyChange ="+error);
}
};
return (
<Fragment>
<Form onSubmit={handleOpenAIKeySubmit} actionButtons={actionButtons}>
<TextField name={strOpenAIKey} label="Enter OpenAI Secret Key" defaultValue={openAIKeyValue} />
</Form>
{formState && <Text>{formState[{strOpenAIKey}]}</Text>}
</Fragment>
);
};
const ProjectSettingApp = () => {
const context = useProductContext();
// const storage = platformContext.extension.storage;
// Define a state variable to store the toggle state
const [isToggled, setIsToggled] = useState(false);
//console.log(" useEffect-> projToggleKey = " +JSON.stringify(context));
// Function to load the toggle state from storage
useEffect(async () => {
let projToggleKey=context.platformContext.projectKey+"-isToggled";
console.log(" useEffect-> projToggleKey = " +projToggleKey)
const storedIsToggled = await storage.get(projToggleKey);
if (storedIsToggled !== undefined) {
console.log(" useEffect-> projToggleKey = " +projToggleKey + ", storedIsToggled "+storedIsToggled);
setIsToggled(storedIsToggled);
}else{
console.log(" useEffect-> projToggleKey = " +projToggleKey + ", storedIsToggled not defined" );
}
}, []);
// Function to handle the toggle button click
const handleToggleClick = async () => {
const updatedIsToggled = !isToggled;
setIsToggled(updatedIsToggled);
let projToggleKey=context.platformContext.projectKey+"-isToggled";
console.log(" handleToggleClick-> projToggleKey = " +projToggleKey + ", updatedIsToggled "+updatedIsToggled);
// Store the updated state in storage
await storage.set(projToggleKey, updatedIsToggled);
};
return (
<Fragment>
<Text>Enable OpenAI Settings</Text>
<Button
text={`${isToggled ? "Enabled" : "Disabled"}`}
onClick={handleToggleClick}
/>
</Fragment>
);
};
async function updateACfield(generatedAC) {
const context = useProductContext();
console.log(`Inside updateACfield v1 : ${generateAC}`);
try{
await api
.asUser()
.requestJira(route`/rest/api/3/issue/${context.platformContext.issueKey}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
fields: {
customfield_10039: generateAC,
},
}),
});
}catch(error){
console.error("Error in updateACfieldWithDesc updtating CF ",error);
}
}
const IssuePanelApp = () => {
const { issueContext, platformContext } = useProductContext();
const [description, setDescription] = useState('');
const [generatedAC, setGeneratedAC] = useState('');
const [isOpen, setOpen] = useState(true)
if (!isOpen) {
return null;
}
//let OPENAI_API_ENDPOINT = `https://api.openai.com/v1/engines/text-davinci-003/completions`;
let OPENAI_API_KEY = ''
const OPENAI_API_ENDPOINT = `https://api.openai.com/v1/chat/completions`;
const MODEL = "gpt-3.5-turbo-0613";
//const MODEL = "text-davinci-003";
const MAX_TOKENS = 200;
const TEMP = 0.5;
// Function to fetch issue description
useEffect(async () => {
const context = useProductContext();
const res = await api
.asUser()
.requestJira(route`/rest/api/3/issue/${context.platformContext.issueKey}/?fields=description&expand=renderedFields`);
const jsonResp = await res.json();
console.log(`jsonResp : ${JSON.stringify(jsonResp)}`);
const description = jsonResp.renderedFields.description;
setDescription(description.replace( /(<([^>]+)>)/ig, '') || '');
}, []);
// Function to generate acceptance criteria
const generateAC = async () => {
let strOpenAIKey="OpenAIKey";
//let prompt = "Generate Acceptance criteria for "+description+" in 3 lines in json format having seperate attributes for acceptance criteria,Test Stratergy, Definition for done";
// let prompt = "Generate Acceptance criteria, Definition of Done in 3 lines each for "+description+" and format the ouput in correct correct json format";
let prompt ="Description: "+description+". Apply the following instruction from a user to the above Description. Answer the instruction exclusively in form of a markup format that contains the answer items. The answers are strings in markup fomat with hedings. Instruction: `generate improved description and only precise 1 lines each for Acceptance criteria, definition of done, Test Strategy and Test cases in standrard table format`"
// let prompt = "Generate a perfect Acceptance Criteria for the below description "+description;
console.log(" Inside generateAC prompt = "+prompt);
let apiName = "generateAC";
try {
const data = await storage.get(strOpenAIKey);
console.log("'"+strOpenAIKey+"' from Storage ="+data);
OPENAI_API_KEY=data;
// Replace with your OpenAI API call to generate acceptance criteria
const response = await fetch(OPENAI_API_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + OPENAI_API_KEY.replace(/"/g, ''),
},
body: JSON.stringify({
"model": MODEL,
"messages":[
{
"role": "user",
"content": prompt
}
],
"temperature": TEMP,
"max_tokens": MAX_TOKENS,
"top_p": 1,
"frequency_penalty": 0.51,
"presence_penalty": 0.1,
}
)
/*({
"prompt":prompt,
"temperature": TEMP,
"max_tokens": MAX_TOKENS,
"top_p": 1,
"frequency_penalty": 0.51,
"presence_penalty": 0.1,
}
)*/
,
});
if (!response.ok) {
const message = `Error from ${apiName}: ${response.status} ${await response.text()}`;
console.error(message);
throw new Error(message);
} else{
console.debug(`Response from ${apiName}: ${await response.text()}`);
const data = await response.json();
console.log(`jsonResp : ${JSON.stringify(data)}`);
// setGeneratedAC(data.choices[0].text);
setGeneratedAC(data.choices[0].message.content);
}
} catch (error) {
console.error('Error generating acceptance criteria:', error);
}
};
return (
<ModalDialog header="Generate Acceptance Criteria" onClose={() => setOpen(false)} >
<Text>Issue Description:</Text>
<Text>{description.replace( /(<([^>]+)>)/ig, '')}</Text>
<Button text="Generate Acceptance Criteria" onClick={generateAC} />
{generatedAC && (
<Fragment>
<Text>Generated Acceptance Criteria:</Text>
<Text >{generatedAC}</Text>
<Button text="Save" onClick={updateACfield(generatedAC)} />
</Fragment>
)}
</ModalDialog>
);
};
export const adminPage = render(
<AdminPage>
<AdminPageStoreOpenAIKey />
</AdminPage>
);
export const projectAdminPage = render(
<ProjectSettingsPage>
<ProjectSettingApp />
</ProjectSettingsPage>
);
export const issuePanelPage = render(
<IssueAction>
<IssuePanelApp />
</IssueAction>
);
Can you also suggest if i’m making any mistake