|
| 1 | +# Migration Guide from v7 or earlier to v8 |
| 2 | + |
| 3 | +## Breaking Changes |
| 4 | + |
| 5 | +### Metadata values must be strings |
| 6 | + |
| 7 | +**Why**: The GoCardless API only accepts string values for metadata, but the Java types previously allowed `Map<String, Object>`. This caused confusing runtime errors. The new version fixes the types to match the API requirements. |
| 8 | + |
| 9 | +**Impact**: Code that passes non-string values to `metadata` fields will fail to compile. |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +## Quick Migration |
| 14 | + |
| 15 | +### Option 1: Use MetadataHelper (Recommended) ✅ |
| 16 | + |
| 17 | +The easiest way to migrate is using the new `MetadataHelper` class: |
| 18 | + |
| 19 | +```java |
| 20 | +import com.gocardless.helpers.MetadataHelper; |
| 21 | +import java.util.HashMap; |
| 22 | +import java.util.Map; |
| 23 | + |
| 24 | +// ❌ BEFORE - Compiled but failed at runtime |
| 25 | +Map<String, Object> metadata = new HashMap<>(); |
| 26 | +metadata.put("user_id", 12345); // Integer |
| 27 | +metadata.put("is_active", true); // Boolean |
| 28 | +metadata.put("tags", Arrays.asList("vip", "premium")); // List |
| 29 | + |
| 30 | +client.customers().create() |
| 31 | + .withEmail("user@example.com") |
| 32 | + .withMetadata(metadata) // Runtime error! |
| 33 | + .execute(); |
| 34 | + |
| 35 | +// ✅ AFTER - One function call |
| 36 | +client.customers().create() |
| 37 | + .withEmail("user@example.com") |
| 38 | + .withMetadata(MetadataHelper.toMetadata(metadata)) // Converts all values |
| 39 | + .execute(); |
| 40 | +``` |
| 41 | + |
| 42 | +### Option 2: Manual Conversion |
| 43 | + |
| 44 | +If you prefer explicit control: |
| 45 | + |
| 46 | +```java |
| 47 | +Map<String, String> metadata = new HashMap<>(); |
| 48 | +metadata.put("user_id", String.valueOf(12345)); // "12345" |
| 49 | +metadata.put("is_active", String.valueOf(true)); // "true" |
| 50 | + |
| 51 | +Gson gson = new Gson(); |
| 52 | +metadata.put("tags", gson.toJson(Arrays.asList("vip", "premium"))); // JSON |
| 53 | + |
| 54 | +client.customers().create() |
| 55 | + .withEmail("user@example.com") |
| 56 | + .withMetadata(metadata) |
| 57 | + .execute(); |
| 58 | +``` |
| 59 | + |
| 60 | +--- |
| 61 | + |
| 62 | +## Helper Functions Reference |
| 63 | + |
| 64 | +### `MetadataHelper.toMetadata(obj)` |
| 65 | + |
| 66 | +Converts a map with mixed value types to metadata format: |
| 67 | + |
| 68 | +```java |
| 69 | +Map<String, Object> data = new HashMap<>(); |
| 70 | +data.put("user_id", 12345); |
| 71 | +data.put("is_premium", true); |
| 72 | +data.put("signup_date", new Date()); |
| 73 | +data.put("preferences", Map.of("theme", "dark")); |
| 74 | + |
| 75 | +Map<String, String> metadata = MetadataHelper.toMetadata(data); |
| 76 | +// Result: { |
| 77 | +// "user_id": "12345", |
| 78 | +// "is_premium": "true", |
| 79 | +// "signup_date": "Mon Jan 15 10:30:00 GMT 2024", |
| 80 | +// "preferences": "{\"theme\":\"dark\"}" |
| 81 | +// } |
| 82 | +``` |
| 83 | + |
| 84 | +### `MetadataHelper.toMetadataValue(value)` |
| 85 | + |
| 86 | +Converts a single value: |
| 87 | + |
| 88 | +```java |
| 89 | +MetadataHelper.toMetadataValue(12345); // "12345" |
| 90 | +MetadataHelper.toMetadataValue(true); // "true" |
| 91 | +MetadataHelper.toMetadataValue(Arrays.asList("vip")); // "[\"vip\"]" |
| 92 | +MetadataHelper.toMetadataValue(Map.of("theme", "dark")); // "{\"theme\":\"dark\"}" |
| 93 | +``` |
| 94 | + |
| 95 | +### `MetadataHelper.isValidMetadata(obj)` |
| 96 | + |
| 97 | +Check if metadata is valid: |
| 98 | + |
| 99 | +```java |
| 100 | +Map<String, Object> metadata = new HashMap<>(); |
| 101 | +metadata.put("key", "value"); |
| 102 | + |
| 103 | +if (MetadataHelper.isValidMetadata(metadata)) { |
| 104 | + // All values are strings |
| 105 | + client.customers().create() |
| 106 | + .withMetadata((Map<String, String>) (Object) metadata) |
| 107 | + .execute(); |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +### `MetadataHelper.parseMetadataValue(value, type)` |
| 112 | + |
| 113 | +Parse metadata values back to their original types: |
| 114 | + |
| 115 | +```java |
| 116 | +Customer customer = client.customers().get("CU123").execute(); |
| 117 | +Map<String, String> metadata = customer.getMetadata(); |
| 118 | + |
| 119 | +// Parse back to original types |
| 120 | +Integer userId = MetadataHelper.parseMetadataValue( |
| 121 | + metadata.get("user_id"), |
| 122 | + Integer.class |
| 123 | +); // 12345 |
| 124 | + |
| 125 | +Boolean isActive = MetadataHelper.parseMetadataValue( |
| 126 | + metadata.get("is_active"), |
| 127 | + Boolean.class |
| 128 | +); // true |
| 129 | + |
| 130 | +// For generic types, use TypeToken |
| 131 | +List<String> tags = MetadataHelper.parseMetadataValue( |
| 132 | + metadata.get("tags"), |
| 133 | + new TypeToken<List<String>>(){}.getType() |
| 134 | +); // ["vip", "premium"] |
| 135 | +``` |
0 commit comments