How to use Catalog Ingestion API to push data into Harness IDP
Introduction
In this tutorial we will be creating Jira tickets using Workflows and add the information (i.e. ticket numbers) to the corresponding Software Component in the Catalog and display the same in the UI using Additional Info Card. The aim of this tutorial is to help you understand the usage of REST APIs to push information in the Catalog and then use them in different parts of IDP according to your use-cases.
Pre-Requisite
- We assume you have JIRA set-up as your ticket management system, and you have access to the same to create tickets in your corresponding project as well as administrator access to create projects.
Create the Workflow
- Create JIRA Ticket
- Create JIRA Project
Create Jira ticket
- Using RUN Step
- Using HTTP Step
Using RUN Step
- Go to Admin in your IDP
- Now select the project where you you want to create the pipeline for the Workflows.
- Begin by selecting the Create a Pipeline button followed by adding a name for the pipeline and set up your pipeline as inline.
- Now select the Developer Portal Stage and give it a name.
- Add a RUN step, name it as create jira ticket and select the Shell as Bash
- Now add the following under the Command.
EMAIL_ID="<+pipeline.variables.email-id>"
JIRA_TOKEN="<+pipeline.variables.jiratoken>"
PROJECT_KEY="<+pipeline.variables.projectkey>"
COMPONENT_NAME="<+pipeline.variables.componentname>"
ISSUE_TYPE="<+pipeline.variables.issuetype>"
ISSUE_SUMMARY="<+pipeline.variables.issuesummary>"
ISSUE_CONTENT="<+pipeline.variables.issuecontent>"
LABELS="<+pipeline.variables.labels>"
# Perform the POST request with curl and capture the response
response=$(curl --silent --request POST \
  --url 'https://harness.atlassian.net/rest/api/3/issue' \
  --user "$EMAIL_ID:$JIRA_TOKEN" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
    "fields": {
        "project": {
            "key": "$PROJECT_KEY"
        },
        "components": {
            "name": "$COMPONENT_NAME"
        },
        "issuetype": {
            "name": "$ISSUE_TYPE"
        },
        "summary": "$ISSUE_SUMMARY",
        "description": {
            "version": 1,
            "type": "doc",
            "content": [
                {
                    "type": "paragraph",
                    "content": [
                        {
                            "type": "text",
                            "text": "$ISSUE_CONTENT"
                        }
                    ]
                }
            ]
        },
        "labels": [
            "$LABELS"
        ]
    }
}')
# Extract the key from the JSON response using jq
issue_key=$(echo "$response" | jq -r '.key')
# Export the issue key as an environment variable
export ISSUE_KEY="$issue_key"
# Print the issue key (optional)
echo "The created issue key is: $ISSUE_KEY"
The above given request body can create a Jira ticket based on project and component and add a label to the same.
We have used few pipeline variables in the body, which will be used to take input from the IDP Workflows and for the users to choose project, add the summary, description for the tickets.
- 
Now under Optional Configuration add the Output Variables as ISSUE_KEY.
- 
Apply the Changes. 
- 
Go to Variables on the right side of the page and add the following variables and have the input type as Runtime Input. - emailid
- jiratoken
- projectkey
- componentname
- issuetype
- issuesummary
- issuecontent
- labels
- usergroup
 
- 
Apply the changes. 

Use Catalog Metadata Ingestion API
- Start by adding another RUNstep.
- Name it as Ingestion API and select the Shell as Bash
- Now add the following under the Command.
curl --location 'https://app.harness.io/gateway/v1/catalog/custom-properties' \
--header 'Content-Type: application/json' \
--header 'x-api-key: <+secrets.getValue('account.TOKEN_ID')>' \
--data '{
    "properties": [
        {
            "field": "metadata.openTicket",
            "filter": {
                "kind": "Component",
                "type": "service",
                "owners": [
                    "<+pipeline.variables.usergroup>"
                ]
            },
            "value_overrides": [
                {
                    "entity_ref": "YOUR_COMPONENT_LINK",
                    "override_value": "<+stage.spec.execution.steps.create_jira_ticket.output.outputVariables.ISSUE_KEY>"
                }
            ],
            "value": "0"
        }
    ]
}'
- Under headerx-api-key:<+secrets.getValue('account.TOKEN_ID')>, add the token ID for your API key. Get your token ID from your Profile

In the above body the openTicket which got created in JIRA will be added, to kind component and type service owned by the usergroup selected in the Workflows. Under entity_ref add the component link to which you want to add the ticket ID, the unique entity reference could be found using inspect entity for the component in Catalog.


- Now Apply the changes.
Using HTTP Step
HTTP Step is available under custom stage and it's part of CD License
- Go to Admin in your IDP
- Now select the project where you you want to create the pipeline for the Workflows.
- Begin by selecting the Create a Pipeline button followed by adding a name for the pipeline and set up your pipeline as inline.
- Now select the Custom Stage and give it a name.
- Add a HTTP step and name it as create jira ticket
- Under the URL add this endpoint https://YOUR_COMPANY.atlassian.net/rest/api/3/issueand replace theYOUR_COMPANYwith your domain name.
- Select the Method as POST.
- Under the body add the following:
{
    "fields": {
        "project": {
            "key": "<+pipeline.variables.projectkey>"
        },
        "components": [
        {
            "name": "<+pipeline.variables.componentname>"
        }
        ],
        "issuetype": {
            "name": "<+pipeline.variables.issuetype>"
        },
        "summary": "<+pipeline.variables.issuesummary>",
        "description": {
            "version": 1,
            "type": "doc",
            "content": [
                {
                    "type": "paragraph",
                    "content": [
                        {
                            "type": "text",
                            "text": "<+pipeline.variables.issuecontent>"
                        }
                    ]
                }
            ]
        },
        "labels": [
            "<+pipeline.variables.labels>"
        ]
    }
}
The above given request body can create a Jira ticket based on project and component and add a label to the same.
We have used few pipeline variables in the body, which will be used to take input from the IDP Workflows and for the users to choose project, add the summary, description for the tickets.
- Under Optional Configuration add the Assertion as <+httpResponseCode>==201.
- Under Headers add the following key value pairs:
- Accept: application/json
- Authorization: Basic <Base64 encoded username and password>, the username is the email-id and the password would be the JIRA API key
- Accept-Encoding: gzip, deflate, br
 
- Accept: 
- Under output add a variable issue_keyand assign a value as expression<+json.object(httpResponseBody).key>
- Now Apply Changes.
- Go to Variables on the right side of the page and add the following variables and have the input type as Runtime Input.
- projectkey
- componentname
- issuetype
- issuesummary
- issuecontent
- labels
- usergroup
 
- Apply the changes.

Use Catalog Metadata Ingestion API
- Start by adding another HTTPstep.
- Add the Timeout as 30s.
- Add this endpoint as as URL https://app.harness.io/gateway/v1/catalog/custom-properties, read more about Catalog Metadata Ingestion API
- Select the Method as POST.
- And add the following json as Request Body
{
    "properties": [
        {
            "field": "metadata.openTicket",
            "filter": {
                "kind": "Component",
                "type": "service",
                "owners": [
                    "<+pipeline.variables.usergroup>"
                ]
            },
            "value_overrides": [
                {
                    "entity_ref": "YOUR_COMPONENT_LINK",
                    "override_value": "<+stage.spec.execution.steps.create_jira_ticket.output.outputVariables.issue_key>"
                }
            ],
            "value": "0"
        }
    ]
}
In the above body the openTicket which got created in JIRA will be added, to kind component and type service owned by the usergroup selected in the Workflows. Under entity_ref add the component link to which you want to add the ticket ID, the component link could be found using inspect entity for the component in catalog.


- 
Under Optional Configuration add the Assertion as <+httpResponseCode>==200.
- 
Under Headers add the following key value pairs: - Content-Type: application/json
- Accept: */*
- x-api-key: <+secrets.getValue('account.TOKEN_ID')>, add the token ID for your API key. Get your token ID from your Profile
  
- Content-Type: 
- 
Now Apply Changes and SAVE the pipeline. 
Create Workflow
Now we have to create a workflow, which takes the input from the user and triggers the pipeline. Here's the workflow YAML
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: jira-ticket
  title: Create a JIRA Ticket
  description: A template to create a Jira Ticket
  tags:
    - jira
    - management
spec:
  owner: owner@company.com
  type: service
  parameters:
    - title: Service Details
      required:
        - projectkey
        - issuetype
        - jiracomponentname
        - issuesummary
        - issuecontent
        - labels
        - usergroup
      properties:
        emailid:
          title: Email ID used for Atlassain account
          type: string
          description: Use the email ID associated with JIRA
        jiratoken:
          title: Jira Access Tokens
          type: string
          ui:widget: password
          description: Add the API access tokens created under the above mentioned email id 
        projectkey:
          title: Jira Project Key
          type: string
          default: IDP
          description: Your ticket will be created under this project
        issuetype:
          type: string
          title: Add your Jira component type
          default: Story
        jiracomponentname:
          type: string
          title: Add the component for this issue
          desciption: Component for ticket tracing eg., Misc, Backstage Core, Platform etc. 
        issuesummary:
          title: Give a title for your ticket
          type: string
        issuecontent:
          title: Describe the details needed in the ticket
          type: string
        labels:
          type: string
          title: Select a label for the issue
          default: roadmap     
        usergroup:
          title: Choose an Owner for the Service
          type: string
          ui:field: OwnerPicker
          ui:options:
            allowedKinds:
              - Group
        # This field is hidden but needed to authenticate the request to trigger the pipeline
        token:
          title: Harness Token
          type: string
          ui:widget: password
          ui:field: HarnessAuthToken
  steps:
    - id: trigger
      name: Bootstrapping your new service
      action: trigger:harness-custom-pipeline
      input:
        url: "PIPELINE URL"
        inputset:
          emailid: ${{ parameters.emailid }}
          jiratoken: ${{ parameters.jiratoken }}
          projectkey: ${{ parameters.projectkey }}
          issuetype: ${{ parameters.issuetype }}
          componentname: ${{ parameters.jiracomponentname }}
          issuesummary: ${{ parameters.issuesummary }}
          issuecontent: ${{ parameters.issuecontent }}
          labels: ${{ parameters.labels }}
          usergroup: ${{ parameters.usergroup }}
        apikey: ${{ parameters.token }}
  output:
    links:
      - title: Pipeline Details
        url: ${{ steps.trigger.output.PipelineUrl }}
In the above YAML just replace the url with the pipeline URL we created above, also make sure the key values under inputset exactly matches with the pipeline variable names.
Also for Jira token input the input should be the personal access token from JIRA.
- 
Now go to your git provider and add this workflow yaml and save it, make sure it's public incase it's in private repo make sure you have the git integration setup. 
- 
Once the file is created in your git repo, copy the full URL to the file. For example, https://github.com/harness-community/idp-samples/blob/main/tutorial-jira-ticket-catalog-ingestion.yaml.

- In the left navigation, select Create, and then select Register Software Component.

- Enter the URL to your new workflow.yaml.

- Click Import.

- Now go to the workflow and select the workflow you just added.

- Add the input values in the field that would be used by the workflow

Create Additional Info Card
In case you want to display the same information you have ingested on your Overview page as an additional card, follow the steps below.
- Go to the Layout Page and under Admin and add the following for Services and Save it.
- component: EntityAdditionalInfoCard
    specs:
    props:
        title: Open JIRA Tickets
        items:
        - label: JIRA TICKET KEY
            value: <+metadata.openTicket>
            type: string
            style:
            bold: true
    gridProps:
        md: 6

- Now go to the Software Component in the Catalog and you'll find an additional info card populated with information we ingested using the API above. You can read more about additional info card
Create Jira Project
- Using RUN Step
- Using HTTP Step
Using RUN Step
- Go to Admin in your IDP
- Now select the project where you you want to create the pipeline for the Workflows.
- Begin by selecting the Create a Pipeline button followed by adding a name for the pipeline and set up your pipeline as inline.
- Now select the Developer Portal Stage and give it a name.
- Add a RUN step, name it as create jira project and select the Shell as Bash
- Now add the following under the Command.
EMAIL_ID="<+pipeline.variables.emailid>"
JIRA_TOKEN="<+pipeline.variables.jiratoken>"
NEW_PROJECT_NAME="<+pipeline.variables.newprojectname>"
DESCRIPTION="<+pipeline.variables.description>"
NEW_PROJECT_KEY="<+pipeline.variables.newprojectkey>"
PROJECT_LEAD_ACCOUNT_ID="<+pipeline.variables.projectleadaccountid>"
PROJECT_TEMPLATE_KEY="<+pipeline.variables.projecttemplatekey>"
# Perform the POST request with curl and capture the response
response=$(curl --silent --request POST \
  --url 'https://YOUR_COMPANY.atlassian.net/rest/simplified/latest/project' \
  --user "$EMAIL_ID:$JIRA_TOKEN" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
    "assigneeType": "PROJECT_LEAD",
    "description": "$DESCRIPTION",
    "key": "$NEW_PROJECT_KEY",
    "leadAccountId": "$PROJECT_LEAD_ACCOUNT_ID",
    "name": "$NEW_PROJECT_NAME",
    "projectTemplateKey": "$PROJECT_TEMPLATE_KEY",
}')
# Extract the key from the JSON response using jq
project_key=$(echo "$response" | jq -r '.key')
# Export the issue key as an environment variable
export PROJECT_KEY="$project_key"
# Print the issue key (optional)
echo "The created issue key is: $PROJECT_KEY"
The above given request body can create a Jira project
We have used few pipeline variables in the body, which will be used to take input from the IDP Workflows and for the users to choose project, add the summary, description for the tickets.
- 
Now under Optional Configuration add the Output Variables as PROJECT_KEY.
- 
Apply the Changes. 
- 
Go to Variables on the right side of the page and add the following variables and have the input type as Runtime Input. - emailid
- jiratoken
- newprojectname
- newprojectkey
- projectleadaccountid
- projecttemplatekey
- usergroup
 
- 
Apply the changes. 

Use Catalog Metadata Ingestion API
- Start by adding another RUNstep.
- Name it as Ingestion API and select the Shell as Bash
- Now add the following under the Command.
curl --location 'https://app.harness.io/gateway/v1/catalog/custom-properties' \
--header 'Content-Type: application/json' \
--header 'x-api-key: <+secrets.getValue('account.TOKEN_ID')>' \
--data '{
    "properties": [
        {
            "field": "metadata.openTicket",
            "filter": {
                "kind": "Component",
                "type": "service",
                "owners": [
                    "<+pipeline.variables.usergroup>"
                ]
            },
            "value_overrides": [
                {
                    "entity_ref": "YOUR_COMPONENT_LINK",
                    "override_value": "<+stage.spec.execution.steps.create_jira_project.output.outputVariables.PROJECT_KEY>"
                }
            ],
            "value": "0"
        }
    ]
}'
- Under headerx-api-key:<+secrets.getValue('account.TOKEN_ID')>, add the token ID for your API key. Get your token ID from your Profile

In the above body the openTicket which got created in JIRA will be added, to kind component and type service owned by the usergroup selected in the Workflows. Under entity_ref add the component link to which you want to add the ticket ID, the unique entity reference could be found using inspect entity for the component in Catalog.


- Now Apply the changes.
- Go to Admin in your IDP
- Now select the project where you you want to create the pipeline for the Workflows.
- Begin by selecting the Create a Pipeline button followed by adding a name for the pipeline and set up your pipeline as inline.
- Now select the Custom Stage and give it a name.
- Add a HTTP step and name it as create jira project
- Under the URL add this endpoint https://YOUR_COMPANY.atlassian.net/rest/simplified/latest/projectand replace theYOUR_COMPANYwith your domain name.
- Select the Method as POST.
- Under the body add the following:
{
    "key": "<+pipeline.variables.newprojectkey>",
    "leadAccountId": "<+pipeline.variables.projectleadaccountid>",
    "templateKey": "<+pipeline.variables.projecttemplatekey>",
    "name": "<+pipeline.variables.newprojectname>"
}
The above given request body can create a Jira Project based on templatekey
We have used few pipeline variables in the body, which will be used to take input from the IDP Workflows and for the users to provide the name, key and template.
- 
Under Optional Configuration add the Assertion as <+httpResponseCode>==200.
- 
Under Headers add the following key value pairs: - Accept: application/json
- Authorization: Basic <Base64 encoded username and password>, the username is the email-id and the password would be the JIRA API key
- Accept-Encoding: gzip, deflate, br
 
- Accept: 
- 
Under output add few variable like project_key, assign a value as expression<+json.object(httpResponseBody).projectKey>andproject_name, assign the values as expression<+json.object(httpResponseBody).projectName>
- 
Now Apply Changes. 
- 
Go to Variables on the right side of the page and add the following variables and have the input type as Runtime Input. - projectkey
- templatekey
- projectname
- usergroup
 
- 
Apply the changes. 
Use Catalog Metadata Ingestion API
- Start by adding another HTTPstep.
- Add the Timeout as 30s.
- Add this endpoint as as URL https://app.harness.io/gateway/v1/catalog/custom-properties, read more about Catalog Metadata Ingestion API
- Select the Method as POST.
- And add the following json as Request Body
{
    "properties": [
        {
            "field": "metadata.projectKey",
            "filter": {
                "kind": "Component",
                "type": "service",
                "owners": [
                    "<+pipeline.variables.usergroup>"
                ]
            },
            "value_overrides": [
                {
                    "entity_ref": "YOUR_COMPONENT_LINK",
                    "override_value": "<+stage.spec.execution.steps.create_jira_project.output.outputVariables.project_key>"
                }
            ],
            "value": "0"
        }
    ]
}
In the above body the openTicket which got created in JIRA will be added, to kind component and type service owned by the usergroup selected in the Workflows. Under entity_ref add the component link to which you want to add the ticket ID, the component link could be found using inspect entity for the component in Catalog.


- 
Under Optional Configuration add the Assertion as <+httpResponseCode>==200.
- 
Under Headers add the following key value pairs: - Content-Type: application/json
- Accept: */*
- x-api-key: <+secrets.getValue('account.TOKEN_ID')>, add the token ID for your API key. Get your token ID from your Profile
  
- Content-Type: 
- 
Now Apply Changes and SAVE the pipeline. 
Create Workflow
Now we have to create a workflow, which takes the input from the user and triggers the pipeline. Here's the workflow YAML
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: jira-project
  title: Create a JIRA Project
  description: A template to create a Jira Project
  tags:
    - jira
    - management
spec:
  owner: owner@company.com
  type: service
  parameters:
    - title: Service Details
      required:
        - emailid
        - jiratoken
        - newprojectname
        - newprojectkey
        - projectleadaccountid
        - projecttemplatekey
        - usergroup
      properties:
        newprojectkey:
          title: Jira Project Key
          type: string
          default: DEMO
          description: Your Project will have this key which will appear in the url
        newprojectname:
          type: string
          title: Add a project name
          default: demo
        projecttemplatekey:
          type: string
          title: Add the template type
          description: Select and add a template  
        jiratoken:
          type: string
          title: Add the Jira Token
          ui:widget: password
        projectleadaccountid:
          type: string
          title: Add the account id for your project lead 
          description: Atlassian Account ID  for the project lead   
        emailid:
          type: string
          tile: Add the Atlassian emailid
          description: emailid to be used for atlassian login     
        usergroup:
          title: Choose an Owner for the Service
          type: string
          ui:field: OwnerPicker
          ui:options:
            allowedKinds:
              - Group
        # This field is hidden but needed to authenticate the request to trigger the pipeline
        token:
          title: Harness Token
          type: string
          ui:widget: password
          ui:field: HarnessAuthToken
  steps:
    - id: trigger
      name: Bootstrapping your new service
      action: trigger:harness-custom-pipeline
      input:
        url: "PIPELINE URL"
        inputset:
          newprojectkey: ${{ parameters.newprojectkey }}
          newprojectname: ${{ parameters.newprojectname }}
          projecttemplatekey: ${{ parameters.projecttemplatekey }}
          jiratoken: ${{ parameters.jiratoken }} 
          usergroup: ${{ parameters.usergroup }}
          projectleadaccountid: ${{ parameters.projectleadaccountid }}
          emailid: ${{ parameters.emailid }}
        apikey: ${{ parameters.token }}
  output:
    links:
      - title: Pipeline Details
        url: ${{ steps.trigger.output.PipelineUrl }}
In the above YAML just replace the url with the pipeline URL we created above, also make sure the key values under inputset exactly matches with the pipeline variable names.
- 
Now go to your git provider and add this workflow yaml and save it, make sure it's public incase it's in private repo make sure you have the git integration setup. 
- 
Once the file is created in your git repo, copy the full URL to the file. For example, https://github.com/harness-community/idp-samples/blob/main/tutorial-jira-ticket-catalog-ingestion.yaml.

- In the left navigation, select Create, and then select Register Software Component.

- Enter the URL to your new workflow.yaml.

- Click Import.

- 
Now go to the workflow and select the workflow you just added.  
- 
Add the values, under template key, make sure you add one of the mentioned template key, for eg., here we usedcom.pyxis.greenhopper.jira:gh-simplified-agility-kanban.

- Create Additional Info Card
- Add UI Picker to dynamically fetch data in workflows
Create Additional Info Card
In case you want to display the same information you have ingested on your Overview page as an additional card, follow the steps below.
- Go to the Layout Page and under Admin and add the following for Services and Save it.
- component: EntityAdditionalInfoCard
    specs:
    props:
        title: JIRA PROJECT
        items:
        - label: JIRA PROJECT KEY
            value: <+metadata.projectKey>
            type: string
            style:
            bold: true
    gridProps:
        md: 6

- Now go to the Software Component in the Catalog and you'll find an additional info card populated with information we ingested using the API above. You can read more about additional info card
Create UI Picker to dynamically values in Workflows
In case you need to use the data present in your Catalog as an input for the Workflows, you can do so by using the EntityFieldPicker.
Here's an example workflow template that uses the  jira projectName for the corresponding new service being created.
## Example workflow.yaml
...
projectName:
    title: Project-Name
    type: string
    description: Name of the project in JIRA
    ui:field: EntityFieldPicker
    ui:displayField: metadata.projectName
    ui:options:
    catalogFilter:
        kind: 
        - Component
        - Service
...
The ui:displayField fetches all the projectName names from the Catalog.
### Complete Example of a service onboarding workflow.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
# some metadata about the template itself
metadata:
  name: react-app
  title: Create a new service
  description: A workflow to create a new service
  tags:
    - nextjs
    - react
    - javascript
# these are the steps which are rendered in the frontend with the form input
spec:
  owner: debabrata.panigrahi@harness.io
  type: service
  parameters:
    - title: Service Details
      required:
        - template_type
        - public_template_url
        - owner
      properties:
        template_type:
          title: Type of the Template
          type: string
          description: Type of the Template
        public_template_url:
          title: Give a Public template URL
          type: string
          description: Give a Public Cookiecutter Template  
        projectName:
            title: Project-Name
            type: string
            description: Name of the project in JIRA
            ui:field: EntityFieldPicker
            ui:displayField: metadata.projectName
            ui:options:
            catalogFilter:
                kind: 
                - Component
                - Service
        # This field is hidden but needed to authenticate the request to trigger the pipeline
        token:
          title: Harness Token
          type: string
          ui:widget: password
          ui:field: HarnessAuthToken
  # here's the steps that are executed in series in the scaffolder backend
  steps:
    - id: trigger
      name: Creating your react app
      action: trigger:harness-custom-pipeline
      input:
        url: "Your PIPELINE URL"
        inputset:
          project_name: ${{ parameters.projectName }}
          template_type: ${{ parameters.template_type }}
          public_template_url: ${{ parameters.public_template_url }}
        apikey: ${{ parameters.token }}
  # some outputs which are saved along with the job for use in the frontend
  output:
    links:
      - title: Pipeline Details
        url: ${{ steps.trigger.output.PipelineUrl }}