Is it possible to query Assets using requestJira?

I’m building a custom UI forge app that runs when you open the new request form in JSM portal. I’m simply trying to pull in data from the assets module into a Select box, but am getting strange errors. I just want to verify that this is possible. I’ve seen some other sample code (asset import sample code) where assets are being queried by requestJira, but want to verify that this should work. The error I see in the browser console is: fetch.js:14 Refused to connect to https://jira/jsm/assets/workspace/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/v1/object/aql?startAt=0&maxResults=100&includeAttributes=false because it violates the following Content Security
Here is my api call:

    const fetchAssetsFromJiraAssetLocation = async () => {

        const requestBody = {
            qlQuery: 'ObjectType = "Office Locations"',
        };


        const response = await api.asUser().requestJira(route`/jsm/assets/workspace/xxxxxxxxxxxxxxxx/v1/object/aql?startAt=0&maxResults=100&includeAttributes=false`,
            {
                method: "POST",
                headers: {
                    'Content-Type': 'application/json',  // This is crucial for POST requests with a JSON body
                },
                body: JSON.stringify(requestBody),
            }
        );


        const rawText = await response.text();
        setRawResponse(rawText);

        console.log(await response.text());
        if (!response.ok) {
            const content = await response.text();
            console.log(`Failed to fetch assets from Jira: ${content}`);
            throw new Error(`Failed to fetch assets from Jira: ${content}`);
        }

        return await response.json();
    };
1 Like

@EricKruegerStrataCom,

It looks like you’re pretty close. For documentation about calling the Assets API from Forge, see:
https://developer.atlassian.com/platform/forge/assets-import-app/#import-data-into-assets-via-imports-rest-api

Specifically, you’ll want to pull out the workspaceId path parameter (where you have all the x’s). The docs are focused on import scenarios not a query, but I think you can follow the gist:

  const asUserRequest = await api
        .asUser()
        .requestJira(
                route`/jsm/assets/workspace/${context.workspaceId}/v1/importsource/${context.importId}/executions`,
                {
                   method: "POST",
                }
        );

Thanks. I replaced my workspace ID with the Xxxxx (I had hard-coded it in). It seems like it’s a security issue…can I query Assets from the JSM Portal?

@EricKruegerStrataCom,

Yes. As long as the security context would allow. Some users (or even apps) may be denied permission to Assets.

Here is the full error…fetch.js:14 Refused to connect to ‘https://jira/jsm/assets/workspace/cc904c8d-18bc-4462-b316-6667646c4b75/v1/object/aql?startAt=0&maxResults=100&includeAttributes=false’ because it violates the following Content Security Policy directive: "connect-src ‘self’

Do I need to add a security parameter to the yml file? I found another post where a user was having a similar error and there was no resolution to that.

Why is the url resolving to https://jira instead of api.atlassian.com?

@EricKruegerStrataCom,

Perhaps. The Forge example I linked uses a scope (import:import-configuration:cmdb) that might not be sufficient for this endpoint. And I am no noticing that our docs don’t enumerate the scopes for Assets. I’ve captured the problem here; please watch, vote, and comment. That said, I don’t think wrong scopes cause a CSP error. I think something else is going on here.

Can I confirm how your app is trying to make the call? Are you following the pattern established in the Forge Jira tutorial? Specifically, what kind of UI code is calling your fetchAssetsFromJiraAssetLocation function?

Do you have a list of asset scopes? You are right about that list not being documented. :slight_smile: I’m trying to populate a drop-down list in a custom UI app. below is my code. This is a playground app, so there are a few other things in there, but it should be calling from the select widget.

import React, { useEffect, useState } from 'react';
//import { requestJira } from '@forge/bridge';
import { Select, Option } from '@forge/ui'; // Consolidate imports from @forge/ui
import api, { route } from "@forge/api";

function App() {

    const [formData, setFormData] = useState({
        username: '',
        description: '',
        issueType: 'bug'
    });

    const [rawResponse, setRawResponse] = useState("");

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log(formData);
        // You can send this data to the Forge backend function or any other API.
    };

    const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData(prevState => ({ ...prevState, [name]: value }));
    };

    const [toggleValue, setToggleValue] = useState("Hide");
    const [toggleValue2, setToggleValue2] = useState("Hide");

    const currentURL = window.location.href;
    const [assets, setAssets] = useState([]);
    useEffect(() => {

        const fetchData = async () => {
            const data = await fetchAssetsFromJiraAssetLocation();
            setAssets(data.values); // Adjust based on the actual response structure
        };

        fetchData();
    }, []);

    const fetchAssetsFromJiraAssetLocation = async () => {

        const requestBody = {
            qlQuery: 'ObjectType = "Office Locations"',
        };


        const response = await api.asApp().requestJira(route`/jsm/assets/workspace/cc904c8d-18bc-4462-b316-6667646c4b75/v1/object/aql?startAt=0&maxResults=100&includeAttributes=false`,
            {
                method: "POST",
                headers: {
                    'Content-Type': 'application/json',  // This is crucial for POST requests with a JSON body
                },
                body: JSON.stringify(requestBody),
            }
        );

        setRawResponse("test text")    
        const rawText = await response.text();
        setRawResponse(rawText);

        console.log(await response.text());
        if (!response.ok) {
            const content = await response.text();
            console.log(`Failed to fetch assets from Jira: ${content}`);
            throw new Error(`Failed to fetch assets from Jira: ${content}`);
        }

        return await response.json();
    };
// end assets drop-down code

    return (
        <div>
            <h1>Fill out the form below:</h1>
            <form onSubmit={handleSubmit}>
                <div>
                    <p><label>Raw API Response:</label></p>
                    <p><textarea value={rawResponse} readOnly style={{ width: "100%", height: "200px" }} /></p>
                    <p>Current URL should be below...</p>
                    <p>Current URL: {currentURL}</p>
                    <p><label>
                        Choose an asset:
                        <Select >
                            {assets.map(asset => (
                                <Option label={asset.label} value={asset.id} key={asset.id} />
                                // Adjust 'asset.name' and 'asset.id' based on the actual response structure
                            ))}
                        </Select>
                    </label>
                    </p>
                    <label htmlFor="toggleField">Toggle Field: </label>
                    <select
                        id="toggleField"
                        value={toggleValue}
                        onChange={(e) => setToggleValue(e.target.value)}
                    >
                        <option value="Show">Show</option>
                        <option value="Hide">Hide</option>
                    </select>
                    <label htmlFor="toggleField2">Toggle Field 2: </label>
                    <select
                        id="toggleField2"
                        value={toggleValue2}
                        onChange={(e) => setToggleValue2(e.target.value)}
                    >
                        <option value="Show">Show</option>
                        <option value="Hide">Hide</option>
                    </select>

                    {toggleValue === "Show" && toggleValue2 === "Show" && (
                        <div>
                            <label htmlFor="specialField">Special Field: </label>
                            <input type="text" id="specialField" />
                        </div>
                    )}
                </div>

                <div>
                    <label>Username:</label>
                    <input type="text" name="username" value={formData.username} onChange={handleChange} />
                </div>

                <div>
                    <label>Description:</label>
                    <textarea name="description" value={formData.description} onChange={handleChange} />
                </div>

                <div>
                    <label>Issue Type:</label>
                    <select name="issueType" value={formData.issueType} onChange={handleChange}>
                        <option value="bug">Bug</option>
                        <option value="feature">Feature Request</option>
                        <option value="question">Question</option>
                    </select>
                </div>

                <button type="submit">Submit</button>
            </form>
        </div>
    );
}

export default App;

I simplified everything and put the call into a forge UI Kit app and now I see this error:

{ code: 401, message: ‘Unauthorized; scope does not match’

Which scope do I need to query Assets?

@EricKruegerStrataCom,

A new problem is progress. :wink:

Custom UI and UI Kit render differently. The @forge/api is not available in the Custom UI context but it is for UI Kit. (Same problem as this other thread.) Glad you could switch to UI Kit.

Meanwhile, for our new problem, that’s definitely a scope problem and is beyond my ken. I’ve pinged the PM on the issue, but it might be worth opening up a developer support ticket to see if you can get a more direct answer.

1 Like

@EricKruegerStrataCom,

I think I have a path forward! I was looking at the wrong API endpoint. The one I was reading (/aql/object) has been deprecated; hence, no OAuth scope. The one you are using isn’t deprecated and does have an OAuth/Forge scope: read:cmdb-object:jira. It’s right there in the docs as the “OAuth 2.0 scopes required”, which is the same as for Forge. Sorry for overlooking that earlier.

1 Like

This scope isn’t valid for a Forge UI Kit Issue panel app. Any other suggestions to get this working?