go-jira icon indicating copy to clipboard operation
go-jira copied to clipboard

Some APIs used will be shutdown on May 1st 2025

Open dustin-decker opened this issue 7 months ago • 1 comments

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

It looks like v1 and v2 both use deprecated search APIs that will be shutdown on May 1st. There is a partial replacement available.

dustin-decker avatar Apr 11 '25 18:04 dustin-decker

Does anyone know when this might get merged?

ns-mglaske avatar Apr 23 '25 17:04 ns-mglaske

those old JIRA search API just closed. 😭

kindy avatar Aug 18 '25 08:08 kindy

Also got hit by this 😢

nunofernandes avatar Aug 19 '25 15:08 nunofernandes

Facing the same issue.

IvanKuzyshyn avatar Aug 25 '25 10:08 IvanKuzyshyn

Facing this as well, any quick fix for this ?

yarinm avatar Aug 25 '25 11:08 yarinm

I will look into https://github.com/andygrunwald/go-jira/pull/717 today or in the next days to get it into main.

andygrunwald avatar Aug 25 '25 11:08 andygrunwald

If anyone needs some temporary workaround you can try doing something like this using NewRequestWithContext from jira client:


	// Use the new Jira API v3 search endpoint
	searchURL := "/rest/api/3/search/jql"
	queryParams := url.Values{}
	queryParams.Set("jql", query)
	queryParams.Set("maxResults", "50")
	queryParams.Set("fields", "key,summary")

	req, err := c.client.NewRequestWithContext(ctx, "GET", searchURL+"?"+queryParams.Encode(), nil)
	if err != nil {
		return nil, errorlib.Wrap(err, "failed to create jira search request")
	}

	var searchResult struct {
		Issues []struct {
			Key    string `json:"key"`
			Fields struct {
				Summary string `json:"summary"`
			} `json:"fields"`
		} `json:"issues"`
	}

	_, err = c.client.Do(req, &searchResult)
	if err != nil {
		return nil, errorlib.Wrap(err, "failed to search jira issues")
	}

yarinm avatar Aug 25 '25 11:08 yarinm

I was able to patch it locally using a similar approach from this PR:

func (c *Client) SearchV3(ctx context.Context, jql string) ([]j.Issue, *j.Response, error) {
	u := url.URL{
		Path: "rest/api/3/search/jql",
	}
	uv := url.Values{}
	if jql != "" {
		uv.Add("jql", jql)
	}

	u.RawQuery = uv.Encode()

	req, err := c.client.NewRequest(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return []j.Issue{}, nil, err
	}

	v := new(searchResult)
	resp, err := c.client.Do(req, v)
	if err != nil {
		err = j.NewJiraError(resp, err)
	}
	return v.Issues, resp, err
}

IvanKuzyshyn avatar Aug 25 '25 11:08 IvanKuzyshyn

Hey any news? The API still doesn't work 🙏

ofirdagan avatar Aug 27 '25 11:08 ofirdagan

@andygrunwald Is this still in-progress? Just wondering if we should be looking at a workaround internally.

rdubya16 avatar Aug 27 '25 13:08 rdubya16

@andygrunwald any update?

dan-ih avatar Aug 27 '25 15:08 dan-ih

Hey all, I am working on this right now. https://github.com/andygrunwald/go-jira/pull/717 itself is not mergeable as is, as it defines types like integer (which do not exist).

I am playing around with my Jira Cloud instance at https://go-jira-opensource.atlassian.net/. In this setup, the v2 API still works as before - See https://go-jira-opensource.atlassian.net/rest/api/2/search?jql=type+%3D+Bug+and+Status+NOT+IN+%28Resolved%29

Can you provide me the information on what setup you are running?

On another note: I am making the change in the current main branch. I keep you posted as I progress.

andygrunwald avatar Aug 27 '25 18:08 andygrunwald

@andygrunwald here is a snippet that produces the error for us using 1.16 of the lib.

  username := "testuser"
  jql := fmt.Sprintf(`project in ("Example") AND
  statusCategory = "In Progress" AND
  (assignee = "%[email protected]" OR reviewer = "%[email protected]" OR "Prod Confirmation Owner" = "%[email protected]")`, username, username, username)

  issues, _, err := client.Issue.Search(jql, nil)
  if err != nil {
    log.Printf("Error Retrieving Issues: %v", err)
  }
2025/08/25 15:39:52 Error Retrieving Issues: The requested API has been removed. Please migrate to the /rest/api/3/search/jql API. A full migration guideline is available at https://developer.atlassian.com/changelog/#CHANGE-2046: request failed. Please analyze the request body for more details. Status code: 410

rdubya16 avatar Aug 27 '25 18:08 rdubya16

Hey all, I put out a new PR, please see https://github.com/andygrunwald/go-jira/pull/724

I only patched this in main, not in https://github.com/andygrunwald/go-jira/releases/tag/v1.16.0

What I would need from you

  • Please test this branch, and if the search functionality works as expected
  • Please provide back the feedback

You should be able to use the new branch by go get github.com/andygrunwald/go-jira@fix-search-jql-api-deprecation

See https://stackoverflow.com/questions/53682247/how-to-point-go-module-dependency-in-go-mod-to-a-latest-commit-in-a-repo

andygrunwald avatar Aug 27 '25 19:08 andygrunwald

@andygrunwald - hey, is this a fix for v1 or v2? when trying to install it I get

go: github.com/andygrunwald/go-jira@fix-search-jql-api-deprecation: github.com/andygrunwald/[email protected]: invalid version: go.mod has post-v1 module path "github.com/andygrunwald/go-jira/v2" at revision a1568d030dcc

ofirdagan avatar Aug 28 '25 09:08 ofirdagan

@ofirdagan The fix is only in v2 / based on the current main. We are not able to fix this in v1, as v1 doesn't have the split between the on-premises and Cloud versions. This breaking change is only in the Cloud version, but not in the On-Premise one. If we were to modify the existing Search functionality, it would break on-premises.

One possibility would be to add a new Search function like SearchV3JQL or anything like this. Happy to accept a PR on this against version 1, then I would ship the new version. The code should be nearly the same as in https://github.com/andygrunwald/go-jira/pull/724

@All: Did anyone test it already?

andygrunwald avatar Aug 28 '25 18:08 andygrunwald

I wasn't able to test for the same reason as mentioned above wrt to getting the v2 version to install/resolve correctly.

However, I'm happy to try to get a PR together to port the changes to v1

conor-naranjo avatar Aug 28 '25 18:08 conor-naranjo

We are running into the same issue as above with v1

rdubya16 avatar Aug 28 '25 18:08 rdubya16

@conor-naranjo This would be great. Lets ensure to have a new function to not break on-premise.

When i find some time, i will see how I can document the move to v2.

andygrunwald avatar Aug 28 '25 19:08 andygrunwald

@andygrunwald should I branch off v1.16.1-dev for these changes?

conor-naranjo avatar Aug 28 '25 19:08 conor-naranjo

@conor-naranjo Yes please. I created https://github.com/andygrunwald/go-jira/tree/v1.16.1-dev for this purpose.

andygrunwald avatar Aug 28 '25 19:08 andygrunwald

@andygrunwald https://github.com/andygrunwald/go-jira/pull/725 please review at your convenience

conor-naranjo avatar Aug 28 '25 19:08 conor-naranjo

@conor-naranjo @andygrunwald Would like to help test this v1 fix, but not sure how to go about using it where the go.mod still points to /v2 in the fork.

rdubya16 avatar Aug 29 '25 14:08 rdubya16

Any ETA when this will merge to the main branch? They conveniently killed the old API over labor day weekend for us

danekantner avatar Sep 02 '25 16:09 danekantner

Hi @andygrunwald,

We have panic hot fixed most of our tooling today with your PR branch and it looks alright.

However, I have run into the issue where SearchOptions{Fields: []string{"*all"}} is causing this error here:

json: cannot unmarshal object into Go struct field Issue.issues.fields.Alias.description of type string: file already closed

The fix/workaround is to explicitly only select fields, which I require.

EDIT: Looks like this already addressed in a comment in your PR: https://github.com/andygrunwald/go-jira/pull/724#issuecomment-3237491382.

moritzschmitz-oviva avatar Sep 04 '25 18:09 moritzschmitz-oviva

Hey all, Quick update on this one.

v1 of go-jira

I merged https://github.com/andygrunwald/go-jira/pull/725 (Kudos to @conor-naranjo and @erezrokah). This introduces a IssueService.SearchV2JQL() and IssueService.SearchV2JQLWithContext() to keep backwards compatibility with on-premise instances. A new version https://github.com/andygrunwald/go-jira/releases/tag/v1.16.1 was released.

v2 / Current main of go-jira

I merged a backported version of https://github.com/andygrunwald/go-jira/pull/725 by @conor-naranjo in https://github.com/andygrunwald/go-jira/pull/731. This introduces a new function IssueService.SearchV2JQL. It looks like the original Search function is still working on some installations. At least I have access to one cloud instance where Search() is still working.

https://github.com/andygrunwald/go-jira/blob/610ff3d23bbdb0d20456545f13db496039389396/cloud/examples/jql/main.go shows how to use the IssueService.SearchV2JQL function.

@Jcparkyn hinted that we can stay on Jira's v2 API. This lowers the migration effort and we kick the can down the road to by some time to get this lib working for Jira v3.

Feedback needed

Please let me know if both new versions work for you or if anything else is needed to get your current code back into a working state. Even a comment "Yep, works" is a good signal. If you react to this comment with emojis, I won't receive a notification and most likely won't see it.

I will leave this issue open for a bit to let others have a chance to look into it / to find it. When the few positive feedback came in, I will close it as I assume this is working for you.

Thanks for all the pings, Pull Requests, feedback, and testing comments.

andygrunwald avatar Sep 13 '25 19:09 andygrunwald

Using v1.16.1

	issues, _, err := jiraClient.Issue.SearchV2JQL(jql, &jira.SearchOptionsV2{
		MaxResults: 1000,
		Fields:     []string{"summary", "status", "priority", "labels", "issuetype"},
	})

Works for me 👍

aliakseiz avatar Sep 16 '25 07:09 aliakseiz

@andygrunwald I did the replacement from 1.13 to 1.17 today to cater for the deprecation warnings I was getting. Unfortunately, for my use case, it didn't work.

Considering the missing implementation of "Count Issues using JQL", I've been using the SearchResult.total present on the output of IssueService.Search() to count the issues. The absence of the "approximate count" method on the lib API is also reported in #692

The SearchV2JQL returns a different type, SearchAndReconciliateResult on JIRA docs (searchResultV2 in v1.16.1), which doesn't contain total.

UPDATE: I tried to put together the count endpoint quickly and realised after that there's a chance that this wouldn't work simultaneously for Cloud & OnPrem. It seems I do need the v3/search to fulfil my goal, or branch my code based on being on cloud or prem.

johnholiver avatar Sep 16 '25 12:09 johnholiver

github.com/andygrunwald/[email protected] Works great 👍

Replaced:

	startAt := 0
	for {
		searchOptions := &jira.SearchOptions{
			StartAt:    startAt,
			MaxResults: 50,
			Expand:     "changelog,comment",
		}
		...
		issues, resp, err := s.client.Issue.Search(jql, searchOptions)
		...
		startAt += len(issues)
	}

with

	nextPageToken := ""
	for {
		searchOptions := &jira.SearchOptionsV2{
			NextPageToken: nextPageToken,
			MaxResults:    50,
			Expand:        "changelog,comment",
			Fields:        []string{"*all"},
		}
		...
		issues, resp, err := s.client.Issue.SearchV2JQL(jql, searchOptions)
		...
		nextPageToken = resp.NextPageToken
	}

nareddyt avatar Sep 16 '25 14:09 nareddyt

confirmed it works after refactoring my search wrapper (...I also switched to v2 at same time and that itself was more ... work?)

func (j *Jira) Search(ctx context.Context, jql string, fields ...string) ([]*jira.Issue, error) {
	if fields == nil || len(fields) == 0 {
		fields = []string{"*navigable"}
	}

	options := &jira.SearchOptionsV2{
		NextPageToken: "",
		MaxResults:    100,
		Fields:        fields,
	}
	var allIssues []*jira.Issue

	for {
		issues, resp, err := j.Client.Issue.SearchV2JQL(ctx, jql, options)
		if err != nil {
			respErr(resp, err, jql)
			return nil, err
		}

		startLen := len(allIssues)
		allIssues = append(allIssues, make([]*jira.Issue, len(issues))...)
		for i := range issues {
			allIssues[startLen+i] = &issues[i]
		}

		if resp.NextPageToken == "" {
			break
		}
		options.NextPageToken = resp.NextPageToken
	}

	return allIssues, nil
}

danekantner avatar Sep 16 '25 21:09 danekantner