How to return only required fields of a Jira project and issueType in Forge function?

Hi,

My code call a Jira API to get create issue metadata of a project and issuetype. My code works but I would like only “required” fields. I don’t know how do that.

I read the forge example forge-feedback-collector that uses the filter function to return issueType is not “sub-task”.

const fetchIssueTypes = async () => {
  const response = await api.asApp().requestJira(route`/rest/api/2/issuetype`);
  const issueTypes = await response.json();
  return issueTypes.filter(issueType => !issueType.subtask);
};

So, I am trying to reuse the filter function to achieve my goal to return only fields with attribute required: true

// Get list of required fields to create an issue
const fetchIssueRequiredFields = async (projectId: string, issueType: string) => {

  let toUseProjectId = "10000";
  if (projectId && projectId.length > 0) {
    toUseProjectId = projectId
  }
  let toUseIssueType = "10000";
  if (issueType && issueType.length > 0) {
    toUseIssueType = issueType
  }
  const response = await api.asUser().requestJira(route`/rest/api/3/issue/createmeta?projectIds=${toUseProjectId}&issuetypeIds=${toUseIssueType}&expand=projects.issuetypes.fields`, {
  headers: {
    'Accept': 'application/json'
  }
});

  if (!response.ok) {
    return "Error to get Jira issue type list" + response.statusText;
  }

//Here how to use filter
// const requiredFields = response.projects.issuetypes.fields.filter(requiredField => required = true)");/
// return await requiredFields;

return await response.json();

};

Each time i use filter function, forge return error: .filter is not a function

thx for your help

Hi @ThibautFAURE ,
Thank you for reaching out. ‘filter’ function works on array objects. please check on the data that you are retrieving on ‘response.projects.issuetypes.fields’.

Hello @ThibautFAURE ,

I do not think that a filter call on fields will work since it is an object and could explain what you’re currently getting.

A possible route is something like

  ...

  const createMetaResponse = await response.json();

  // This is under the assumption that you are only going to pass a single `projectId` and `issueType`
  const [myProject] = createMetaResponse.projects;
  const [myIssueType] =  myProject.issuetypes;
  
  // Used `Object.entries` to convert it to array so that you can still use `filter`
  const requiredFields = Object.fromEntries(Object.entries(myIssueType.fields).filter(field => {
    const [key, value] = field;
    return value.required
  }));

  return requiredFields;

Do let us know of this works for your use case (of course with a little more refactoring and such).

Cheers,
Ian

Thx @iragudo, i adapted your code to my use-case. I added some conditions to filter my json to have the result i wanted.

console.log(requiredFields); output is :

 {
  summary: {
    required: true,
    schema: { type: 'string', system: 'summary' },
    name: 'Résumé',
    key: 'summary',
    hasDefaultValue: false,
    operations: [ 'set' ]
  }
}

But i have a little problem, because when i call my function useState(fetchIssueRequiredFields(10000, 10000)); return [ undefined ] . I don’t understand why undefined

// Get list of required fields to create an issue
const fetchIssueRequiredFields = async (projectId: string, issueType: string) => {

  let toUseProjectId = "10000";
  if (projectId && projectId.length > 0) {
    toUseProjectId = projectId
  }
  let toUseIssueType = "10000";
  if (issueType && issueType.length > 0) {
    toUseIssueType = issueType
  }
  const response = await api.asUser().requestJira(route`/rest/api/3/issue/createmeta?projectIds=${toUseProjectId}&issuetypeIds=${toUseIssueType}&expand=projects.issuetypes.fields`, {
  headers: {
    'Accept': 'application/json'
  }
});

  if (!response.ok) {
    return "Error to get Jira issue type list" + response.statusText;
  }
  const responseJson = await response.json();

responseJson.projects.map(project => {
  project.issuetypes.map(issuetype => {
    const requiredFields = Object.fromEntries(Object.entries(issuetype.fields).filter(field => {
      const [key, value] = field;
      return value.required == true && value.key != 'project' && value.key != 'issuetype';
    }));
    console.log(requiredFields);
    return requiredFields;
  })
});
};

const Config = () => {
  const [issueRequiredFields] = useState(fetchIssueRequiredFields(10000, 10000));
  console.log([issueRequiredFields]); // HERE return [ undefined ]

    return (
    <MacroConfig>
    </MacroConfig>
    );
};

export const config = render(<Config />);

Thx again

Hi @ThibautFAURE ,

At first glance, there are two things that pop out:

  1. Your fetchIssueRequiredFields is not returning anything, hence, the undefined. I’m guessing you want to return the result of responseJson.projects.map
  2. The function parameter of your responseJson.projects.map call does not return anything. You can either
    2.1 Remove the curly braces after project => , or
    2.2 If you want to retain the curly braces mentioned in 2.1, then return the results of project.issuetypes.map

Cheers,
Ian

1 Like

Hi @iragudo

After few days to try to reach my goal, I still need your help.
Thank you for your previous indications, I am now returning an object. I’m a beginner in development.
I’m having trouble to exploit the returned object. For example I am looking to display the value of attribute name

My code to get data from createmeta API and return a object named responseJsonFiltered :

// Get list of required fields to create an issue
const fetchIssueRequiredFields = async (projectId: string, issueType: string) => {

  let toUseProjectId = "10000";
  if (projectId && projectId.length > 0) {
    toUseProjectId = projectId
  }
  let toUseIssueType = "10000";
  if (issueType && issueType.length > 0) {
    toUseIssueType = issueType
  }
  const response = await api.asUser().requestJira(route`/rest/api/3/issue/createmeta?projectIds=${toUseProjectId}&issuetypeIds=${toUseIssueType}&expand=projects.issuetypes.fields`, {
  headers: {
    'Accept': 'application/json'
  }
});

  if (!response.ok) {
    return "Error to get Jira issue type list" + response.statusText;
  }
  const responseJson = await response.json();

  //responseJson contain too much data not useful. Only inside object projects=>issuetype=>fields is interesting
  const responseJsonFiltered = responseJson.projects[0].issuetypes.map(issuetype => {
      const requiredFields = Object.fromEntries(Object.entries(issuetype.fields).filter(field => {
          // I want only required fields
          const [key, value] = field;
          return value.required == true;
      }))
      // Perfect it's works
      Object.values(requiredFields).map(item => console.log(item.name));
      return Object.values(requiredFields);
  });

  // Doesn't work "[ [ undefined, undefined, undefined ] ]"
  console.log(responseJsonFiltered.map(firstChild => firstChild.map(item => console.log(item.name))));
  return responseJsonFiltered;
};

The first Object.values(requiredFields).map(item => console.log(item.name)); return in console my goal :partying_face:

INFO 10:31:42.141 0f630de12012200b Résumé
INFO 10:31:42.142 0f630de12012200b Type de ticket
INFO 10:31:42.142 0f630de12012200b Projet

but the second console.log(responseJsonFiltered.map(firstChild => firstChild.map(item => console.log(item.name)))); return

INFO 10:31:42.142 0f630de12012200b Résumé
INFO 10:31:42.142 0f630de12012200b Type de ticket
INFO 10:31:42.142 0f630de12012200b Projet
INFO 10:31:42.143 0f630de12012200b [ [ undefined, undefined, undefined ] ]

Why is there more [ [ undefined, undefined, undefined ] ] ? :fearful:

For information if i show console.log(responseJsonFiltered); the return is :

INFO 10:18:02.174 b23eeffd31d5ef85 [
[
{
required: true,
schema: [Object],
name: ‘Résumé’,
key: ‘summary’,
hasDefaultValue: false,
operations: [Array]
},
{
required: true,
schema: [Object],
name: ‘Type de ticket’,
key: ‘issuetype’,
hasDefaultValue: false,
operations: ,
allowedValues: [Array]
},
{
required: true,
schema: [Object],
name: ‘Projet’,
key: ‘project’,
hasDefaultValue: false,
operations: [Array],
allowedValues: [Array]
}
]
]

If you have ideas to simplify my code do not hesitate

Thx,
Thibaut

to solve my problem i added flat();

return responseJsonFiltered.flat();

@ThibautFAURE,

I know you solved this well over a year ago with code, but there is now a new endpoint for “issue types by project”:

Get create metadata issue types for a project: fetches all Issue Types for a specific Project; it will support pagination on the Issue Types, but not include expanded fields

This new endpoint was built so the old createmeta could be deprecated.