v0.8.4

Guía de migración de API v1 a v2

Ticketing API Client v1 to v2 Migration Guide

This guide helps consumers of the Ticketing API client libraries migrate from the v1 generation of the Java and Go clients to v2.

What “v1” and “v2” mean here. Both client generations call the same HTTP API, served under https://ticketing.masstack.com/v2 (production) and https://ticketing.sta.masstack.com/v2 (staging). The version in the library path refers to the generated client, not to a different HTTP base path: v1 clients were generated from OpenAPI document 2.1.1, v2 clients from 2.2.0. If you call the API over HTTP without the libraries, only the HTTP-level changes section applies to you.

At a glance

Topicv1 clientsv2 clients
Java Bazel target//pkg/mas-stack/oss/mas-ticketing/client/java/v1:api-client//pkg/mas-stack/oss/mas-ticketing/client/java/v2:api-client
Go Bazel target//pkg/mas-stack/oss/mas-ticketing/client/go/v1:go_default_library//pkg/mas-stack/oss/mas-ticketing/client/go/v2:go_default_library
Go import pathgithub.com/masmovil/mm-monorepo/pkg/mas-stack/oss/mas-ticketing/client/go-go-v1github.com/masmovil/mm-monorepo/pkg/mas-stack/oss/mas-ticketing/client/go-go-v2
OpenAPI document2.1.12.2.0
Transition custom fieldsPlaceholder properties customField1, customField2, customFieldNSingle customFields object keyed by Jira custom field name
ticketing-provider valuesJAM, JCC, TGJ, JNEXTJAM, TGJ, JNEXT, JNODUS, OCEANE
Go service namesTicketApi, CommentApi, AttachmentApi, IssueLinkApi, IssueLinkTypesApiTicketAPI, CommentAPI, AttachmentAPI, IssueLinkAPI, IssueLinkTypesAPI, plus RemotelinkAPI, GridAPI, IaSummaryAPI
Java JSON serializationGson (Retrofit GsonConverterFactory)Gson (unchanged)

Breaking changes

1. Custom fields in transitions

The request body of POST /orgs/{org}/ticket/{ticketId}/transitions changed shape. The v1 Transition model could not express real custom fields: it only exposed placeholder properties (customField1, customField2, customFieldN), so integrations that needed to set custom fields during a transition typically bypassed the typed model and sent them as top-level properties of a hand-built JSON body.

In v2 custom fields travel in a dedicated customFields object, keyed by the actual Jira custom field name (for example external_system_name, external_system_info, solution_detail). Only the fields allowed by the specific transition are processed.

Legacy layout (v1 era, top-level fields):

json

{
  "transitionId": 111,
  "external_system_name": "Orange",
  "other_custom_field": "value"
}

v2 layout (required):

json

{
  "transitionId": 111,
  "customFields": {
    "external_system_name": "Orange",
    "other_custom_field": "value"
  }
}

2. Ticketing providers

The ticketing-provider header keeps its name in both generations, but its allowed values changed:

  • JCC (Jira CC) was removed. The provider is decommissioned; requests using it will not be routed anywhere.
  • JNODUS (Jira Nodus) and OCEANE (Oceane) were added.

If your code hard-codes provider values, review them against the current list: JAM, TGJ, JNEXT, JNODUS, OCEANE.

3. Go: service names and import path

The v2 Go client was generated with a newer generator that names services with an uppercase API suffix: client.TicketApi becomes client.TicketAPI, client.CommentApi becomes client.CommentAPI, and so on. The import path also changes (see the table above). Both changes are caught by the compiler, so they are easy to find but must be fixed in every call site.

4. New operations (non-breaking)

The v2 clients expose operations that did not exist in v1: remote links (RemotelinkApi / RemotelinkAPI, already present in the Java v1 client but new for Go), grid (GridApi / GridAPI) and IA summary (IaSummaryApi / IaSummaryAPI). Nothing to migrate; they are simply available once you move to v2.

Java serialization

Both Java generations are Retrofit 2 + RxJava 3 clients that serialize with Gson through GsonConverterFactory (see com.masmovil.ticketing.client.JSON and ApiClient). There is no serializer change to account for. The only serialization-related difference is the Transition model itself: customFields is declared as Object and accepts any Map<String, Object>, which Gson serializes as a nested JSON object.

Migration steps

Java client

1. Update the dependency

python

# Old
deps = [
    "//pkg/mas-stack/oss/mas-ticketing/client/java/v1:api-client",
]

# New
deps = [
    "//pkg/mas-stack/oss/mas-ticketing/client/java/v2:api-client",
]

2. Imports

Package names are unchanged (com.masmovil.ticketing.client, com.masmovil.ticketing.client.api, com.masmovil.ticketing.client.model), so existing imports keep compiling. Re-run your build to make sure no class you used disappeared or changed signature.

3. Update transition calls

The doTransition signature is the same in both generations:

java

Observable<Response<Void>> doTransition(
    String ticketId,
    String org,
    Transition transition,
    Map<String, String> headers,      // must include ticketing-provider
    Map<String, String> queryParams);

What changes is how you build the body. With v1 there was no typed way to send custom fields; with v2 they go into Transition.customFields:

java

ApiClient apiClient = new ApiClient(); // configure base URL and auth as you do today
TicketApi api = apiClient.createService(TicketApi.class);

Map<String, Object> customFields = new HashMap<>();
customFields.put("external_system_name", "Orange");
customFields.put("external_system_info", "Escalated via API");

Transition transition = new Transition()
    .transitionId(111)
    .customFields(customFields);

Map<String, String> headers = Map.of("ticketing-provider", "TGJ");

api.doTransition(ticketId, org, transition, headers, Collections.emptyMap())
    .subscribe(
        response -> {
            if (!response.isSuccessful()) {
                // handle 4xx/5xx: response.code(), response.errorBody()
            }
        },
        error -> {
            // handle transport errors
        });

Note the parameter order: ticketId comes before org.

The client returns an RxJava 3 Observable, the same reactive types Vert.x Rx uses, so from a verticle you stay reactive: chain or subscribe as above, and let OkHttp run the request on its own threads. Do not call blockingFirst() or any other blocking* operator on the event loop — it stalls the loop and Vert.x will report a blocked thread. Reserve blocking only for plain, non-reactive code (a CLI, a batch job):

java

Response<Void> response = api
    .doTransition(ticketId, org, transition, headers, Collections.emptyMap())
    .blockingFirst(); // only outside an event loop

Go client

1. Update the dependency

python

# Old
deps = [
    "//pkg/mas-stack/oss/mas-ticketing/client/go/v1:go_default_library",
]

# New
deps = [
    "//pkg/mas-stack/oss/mas-ticketing/client/go/v2:go_default_library",
]

2. Update the import path

go

// Old
import ticketing "github.com/masmovil/mm-monorepo/pkg/mas-stack/oss/mas-ticketing/client/go-go-v1"

// New
import ticketing "github.com/masmovil/mm-monorepo/pkg/mas-stack/oss/mas-ticketing/client/go-go-v2"

The Go package name is mas_ticketing_client in both generations; aliasing the import (as above) keeps call sites stable.

3. Rename services and update transition calls

go

client := ticketing.NewAPIClient(cfg)

transition := ticketing.Transition{
    TransitionId: 111, // int32, required (not a pointer)
    CustomFields: map[string]interface{}{
        "external_system_name": "Orange",
        "external_system_info": "Escalated via API",
    },
}

// v1 was client.TicketApi; v2 is client.TicketAPI
resp, err := client.TicketAPI.
    DoTransition(ctx, ticketId, org).
    TicketingProvider([]string{"TGJ"}).
    Transition(transition).
    Execute()

Execute() returns (*http.Response, error); the transition endpoint has no response body.

HTTP-level changes

If you integrate directly over HTTP, this is the whole migration:

  1. Send transition custom fields inside the customFields object (see Custom fields in transitions) instead of as top-level properties.
  2. Stop using JCC as ticketing-provider; use one of JAM, TGJ, JNEXT, JNODUS, OCEANE.
  3. Base URLs, authentication (Authorization: Bearer {token}) and the rest of the paths are unchanged.

The current contract is published on the developer portal (Ticketing API v2) and, for internal users, in the IDP catalog (ticketing-api-v2, Definition tab).

Testing your migration

Add a serialization test so a future refactor cannot silently fall back to the legacy layout. Test with the same serializer the client uses at runtime.

Java (Gson, as used by the client):

java

@Test
void transitionSerializesCustomFieldsNested() {
    Map<String, Object> customFields = new LinkedHashMap<>();
    customFields.put("external_system_name", "Orange");

    Transition transition = new Transition().transitionId(111).customFields(customFields);

    Gson gson = new JSON().getGson();
    JsonObject root = gson.toJsonTree(transition).getAsJsonObject();

    assertTrue(root.has("customFields"));
    assertFalse(root.has("external_system_name"));
    assertEquals("Orange", root.getAsJsonObject("customFields").get("external_system_name").getAsString());
}

Go (encoding/json, as used by the client):

go

func TestTransitionSerializesCustomFieldsNested(t *testing.T) {
    transition := ticketing.Transition{
        TransitionId: 111,
        CustomFields: map[string]interface{}{"external_system_name": "Orange"},
    }

    payload, err := json.Marshal(transition)
    require.NoError(t, err)

    var parsed map[string]interface{}
    require.NoError(t, json.Unmarshal(payload, &parsed))

    require.NotContains(t, parsed, "external_system_name")
    customFields := parsed["customFields"].(map[string]interface{})
    assert.Equal(t, "Orange", customFields["external_system_name"])
}

The v2 Java client ships equivalent tests (TransitionSerializationTest, TicketApiDoTransitionContractTest) that you can use as a reference.

Compatibility period

  • v1 clients remain in the repository but are deprecated: use them only for code you are not touching yet. They will not receive the new operations nor the updated provider list.
  • v2 clients are the target for all new development.
  • Backend: the API currently still accepts the legacy top-level layout for transition custom fields, but that tolerance is temporary and will be removed. Migrate to customFields now rather than when it breaks.

Common migration issues

Custom fields are not applied in Jira

Symptom: the transition succeeds but the custom fields you sent are missing.

Cause: the fields were sent as top-level properties (legacy layout), or the field is not allowed by that specific transition.

Fix: put them inside customFields, using the Jira custom field names. Check which fields a transition accepts with GET /ticket/{ticketId}/metadata/update/properties before assuming a field can be set.

Go: client.TicketApi undefined

Symptom: compile error after switching the import path.

Cause: services were renamed to the ...API suffix in v2.

Fix: rename every service reference (TicketApi → TicketAPI, CommentApi → CommentAPI, etc.).

Requests rejected or unrouted after migrating

Symptom: errors about an invalid or unsupported ticketing-provider value.

Cause: the provider value JCC is no longer valid.

Fix: use the current provider list; if your tickets lived in Jira CC, contact the mas-ticketing team to confirm where they were migrated.

Existing tests fail after migrating

Symptom: JSON assertions written against the legacy layout fail.

Cause: expectations still look for top-level custom fields.

Fix: update them to the nested customFields layout, as in the examples above.

Rollback plan

If you hit a blocking issue:

  1. Revert the Bazel dependency (and, for Go, the import path and service names) to v1.
  2. Report the problem to the mas-ticketing team with the error message, a minimal code snippet and the environment (dev/sta/prod).
  3. Keep your v2 changes in a branch so they can be re-applied once the issue is resolved.

Need help?

Additional resources