Skip to content

Conversation

prarit
Copy link

@prarit prarit commented Apr 17, 2025

What type of PR is this?

bug

Optionally add one or more of the following kinds if applicable:

api-change

What this PR does / why we need it:

@dustin-decker reported that the current search APIs will be deprecated on May 1 2025 [1].

Switch to the new search API.

[1] https://developer.atlassian.com/changelog/#CHANGE-2046

Which issue(s) this PR fixes:

Fixes #715

Special notes for your reviewer:

This is untested. I want to see if this passes CI.

Additional documentation e.g., usage docs, etc.:

@github-actions github-actions bot added the jira-cloud Changes related to the Jira Cloud client label Apr 17, 2025
@ns-mglaske
Copy link

ns-mglaske commented Apr 23, 2025

fwiw, SearchPages function at line 1166 is missing a return nil in it..

@ns-mglaske
Copy link

Also, I think searchResult needs a nextPageToken field, in case you want to use Search and iterate it.

@dustin-decker reported that the current search APIs will be deprecated on
May 1 2025 [1].

Switch to the new search API.

[1] https://developer.atlassian.com/changelog/#CHANGE-2046

v2: suggested fixed from @ns-mglaske
Signed-off-by: Prarit Bhargava <[email protected]>
@prarit
Copy link
Author

prarit commented Apr 23, 2025

fwiw, SearchPages function at line 1166 is missing a return nil in it..

Fixed in new push

Also, I think searchResult needs a nextPageToken field, in case you want to use Search and iterate it.

Fixed in new push

And thanks :)

@ns-mglaske
Copy link

Also, I think Response in jira.go needs the NextPageToken, along with updates to populatePageValues..

@ns-mglaske
Copy link

Also.. It seems that a number of the fields that jira returns are no longer strings (eg, Description), but Jira's ADF format which really sucks :). I wrote a adf_translator such that you can replace the IssueFields, eg, Description with Description . You could also take it a step further, and just do type ADFText string for a more direct replacement, but I wanted to know which fields broke in my upstream code.

package jira

import (
        "encoding/json"
        "fmt"
        "strings"
)

// ADFText represents an Atlassian Document Format field like "description",
// and extracts just the plain text content.
type ADFText struct {
        Text string
}

// UnmarshalJSON extracts plain text from the ADF document structure.
func (a *ADFText) UnmarshalJSON(data []byte) error {
        // Define a partial struct to match ADF layout
        var doc struct {
                Content []struct {
                        Content []struct {
                                Text string `json:"text"`
                        } `json:"content"`
                } `json:"content"`
        }

        // Unmarshal into ADF format
        if err := json.Unmarshal(data, &doc); err != nil {
                // Fallback: maybe it's just a plain string?
                var s string
                if err2 := json.Unmarshal(data, &s); err2 == nil {
                        a.Text = s
                        return nil
                }

                return fmt.Errorf("ADFText unmarshal failed: %w", err)
        }

        // Build plain text from nested content
        var sb strings.Builder
        for _, block := range doc.Content {
                for _, inline := range block.Content {
                        sb.WriteString(inline.Text)
                }
                sb.WriteRune('\n')
        }

        a.Text = strings.TrimSpace(sb.String())
        return nil
}

// MarshalJSON outputs the plain text as a JSON string (not as ADF)
func (a ADFText) MarshalJSON() ([]byte, error) {
        return json.Marshal(a.Text)
}

// ADFDocument represents the minimal ADF structure Jira accepts
type ADFDocument struct {
        Type    string       `json:"type"`    // always "doc"
        Version int          `json:"version"` // always 1
        Content []ADFContent `json:"content"`
}

func (a *ADFDocument) JSON() string {
        b, _ := json.Marshal(a)
        return string(b)
}

type ADFContent struct {
        Type    string        `json:"type"` // usually "paragraph"
        Content []ADFTextNode `json:"content"`
}

type ADFTextNode struct {
        Type string `json:"type"` // always "text"
        Text string `json:"text"`
}

// takes a plain string and converts it into an ADF JSON payload
func TextToADF(text string) *ADFDocument {
        // Split into lines → paragraphs
        paragraphs := strings.Split(text, "\n")
        var content []ADFContent

        for _, para := range paragraphs {
                para = strings.TrimSpace(para)
                if para == "" {
                        continue
                }

                content = append(content, ADFContent{
                        Type: "paragraph",
                        Content: []ADFTextNode{
                                {
                                        Type: "text",
                                        Text: para,
                                },
                        },
                })
        }

        return &ADFDocument{
                Type:    "doc",
                Version: 1,
                Content: content,
        }
}

func TextToADFJSON(text string) string {
        doc := TextToADF(text)
        b, _ := json.Marshal(doc)
        return string(b)
}

@Jcparkyn
Copy link

@ns-mglaske I think you can avoid some of the effort of supporting ADF by sticking with the v2 version of the search endpoint.

The important part for the deprecation notice is switching from /rest/api/2|3|latest/search to /rest/api/2|3|latest/search/jql, but the /jql versions of the v2 endpoints (which don't use ADF) are still fully supported. IMO for this PR it would be better to prioritise that switch, and leave the migration to v3 as a separate change since it's less urgent and harder to implement.

See https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/#version

func (s *IssueService) Search(ctx context.Context, jql string, options *SearchOptions) ([]Issue, *Response, error) {
u := url.URL{
Path: "rest/api/2/search",
Path: "rest/api/3/search/jql",
Copy link

@Jcparkyn Jcparkyn Apr 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in PR discussion, it might be better to stick with v2 for now to avoid the breaking changes from strings to ADF. V2 is still supported (not deprecated), provided that you use the /jql version which uses nextPageToken.

See https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/#version

@andygrunwald
Copy link
Owner

Hey all,
please see my latest comment in #715 (comment) with an update on this one here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
jira-cloud Changes related to the Jira Cloud client
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Some APIs used will be shutdown on May 1st 2025
4 participants