Skip to content

Commit 7f1a295

Browse files
SK-3061: revert getFields() to Map<String, Object>, keep getTokens() typed (#409)
getTokens() itself (item 6, commit 8148504) stays Map<String, List<Token>>. But getFields() delegating straight to it changed its return-type generics from Object to List<Token> - a needless japicmp break for a deprecated alias nobody should be adding new calls to anyway, and one this PR had flagged as avoidable but initially left in favor of just regenerating the baseline. Reverted getFields() to its original Map<String, Object> contract by rendering getTokens()'s typed data back into that raw shape via a new package-private Token.toRawTokens(Map<String, List<Token>>) - the inverse of Token.parseTokens(). Package-private keeps it outside the accessModifier=protected japicmp contract, so it doesn't itself become a compatibility commitment. The round trip is lossless for map-shaped wire input (the normal case) but lossy for the bare-value edge case parseTokens() also handles (a raw "tok-abc" string loses its way back to {"token": "tok-abc", "tokenGroupName": null} - still valid data, just not byte-identical to the original wire shape). Updated every test asserting on getFields() accordingly, and added direct toRawTokens() coverage (null input, single/multiple token groups, round-trip-with-map-input) in ResponseComponentTests. Regenerated flowvault/api-report/skyflow-flowvault-java.baseline.jar via scripts/contract-snapshot-update.sh flowvault - this supersedes the baseline regenerated in f326ec2, since that one still had getFields() returning the typed map. mvn -pl common,flowvault -am verify passes clean against it. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 44d7340 commit 7f1a295

8 files changed

Lines changed: 122 additions & 14 deletions

File tree

‎flowvault/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ Every bulk response exposes `getSummary()` and `getRecords()`. The records list
767767
| `getError()` | failures only | Error message for this item. `null` means this item succeeded. |
768768
| `getRequestId()` | failures only | The `x-request-id` of the batch this item was in — quote it in support escalations. Items from the same batch share one id. |
769769

770-
The success payload sits alongside those fields on the same object: `getSkyflowId()`/`getTokens()`/`getData()` for insert (`getFields()` is a deprecated alias for `getTokens()`), `getValue()`/`getTokenGroupName()`/`getMetadata()` for detokenize, `getTokens()` for tokenize, `getToken()` for delete.
770+
The success payload sits alongside those fields on the same object: `getSkyflowId()`/`getTokens()`/`getData()` for insert (`getFields()` is deprecated — it returns the same data in its original, pre-typed `Map<String, Object>` shape, not `getTokens()`'s `Token` objects), `getValue()`/`getTokenGroupName()`/`getMetadata()` for detokenize, `getTokens()` for tokenize, `getToken()` for delete.
771771

772772
Summaries per operation:
773773

300 Bytes
Binary file not shown.

‎flowvault/src/main/java/com/skyflow/vault/data/InsertResponseRecord.java‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,15 @@ public Map<String, List<Token>> getTokens() {
5555
}
5656

5757
/**
58-
* @deprecated Response key 'fields' is deprecated. Use {@link #getTokens()} instead.
58+
* @deprecated Response key 'fields' is deprecated. Use {@link #getTokens()} instead. This
59+
* still returns {@code Map<String, Object>}, matching its original (pre-typed) contract —
60+
* see {@link Token#toRawTokens(Map)} for how {@link #getTokens()}'s typed data is rendered
61+
* back into that generic shape.
5962
*/
6063
@Deprecated(since = "1.0.2", forRemoval = true)
61-
public Map<String, List<Token>> getFields() {
64+
public Map<String, Object> getFields() {
6265
LogUtil.printWarningLog(InfoLogs.DEPRECATED_INSERT_FIELDS_GETTER.getLog());
63-
return getTokens();
66+
return Token.toRawTokens(getTokens());
6467
}
6568

6669
public Map<String, Object> getData() {

‎flowvault/src/main/java/com/skyflow/vault/data/Token.java‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,35 @@ private static Token toToken(Object entry) {
100100
}
101101
return null;
102102
}
103+
104+
/**
105+
* The inverse of {@link #parseTokens(Map)}: renders parsed {@link Token} objects back into
106+
* the generic {@code Map<String, Object>} shape {@code getFields()} returned before it was
107+
* deprecated, for callers who haven't migrated to {@link InsertResponseRecord#getTokens()}
108+
* yet. Each column's value becomes a {@code List<Map<String, Object>>}, one map per
109+
* {@code Token} with {@code "token"}/{@code "tokenGroupName"} keys — this doesn't reproduce
110+
* the exact original wire shape (a single-group column may originally have been a bare
111+
* value or an unwrapped map rather than a one-element list), since that distinction is lost
112+
* once parsed, but it's a consistent, self-describing shape every caller can read the same
113+
* way regardless of how many groups a column has.
114+
*
115+
* <p>Returns {@code null} when {@code tokens} is {@code null}.
116+
*/
117+
static Map<String, Object> toRawTokens(Map<String, List<Token>> tokens) {
118+
if (tokens == null) {
119+
return null;
120+
}
121+
Map<String, Object> raw = new LinkedHashMap<>();
122+
for (Map.Entry<String, List<Token>> entry : tokens.entrySet()) {
123+
List<Map<String, Object>> rawEntries = new ArrayList<>();
124+
for (Token token : entry.getValue()) {
125+
Map<String, Object> rawEntry = new LinkedHashMap<>();
126+
rawEntry.put("token", token.getToken());
127+
rawEntry.put("tokenGroupName", token.getTokenGroupName());
128+
rawEntries.add(rawEntry);
129+
}
130+
raw.put(entry.getKey(), rawEntries);
131+
}
132+
return raw;
133+
}
103134
}

‎flowvault/src/test/java/com/skyflow/utils/UtilsTests.java‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1704,8 +1704,14 @@ public void testFormatBulkInsertResponse_success() {
17041704
// The wire type's raw tokens map is parsed into typed Token objects before reaching the
17051705
// caller - see ResponseComponentTests's Token.parseTokens() tests for the parsing logic.
17061706
Assert.assertEquals("tok-abc", inserted.getTokens().get("name").get(0).getToken());
1707-
// getFields() is deprecated but still delegates to getTokens() for backward compatibility.
1708-
Assert.assertEquals("tok-abc", inserted.getFields().get("name").get(0).getToken());
1707+
// getFields() is deprecated, and now renders that typed data back into its original
1708+
// Map<String, Object> shape rather than returning getTokens()'s value directly - a bare
1709+
// string column comes back as a one-element List<Map> instead of the original bare value,
1710+
// since that distinction is lost once the raw data is parsed into Token objects.
1711+
Object nameField = inserted.getFields().get("name");
1712+
Map<?, ?> nameToken = (Map<?, ?>) ((List<?>) nameField).get(0);
1713+
Assert.assertEquals("tok-abc", nameToken.get("token"));
1714+
Assert.assertNull(nameToken.get("tokenGroupName"));
17091715
Assert.assertEquals(data, inserted.getData());
17101716
Assert.assertEquals(0, inserted.getIndex());
17111717
Assert.assertEquals(200, inserted.getHttpCode());

‎flowvault/src/test/java/com/skyflow/vault/controller/VaultControllerTests.java‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -706,8 +706,10 @@ public void testBulkInsert_successWithListOfMapsTokenShape() throws Exception {
706706

707707
BulkInsertResponseRecord inserted = response.getRecords().get(0);
708708
Assert.assertNotNull(inserted.getTokens());
709-
// getFields() is deprecated but still delegates to getTokens() for backward compatibility.
710-
Assert.assertEquals(inserted.getTokens(), inserted.getFields());
709+
// getFields() is deprecated and now renders getTokens()'s typed data back into its
710+
// original raw shape - a lossless round trip for this input, so it equals the raw map
711+
// the mock returned in the first place.
712+
Assert.assertEquals(tokens, inserted.getFields());
711713
// The wire type's List<Map> token shape is parsed into typed Token objects - no casting.
712714
List<Token> field1Tokens = inserted.getTokens().get("field1");
713715
Assert.assertEquals(1, field1Tokens.size());

‎flowvault/src/test/java/com/skyflow/vault/data/BulkResponseTests.java‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,14 @@ public void testBulkInsertResponse_recordsPreserveIndexAndInheritedFields() {
7373
Assert.assertEquals("table1", actual.getTableName());
7474
Assert.assertEquals("id-1", actual.getSkyflowId());
7575
Assert.assertEquals(tokens, actual.getTokens());
76-
// getFields() is deprecated but still delegates to getTokens() for backward compatibility.
77-
Assert.assertEquals(tokens, actual.getFields());
76+
// getFields() is deprecated, and now returns its original (pre-typed) shape - a Map<String,
77+
// Object> rendered back from the typed getTokens() data, not getTokens()'s value itself.
78+
Map<String, Object> rawToken = new HashMap<>();
79+
rawToken.put("token", "token-name");
80+
rawToken.put("tokenGroupName", "group1");
81+
Map<String, Object> expectedFields = new HashMap<>();
82+
expectedFields.put("name", Collections.singletonList(rawToken));
83+
Assert.assertEquals(expectedFields, actual.getFields());
7884
Assert.assertEquals(data, actual.getData());
7985
Assert.assertEquals(hashedData, actual.getHashedData());
8086
Assert.assertEquals(200, actual.getHttpCode());

‎flowvault/src/test/java/com/skyflow/vault/data/ResponseComponentTests.java‎

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@
1919
*/
2020
public class ResponseComponentTests {
2121

22+
// getFields()'s deprecated, pre-typed shape for a single column with one token group -
23+
// {"name": [{"token": "tok-1", "tokenGroupName": "group1"}]} - matching
24+
// tokens.put("name", Collections.singletonList(new Token("tok-1", "group1"))).
25+
private static Map<String, Object> singleColumnRawFields() {
26+
Map<String, Object> rawToken = new HashMap<>();
27+
rawToken.put("token", "tok-1");
28+
rawToken.put("tokenGroupName", "group1");
29+
Map<String, Object> rawFields = new HashMap<>();
30+
rawFields.put("name", Collections.singletonList(rawToken));
31+
return rawFields;
32+
}
33+
2234
// Tests for Success and Summary were removed: the bulk insert response contract replaced
2335
// those classes with BulkInsertResponseRecord / BulkSummary, covered below. Token was removed
2436
// in the same rework, then reintroduced (with the same shape it had before) as the type
@@ -42,8 +54,9 @@ public void testBulkInsertResponseRecord_gettersReturnConstructorValues() {
4254
Assert.assertEquals("persons", record.getTableName());
4355
Assert.assertEquals("skyflow-id-1", record.getSkyflowId());
4456
Assert.assertEquals(tokens, record.getTokens());
45-
// getFields() is deprecated but still delegates to getTokens() for backward compatibility.
46-
Assert.assertEquals(tokens, record.getFields());
57+
// getFields() is deprecated, and now returns its original (pre-typed) shape - a Map<String,
58+
// Object> rendered back from the typed getTokens() data, not getTokens()'s value itself.
59+
Assert.assertEquals(singleColumnRawFields(), record.getFields());
4760
Assert.assertEquals(data, record.getData());
4861
Assert.assertEquals(hashedData, record.getHashedData());
4962
Assert.assertEquals(200, record.getHttpCode());
@@ -67,7 +80,7 @@ public void testBulkInsertResponseRecord_deprecatedConstructorAndGetFieldsStillW
6780
2, "persons", "skyflow-id-1", tokens, hashedData, 200, null, null);
6881

6982
Assert.assertEquals(tokens, record.getTokens());
70-
Assert.assertEquals(tokens, record.getFields());
83+
Assert.assertEquals(singleColumnRawFields(), record.getFields());
7184
Assert.assertNull(record.getData());
7285
Assert.assertEquals(hashedData, record.getHashedData());
7386
}
@@ -89,7 +102,7 @@ public void testInsertResponseRecord_deprecatedConstructorDefaultsDataToNull() {
89102
Assert.assertEquals("persons", record.getTableName());
90103
Assert.assertEquals("skyflow-id-1", record.getSkyflowId());
91104
Assert.assertEquals(tokens, record.getTokens());
92-
Assert.assertEquals(tokens, record.getFields());
105+
Assert.assertEquals(singleColumnRawFields(), record.getFields());
93106
Assert.assertNull(record.getData());
94107
Assert.assertEquals(hashedData, record.getHashedData());
95108
Assert.assertEquals(200, record.getHttpCode());
@@ -254,6 +267,53 @@ public void testParseTokens_skipsNullEntriesWithinAList() {
254267
Assert.assertEquals("tok-a", col1.get(0).getToken());
255268
}
256269

270+
@Test
271+
public void testToRawTokens_returnsNullWhenTokensIsNull() {
272+
Assert.assertNull(Token.toRawTokens(null));
273+
}
274+
275+
@Test
276+
public void testToRawTokens_rendersEachTokenAsAMapWithBothKeys() {
277+
Map<String, List<Token>> tokens = new HashMap<>();
278+
tokens.put("col1", Collections.singletonList(new Token("tok-a", "tg1")));
279+
280+
Map<String, Object> raw = Token.toRawTokens(tokens);
281+
282+
List<?> col1 = (List<?>) raw.get("col1");
283+
Assert.assertEquals(1, col1.size());
284+
Map<?, ?> entry = (Map<?, ?>) col1.get(0);
285+
Assert.assertEquals("tok-a", entry.get("token"));
286+
Assert.assertEquals("tg1", entry.get("tokenGroupName"));
287+
}
288+
289+
@Test
290+
public void testToRawTokens_rendersMultipleTokenGroupsAsSeparateMapEntries() {
291+
Map<String, List<Token>> tokens = new HashMap<>();
292+
tokens.put("col1", Arrays.asList(new Token("tok-a", "tg1"), new Token("tok-b", "tg2")));
293+
294+
List<?> col1 = (List<?>) Token.toRawTokens(tokens).get("col1");
295+
296+
Assert.assertEquals(2, col1.size());
297+
Assert.assertEquals("tok-a", ((Map<?, ?>) col1.get(0)).get("token"));
298+
Assert.assertEquals("tok-b", ((Map<?, ?>) col1.get(1)).get("token"));
299+
}
300+
301+
@Test
302+
public void testToRawTokens_isTheInverseOfParseTokensForMapShapedInput() {
303+
// parseTokens() followed by toRawTokens() round-trips losslessly when every column's raw
304+
// value was already a {token, tokenGroupName} map (or list of them) - the shape toRawTokens
305+
// always produces. Only the bare-value case (see UtilsTests) loses information on the way.
306+
Map<String, Object> entry = new HashMap<>();
307+
entry.put("token", "tok-a");
308+
entry.put("tokenGroupName", "tg1");
309+
Map<String, Object> rawTokens = new HashMap<>();
310+
rawTokens.put("col1", Collections.singletonList(entry));
311+
312+
Map<String, Object> roundTripped = Token.toRawTokens(Token.parseTokens(rawTokens));
313+
314+
Assert.assertEquals(rawTokens, roundTripped);
315+
}
316+
257317
// ── BulkSummary ──────────────────────────────────────────────────────────
258318

259319
@Test

0 commit comments

Comments
 (0)