From 163e235e372d37a4608ea06215dbef0d9826f5e0 Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Tue, 2 Jun 2026 03:53:09 +0200 Subject: [PATCH 01/10] Mark generated API2 endpoint blocks --- .../java/com/transloadit/sdk/Transloadit.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/main/java/com/transloadit/sdk/Transloadit.java b/src/main/java/com/transloadit/sdk/Transloadit.java index 57b95f8..f48a433 100644 --- a/src/main/java/com/transloadit/sdk/Transloadit.java +++ b/src/main/java/com/transloadit/sdk/Transloadit.java @@ -327,11 +327,19 @@ public Assembly newAssembly() { * @throws RequestException if request to transloadit server fails. * @throws LocalOperationException if something goes wrong while running non-http operations. */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + public AssemblyResponse getAssembly(String id) throws RequestException, LocalOperationException { Request request = new Request(this); return new AssemblyResponse(request.get("/assemblies/" + id)); } + // + /** * Returns a single assembly. * @@ -354,12 +362,20 @@ public AssemblyResponse getAssemblyByUrl(String url) * @throws RequestException if request to transloadit server fails. * @throws LocalOperationException if something goes wrong while running non-http operations. */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + public AssemblyResponse cancelAssembly(String url) throws RequestException, LocalOperationException { Request request = new Request(this); return new AssemblyResponse(request.delete(url, new HashMap())); } + // + /** * Returns a list of all assemblies under the user account. * @@ -368,12 +384,20 @@ public AssemblyResponse cancelAssembly(String url) * @throws RequestException if request to transloadit server fails. * @throws LocalOperationException if something goes wrong while running non-http operations. */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + public ListResponse listAssemblies(Map options) throws RequestException, LocalOperationException { Request request = new Request(this); return new ListResponse(request.get("/assemblies", options)); } + // + /** * Returns a list of all assemblies under the user account. * @return {@link ListResponse} @@ -403,11 +427,19 @@ public Template newTemplate(String name) { * @throws RequestException if request to transloadit server fails. * @throws LocalOperationException if something goes wrong while running non-http operations. */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + public Response getTemplate(String id) throws RequestException, LocalOperationException { Request request = new Request(this); return new Response(request.get("/templates/" + id)); } + // + /** * Updates the template with the specified id. * @@ -418,12 +450,20 @@ public Response getTemplate(String id) throws RequestException, LocalOperationEx * @throws RequestException if request to transloadit server fails. * @throws LocalOperationException if something goes wrong while running non-http operations. */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + public Response updateTemplate(String id, Map options) throws RequestException, LocalOperationException { Request request = new Request(this); return new Response(request.put("/templates/" + id, options)); } + // + /** * Deletes a template. * @@ -433,12 +473,20 @@ public Response updateTemplate(String id, Map options) * @throws RequestException if request to transloadit server fails. * @throws LocalOperationException if something goes wrong while running non-http operations. */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + public Response deleteTemplate(String id) throws RequestException, LocalOperationException { Request request = new Request(this); return new Response(request.delete("/templates/" + id, new HashMap())); } + // + /** * Returns a list of all templates under the user account. * @@ -448,12 +496,20 @@ public Response deleteTemplate(String id) * @throws RequestException if request to transloadit server fails. * @throws LocalOperationException if something goes wrong while running non-http operations. */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + public ListResponse listTemplates(Map options) throws RequestException, LocalOperationException { Request request = new Request(this); return new ListResponse(request.get("/templates", options)); } + // + /** * Returns a list of all templates under the user account. * @@ -477,12 +533,20 @@ public ListResponse listTemplates() * @throws RequestException if request to transloadit server fails. * @throws LocalOperationException if something goes wrong while running non-http operations. */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + public Response getBill(int month, int year) throws RequestException, LocalOperationException { Request request = new Request(this); return new Response(request.get("/bill/" + year + String.format("-%02d", month))); } + // + /** * Returns Array List of String encoded Exceptions, which should be qualified for a retry attempt. * {@code "java.net.SocketTimeoutException" } is added by default From 06a79b7924bd10989f704cfa6b9d61220b5377ac Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Tue, 2 Jun 2026 04:07:19 +0200 Subject: [PATCH 02/10] Add API2 template lifecycle example --- examples/build.gradle | 6 + .../Api2DevdockTemplateLifecycle.java | 177 ++++++++++++++++++ settings.gradle | 4 +- .../java/com/transloadit/sdk/Transloadit.java | 23 +++ 4 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 examples/src/main/java/com/transloadit/examples/Api2DevdockTemplateLifecycle.java diff --git a/examples/build.gradle b/examples/build.gradle index a75ea9e..3426c6d 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -10,6 +10,7 @@ sourceCompatibility = 1.8 targetCompatibility = 1.8 repositories { + mavenCentral() jcenter() } @@ -37,3 +38,8 @@ compileTestKotlin { jvmTarget.set(JvmTarget.JVM_1_8) } } + +tasks.register('api2DevdockTemplateLifecycle', JavaExec) { + classpath = sourceSets.main.runtimeClasspath + mainClass = 'com.transloadit.examples.Api2DevdockTemplateLifecycle' +} diff --git a/examples/src/main/java/com/transloadit/examples/Api2DevdockTemplateLifecycle.java b/examples/src/main/java/com/transloadit/examples/Api2DevdockTemplateLifecycle.java new file mode 100644 index 0000000..ee19673 --- /dev/null +++ b/examples/src/main/java/com/transloadit/examples/Api2DevdockTemplateLifecycle.java @@ -0,0 +1,177 @@ +package com.transloadit.examples; + +import com.transloadit.sdk.Transloadit; +import com.transloadit.sdk.response.ListResponse; +import com.transloadit.sdk.response.Response; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Runs API2's contract-owned Template lifecycle scenario against devdock. + */ +public final class Api2DevdockTemplateLifecycle { + /** + * Runs the Template lifecycle scenario. + * + * @param args ignored + * @throws Exception if the scenario cannot be completed + */ + public static void main(String[] args) throws Exception { + JSONObject scenario = loadScenario(); + Transloadit transloadit = new Transloadit( + requiredEnv("TRANSLOADIT_KEY"), + requiredEnv("TRANSLOADIT_SECRET"), + requiredEnv("TRANSLOADIT_ENDPOINT")); + + JSONObject templateConfig = scenario.getJSONObject("template"); + JSONObject updateConfig = scenario.getJSONObject("update"); + String templateName = templateConfig.getString("namePrefix") + "-" + System.currentTimeMillis(); + + Response created = transloadit.createTemplate(templatePayload(templateName, templateConfig)); + String templateId = created.json().getString("id"); + boolean deleteTemplate = true; + + try { + Response fetched = transloadit.getTemplate(templateId); + + Map listOptions = new HashMap(); + listOptions.put("pagesize", scenario.getJSONObject("list").getInt("pageSize")); + ListResponse listed = transloadit.listTemplates(listOptions); + + String updatedTemplateName = templateName + updateConfig.getString("nameSuffix"); + transloadit.updateTemplate(templateId, templatePayload(updatedTemplateName, updateConfig)); + Response updated = transloadit.getTemplate(templateId); + + transloadit.deleteTemplate(templateId); + deleteTemplate = false; + + JSONObject result = new JSONObject(); + JSONObject deletedGet = deletedGetResult(transloadit, templateId); + for (Iterator keys = deletedGet.keys(); keys.hasNext();) { + String key = keys.next(); + result.put(key, deletedGet.get(key)); + } + result.put("fetched", templateResult(fetched.json())); + result.put("listCount", listed.size()); + result.put("templateId", templateId); + result.put("templateName", templateName); + result.put("updated", templateResult(updated.json())); + result.put("updatedTemplateName", updatedTemplateName); + writeResult(result); + } finally { + if (deleteTemplate) { + transloadit.deleteTemplate(templateId); + } + } + + System.out.println("Java SDK devdock scenario " + scenario.getString("scenarioId") + + " passed for " + templateId); + } + + private static JSONObject deletedGetResult(Transloadit transloadit, String templateId) throws Exception { + Response response = transloadit.getTemplate(templateId); + JSONObject body = response.json(); + boolean succeeded = response.status() >= 200 && response.status() < 300 && !body.has("error"); + + JSONObject result = new JSONObject(); + result.put("deletedGetSucceeded", succeeded); + result.put("deletedErrorCode", body.optString("error", body.optString("ok", ""))); + return result; + } + + private static JSONObject loadScenario() throws Exception { + String scenarioPath = System.getenv("API2_SDK_EXAMPLE_SCENARIO"); + if (scenarioPath == null || scenarioPath.isEmpty()) { + scenarioPath = "examples/api2-devdock-template-lifecycle/api2-scenario.json"; + } + + byte[] contents = Files.readAllBytes(Paths.get(scenarioPath)); + return new JSONObject(new String(contents, StandardCharsets.UTF_8)); + } + + private static String requiredEnv(String name) { + String value = System.getenv(name); + if (value == null || value.isEmpty()) { + throw new IllegalStateException(name + " must be set"); + } + + return value; + } + + private static Map templatePayload(String name, JSONObject config) { + JSONObject content = config.getJSONObject("content"); + Map template = jsonObjectToMap(content.getJSONObject("additionalProperties")); + template.put("steps", jsonObjectToMap(content.getJSONObject("steps"))); + + Map payload = new HashMap(); + payload.put("name", name); + payload.put("require_signature_auth", config.getBoolean("requireSignatureAuth") ? 1 : 0); + payload.put("template", template); + return payload; + } + + private static JSONObject templateResult(JSONObject template) { + JSONObject result = new JSONObject(); + result.put("content", template.getJSONObject("content")); + result.put("id", template.getString("id")); + result.put("name", template.getString("name")); + result.put("requireSignatureAuth", template.getInt("require_signature_auth") != 0); + return result; + } + + private static Map jsonObjectToMap(JSONObject object) { + Map map = new HashMap(); + for (Iterator keys = object.keys(); keys.hasNext();) { + String key = keys.next(); + map.put(key, jsonValueToJava(object.get(key))); + } + + return map; + } + + private static Object jsonValueToJava(Object value) { + if (value == JSONObject.NULL) { + return null; + } + + if (value instanceof JSONObject) { + return jsonObjectToMap((JSONObject) value); + } + + if (value instanceof JSONArray) { + JSONArray array = (JSONArray) value; + List list = new ArrayList(); + for (int index = 0; index < array.length(); index += 1) { + list.add(jsonValueToJava(array.get(index))); + } + + return list; + } + + return value; + } + + private static void writeResult(JSONObject result) throws Exception { + String resultPath = System.getenv("API2_SDK_EXAMPLE_RESULT"); + if (resultPath == null || resultPath.isEmpty()) { + return; + } + + Files.write( + Paths.get(resultPath), + (result.toString(2) + "\n").getBytes(StandardCharsets.UTF_8)); + } + + private Api2DevdockTemplateLifecycle() { + throw new IllegalStateException("Utility class"); + } +} diff --git a/settings.gradle b/settings.gradle index 6110b74..dc30395 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ // Uncomment following line if you want to use the local java-sdk // for the example instead of pulling the JARs from JCenter. // This is useful for debugging and testing new features. -// include ':examples' -rootProject.name = 'transloadit' \ No newline at end of file +include ':examples' +rootProject.name = 'transloadit' diff --git a/src/main/java/com/transloadit/sdk/Transloadit.java b/src/main/java/com/transloadit/sdk/Transloadit.java index f48a433..323fce1 100644 --- a/src/main/java/com/transloadit/sdk/Transloadit.java +++ b/src/main/java/com/transloadit/sdk/Transloadit.java @@ -418,6 +418,29 @@ public Template newTemplate(String name) { return new Template(this, name); } + /** + * Creates a template. + * + * @param options a Map of options to create. + * @return {@link Response} + * + * @throws RequestException if request to transloadit server fails. + * @throws LocalOperationException if something goes wrong while running non-http operations. + */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + + public Response createTemplate(Map options) + throws RequestException, LocalOperationException { + Request request = new Request(this); + return new Response(request.post("/templates", options)); + } + + // + /** * Returns a single template. * From 13f4bb78a6110f357add896678a7a49cb1e53d1a Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Tue, 2 Jun 2026 04:18:32 +0200 Subject: [PATCH 03/10] Add API2 TUS assembly example --- examples/build.gradle | 6 + .../examples/Api2DevdockTusAssembly.java | 181 ++++++++++++++++++ .../java/com/transloadit/sdk/Transloadit.java | 23 +++ 3 files changed, 210 insertions(+) create mode 100644 examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java diff --git a/examples/build.gradle b/examples/build.gradle index 3426c6d..5c31b8f 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -16,6 +16,7 @@ repositories { dependencies { implementation rootProject + implementation 'io.tus.java.client:tus-java-client:0.5.1' implementation 'org.json:json:20231013' } buildscript { @@ -43,3 +44,8 @@ tasks.register('api2DevdockTemplateLifecycle', JavaExec) { classpath = sourceSets.main.runtimeClasspath mainClass = 'com.transloadit.examples.Api2DevdockTemplateLifecycle' } + +tasks.register('api2DevdockTusAssembly', JavaExec) { + classpath = sourceSets.main.runtimeClasspath + mainClass = 'com.transloadit.examples.Api2DevdockTusAssembly' +} diff --git a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java new file mode 100644 index 0000000..8fe55ef --- /dev/null +++ b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java @@ -0,0 +1,181 @@ +package com.transloadit.examples; + +import com.transloadit.sdk.Transloadit; +import com.transloadit.sdk.response.AssemblyResponse; +import io.tus.java.client.TusClient; +import io.tus.java.client.TusUpload; +import io.tus.java.client.TusUploader; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.ByteArrayInputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Runs API2's contract-owned TUS Assembly scenario against devdock. + */ +public final class Api2DevdockTusAssembly { + /** + * Runs the TUS Assembly scenario. + * + * @param args ignored + * @throws Exception if the scenario cannot be completed + */ + public static void main(String[] args) throws Exception { + JSONObject scenario = loadScenario(); + Transloadit transloadit = new Transloadit( + requiredEnv("TRANSLOADIT_KEY"), + requiredEnv("TRANSLOADIT_SECRET"), + requiredEnv("TRANSLOADIT_ENDPOINT")); + + JSONObject createRequest = scenario.getJSONObject("createTusAssembly").getJSONObject("request"); + AssemblyResponse created = transloadit.createAssembly( + jsonObjectToMap(createRequest.getJSONObject("normalizedParams")), + jsonObjectToStringMap(createRequest.getJSONObject("formFields"))); + JSONObject createResponse = created.json(); + + String uploadUrl = uploadScenarioBytes(scenario, createResponse); + + JSONObject result = new JSONObject(); + result.put("createResponse", createResponse); + result.put("uploadUrl", uploadUrl); + writeResult(result); + + System.out.println("Java SDK devdock scenario " + scenario.getString("scenarioId") + + " uploaded to " + uploadUrl); + } + + private static JSONObject loadScenario() throws Exception { + String scenarioPath = System.getenv("API2_SDK_EXAMPLE_SCENARIO"); + if (scenarioPath == null || scenarioPath.isEmpty()) { + scenarioPath = "examples/api2-devdock-tus-assembly/api2-scenario.json"; + } + + byte[] contents = Files.readAllBytes(Paths.get(scenarioPath)); + return new JSONObject(new String(contents, StandardCharsets.UTF_8)); + } + + private static String requiredEnv(String name) { + String value = System.getenv(name); + if (value == null || value.isEmpty()) { + throw new IllegalStateException(name + " must be set"); + } + + return value; + } + + private static String uploadScenarioBytes(JSONObject scenario, JSONObject createResponse) throws Exception { + JSONObject uploadConfig = scenario.getJSONObject("upload"); + JSONObject source = uploadConfig.getJSONObject("source"); + byte[] bytes = source.getString("value").getBytes(StandardCharsets.UTF_8); + + TusClient tusClient = new TusClient(); + tusClient.setUploadCreationURL(new URL(createResponse.getString("tus_url"))); + + TusUpload upload = new TusUpload(); + upload.setInputStream(new ByteArrayInputStream(bytes)); + upload.setSize(bytes.length); + upload.setFingerprint("api2-devdock-java-sdk-" + createResponse.getString("assembly_id")); + upload.setMetadata(uploadMetadata(scenario, createResponse)); + + TusUploader uploader = tusClient.createUpload(upload); + uploader.setChunkSize(bytes.length); + while (uploader.uploadChunk() > -1) { + // Upload the single scenario-owned source until tus-java-client reports completion. + } + uploader.finish(false); + + return uploader.getUploadURL().toString(); + } + + private static Map uploadMetadata( + JSONObject scenario, + JSONObject createResponse) { + Map metadata = new HashMap(); + JSONArray fields = scenario.getJSONObject("upload").getJSONArray("metadata"); + for (int index = 0; index < fields.length(); index += 1) { + JSONObject field = fields.getJSONObject(index); + metadata.put(field.getString("name"), String.valueOf(resolveScenarioValue( + field.getJSONObject("value"), + scenario, + createResponse))); + } + + return metadata; + } + + private static Object resolveScenarioValue( + JSONObject value, + JSONObject scenario, + JSONObject createResponse) { + if (value.has("value")) { + return value.get("value"); + } + + JSONObject source = value.getJSONObject("source"); + Object current; + if ("scenario".equals(source.getString("root"))) { + current = scenario; + } else if ("createResponse".equals(source.getString("root"))) { + current = createResponse; + } else { + throw new IllegalStateException("Unsupported scenario value root: " + source.getString("root")); + } + + JSONArray path = source.getJSONArray("path"); + for (int index = 0; index < path.length(); index += 1) { + if (!(current instanceof JSONObject)) { + throw new IllegalStateException("Cannot resolve scenario path through non-object value"); + } + + current = ((JSONObject) current).get(path.getString(index)); + } + + return current; + } + + private static Map jsonObjectToMap(JSONObject object) { + Map map = new HashMap(); + for (Iterator keys = object.keys(); keys.hasNext();) { + String key = keys.next(); + Object value = object.get(key); + if (value instanceof JSONObject) { + value = jsonObjectToMap((JSONObject) value); + } + map.put(key, value); + } + + return map; + } + + private static Map jsonObjectToStringMap(JSONObject object) { + Map map = new HashMap(); + for (Iterator keys = object.keys(); keys.hasNext();) { + String key = keys.next(); + map.put(key, String.valueOf(object.get(key))); + } + + return map; + } + + private static void writeResult(JSONObject result) throws Exception { + String resultPath = System.getenv("API2_SDK_EXAMPLE_RESULT"); + if (resultPath == null || resultPath.isEmpty()) { + return; + } + + Files.write( + Paths.get(resultPath), + (result.toString(2) + "\n").getBytes(StandardCharsets.UTF_8)); + } + + private Api2DevdockTusAssembly() { + throw new IllegalStateException("Utility class"); + } +} diff --git a/src/main/java/com/transloadit/sdk/Transloadit.java b/src/main/java/com/transloadit/sdk/Transloadit.java index 323fce1..1edfc00 100644 --- a/src/main/java/com/transloadit/sdk/Transloadit.java +++ b/src/main/java/com/transloadit/sdk/Transloadit.java @@ -319,6 +319,29 @@ public Assembly newAssembly() { return new Assembly(this); } + /** + * Creates an assembly. + * + * @param options a Map of options to create. + * @param extraData extra form data to create the assembly with. + * @return {@link AssemblyResponse} + * @throws RequestException if request to transloadit server fails. + * @throws LocalOperationException if something goes wrong while running non-http operations. + */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + + public AssemblyResponse createAssembly(Map options, Map extraData) + throws RequestException, LocalOperationException { + Request request = new Request(this); + return new AssemblyResponse(request.post("/assemblies", options, extraData, null, null)); + } + + // + /** * Returns a single assembly. * From 449c229e2d0964621247784ea24cbc6a148322e0 Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Tue, 2 Jun 2026 04:37:10 +0200 Subject: [PATCH 04/10] Generate createTusAssembly feature --- .../examples/Api2DevdockTusAssembly.java | 33 +++---------------- .../java/com/transloadit/sdk/Transloadit.java | 29 ++++++++++++++++ 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java index 8fe55ef..049c8e1 100644 --- a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java +++ b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java @@ -14,7 +14,6 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; /** @@ -34,10 +33,10 @@ public static void main(String[] args) throws Exception { requiredEnv("TRANSLOADIT_SECRET"), requiredEnv("TRANSLOADIT_ENDPOINT")); - JSONObject createRequest = scenario.getJSONObject("createTusAssembly").getJSONObject("request"); - AssemblyResponse created = transloadit.createAssembly( - jsonObjectToMap(createRequest.getJSONObject("normalizedParams")), - jsonObjectToStringMap(createRequest.getJSONObject("formFields"))); + int fileCount = scenario.getJSONObject("createTusAssembly") + .getJSONObject("input") + .getInt("file_count"); + AssemblyResponse created = transloadit.createTusAssembly(fileCount); JSONObject createResponse = created.json(); String uploadUrl = uploadScenarioBytes(scenario, createResponse); @@ -140,30 +139,6 @@ private static Object resolveScenarioValue( return current; } - private static Map jsonObjectToMap(JSONObject object) { - Map map = new HashMap(); - for (Iterator keys = object.keys(); keys.hasNext();) { - String key = keys.next(); - Object value = object.get(key); - if (value instanceof JSONObject) { - value = jsonObjectToMap((JSONObject) value); - } - map.put(key, value); - } - - return map; - } - - private static Map jsonObjectToStringMap(JSONObject object) { - Map map = new HashMap(); - for (Iterator keys = object.keys(); keys.hasNext();) { - String key = keys.next(); - map.put(key, String.valueOf(object.get(key))); - } - - return map; - } - private static void writeResult(JSONObject result) throws Exception { String resultPath = System.getenv("API2_SDK_EXAMPLE_RESULT"); if (resultPath == null || resultPath.isEmpty()) { diff --git a/src/main/java/com/transloadit/sdk/Transloadit.java b/src/main/java/com/transloadit/sdk/Transloadit.java index 1edfc00..6ae5e30 100644 --- a/src/main/java/com/transloadit/sdk/Transloadit.java +++ b/src/main/java/com/transloadit/sdk/Transloadit.java @@ -342,6 +342,35 @@ public AssemblyResponse createAssembly(Map options, Map + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + + /** + * Creates a TUS-ready Assembly that waits for the requested number of resumable uploads + * before execution continues. + */ + public AssemblyResponse createTusAssembly(int fileCount) + throws RequestException, LocalOperationException { + Map options = new HashMap(); + options.put("await", false); + Map optionsSteps = new HashMap(); + Map optionsStepsOriginal = new HashMap(); + optionsStepsOriginal.put("output_meta", true); + optionsStepsOriginal.put("result", "debug"); + optionsStepsOriginal.put("robot", "/upload/handle"); + optionsSteps.put(":original", optionsStepsOriginal); + options.put("steps", optionsSteps); + Map extraData = new HashMap(); + extraData.put("num_expected_upload_files", String.valueOf(fileCount)); + + return createAssembly(options, extraData); + } + + // + /** * Returns a single assembly. * From 00137321be12d59ea438c95aad46fc1f80493d54 Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Tue, 2 Jun 2026 04:58:59 +0200 Subject: [PATCH 05/10] Generate waitForAssembly feature --- .../examples/Api2DevdockTusAssembly.java | 6 ++- .../java/com/transloadit/sdk/Transloadit.java | 47 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java index 049c8e1..02c8dae 100644 --- a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java +++ b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java @@ -40,14 +40,18 @@ public static void main(String[] args) throws Exception { JSONObject createResponse = created.json(); String uploadUrl = uploadScenarioBytes(scenario, createResponse); + AssemblyResponse completed = transloadit.waitForAssembly( + createResponse.getString("assembly_ssl_url")); + JSONObject status = completed.json(); JSONObject result = new JSONObject(); result.put("createResponse", createResponse); + result.put("status", status); result.put("uploadUrl", uploadUrl); writeResult(result); System.out.println("Java SDK devdock scenario " + scenario.getString("scenarioId") - + " uploaded to " + uploadUrl); + + " uploaded to " + uploadUrl + " and finished with " + status.getString("ok")); } private static JSONObject loadScenario() throws Exception { diff --git a/src/main/java/com/transloadit/sdk/Transloadit.java b/src/main/java/com/transloadit/sdk/Transloadit.java index 6ae5e30..24d9282 100644 --- a/src/main/java/com/transloadit/sdk/Transloadit.java +++ b/src/main/java/com/transloadit/sdk/Transloadit.java @@ -400,12 +400,59 @@ public AssemblyResponse getAssembly(String id) throws RequestException, LocalOpe * @throws RequestException if request to transloadit server fails. * @throws LocalOperationException if something goes wrong while running non-http operations. */ + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + public AssemblyResponse getAssemblyByUrl(String url) throws RequestException, LocalOperationException { Request request = new Request(this); return new AssemblyResponse(request.get(url)); } + // + + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + + /** + * Wait for an Assembly to finish uploading and executing. + * The assembly URL should be the assembly_ssl_url returned by createAssembly. + */ + public AssemblyResponse waitForAssembly(String assemblyUrl) + throws RequestException, LocalOperationException { + List responsePollValues = java.util.Arrays.asList( + "ASSEMBLY_UPLOADING", "ASSEMBLY_EXECUTING"); + while (true) { + AssemblyResponse response = getAssemblyByUrl(assemblyUrl); + org.json.JSONObject responseJson = response.json(); + + // Abort polling if the assembly has entered an error state + if (!responseJson.optString("error").isEmpty()) { + return response; + } + + // The polling is done if the assembly is not uploading or executing anymore. + if (!(responsePollValues.contains(responseJson.optString("ok")))) { + return response; + } + + try { + Thread.sleep(1000); + } catch (InterruptedException error) { + Thread.currentThread().interrupt(); + throw new LocalOperationException(error); + } + } + } + + // + /** * cancels a running assembly. * From 9dd6431b387025eab01486ac282430631c16753e Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Tue, 2 Jun 2026 05:37:05 +0200 Subject: [PATCH 06/10] Read TUS scenario preparations generically --- .../examples/Api2DevdockTusAssembly.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java index 02c8dae..0154a57 100644 --- a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java +++ b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java @@ -33,7 +33,11 @@ public static void main(String[] args) throws Exception { requiredEnv("TRANSLOADIT_SECRET"), requiredEnv("TRANSLOADIT_ENDPOINT")); - int fileCount = scenario.getJSONObject("createTusAssembly") + int fileCount = featureStep( + scenario, + "preparations", + "createTusAssembly", + "feature-call") .getJSONObject("input") .getInt("file_count"); AssemblyResponse created = transloadit.createTusAssembly(fileCount); @@ -64,6 +68,29 @@ private static JSONObject loadScenario() throws Exception { return new JSONObject(new String(contents, StandardCharsets.UTF_8)); } + private static JSONObject featureStep( + JSONObject scenario, + String collectionName, + String featureId, + String kind) { + JSONArray steps = scenario.getJSONArray(collectionName); + for (int index = 0; index < steps.length(); index += 1) { + JSONObject step = steps.getJSONObject(index); + if (!featureId.equals(step.getString("featureId"))) { + continue; + } + if (!kind.equals(step.getString("kind"))) { + throw new IllegalStateException(collectionName + "[" + index + + "] must have kind " + kind); + } + + return step; + } + + throw new IllegalStateException("Scenario has no " + collectionName + + " step for feature " + featureId); + } + private static String requiredEnv(String name) { String value = System.getenv(name); if (value == null || value.isEmpty()) { From 381ef81e18ff7e400467f5370905ca6b24fad493 Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Tue, 2 Jun 2026 07:24:52 +0200 Subject: [PATCH 07/10] Generate TUS assembly upload helper --- .../examples/Api2DevdockTusAssembly.java | 102 ++++------------- .../java/com/transloadit/sdk/Transloadit.java | 108 ++++++++++++++++++ .../sdk/UploadTusAssemblyResult.java | 24 ++++ 3 files changed, 156 insertions(+), 78 deletions(-) create mode 100644 src/main/java/com/transloadit/sdk/UploadTusAssemblyResult.java diff --git a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java index 0154a57..978b47b 100644 --- a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java +++ b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java @@ -1,19 +1,15 @@ package com.transloadit.examples; import com.transloadit.sdk.Transloadit; -import com.transloadit.sdk.response.AssemblyResponse; -import io.tus.java.client.TusClient; -import io.tus.java.client.TusUpload; -import io.tus.java.client.TusUploader; +import com.transloadit.sdk.UploadTusAssemblyResult; import org.json.JSONArray; import org.json.JSONObject; -import java.io.ByteArrayInputStream; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; /** @@ -40,22 +36,27 @@ public static void main(String[] args) throws Exception { "feature-call") .getJSONObject("input") .getInt("file_count"); - AssemblyResponse created = transloadit.createTusAssembly(fileCount); - JSONObject createResponse = created.json(); - String uploadUrl = uploadScenarioBytes(scenario, createResponse); - AssemblyResponse completed = transloadit.waitForAssembly( - createResponse.getString("assembly_ssl_url")); - JSONObject status = completed.json(); + JSONObject uploadConfig = scenario.getJSONObject("upload"); + JSONObject source = uploadConfig.getJSONObject("source"); + byte[] bytes = source.getString("value").getBytes(StandardCharsets.UTF_8); + UploadTusAssemblyResult uploadResult = transloadit.uploadTusAssembly( + fileCount, + bytes, + uploadConfig.getString("fieldName"), + uploadConfig.getString("fileName"), + uploadUserMeta(uploadConfig)); + JSONObject status = uploadResult.getAssembly().json(); JSONObject result = new JSONObject(); - result.put("createResponse", createResponse); + result.put("createResponse", status); result.put("status", status); - result.put("uploadUrl", uploadUrl); + result.put("uploadUrl", uploadResult.getUploadUrl()); writeResult(result); System.out.println("Java SDK devdock scenario " + scenario.getString("scenarioId") - + " uploaded to " + uploadUrl + " and finished with " + status.getString("ok")); + + " uploaded to " + uploadResult.getUploadUrl() + + " and finished with " + status.getString("ok")); } private static JSONObject loadScenario() throws Exception { @@ -100,74 +101,19 @@ private static String requiredEnv(String name) { return value; } - private static String uploadScenarioBytes(JSONObject scenario, JSONObject createResponse) throws Exception { - JSONObject uploadConfig = scenario.getJSONObject("upload"); - JSONObject source = uploadConfig.getJSONObject("source"); - byte[] bytes = source.getString("value").getBytes(StandardCharsets.UTF_8); - - TusClient tusClient = new TusClient(); - tusClient.setUploadCreationURL(new URL(createResponse.getString("tus_url"))); - - TusUpload upload = new TusUpload(); - upload.setInputStream(new ByteArrayInputStream(bytes)); - upload.setSize(bytes.length); - upload.setFingerprint("api2-devdock-java-sdk-" + createResponse.getString("assembly_id")); - upload.setMetadata(uploadMetadata(scenario, createResponse)); - - TusUploader uploader = tusClient.createUpload(upload); - uploader.setChunkSize(bytes.length); - while (uploader.uploadChunk() > -1) { - // Upload the single scenario-owned source until tus-java-client reports completion. - } - uploader.finish(false); - - return uploader.getUploadURL().toString(); - } - - private static Map uploadMetadata( - JSONObject scenario, - JSONObject createResponse) { + private static Map uploadUserMeta(JSONObject uploadConfig) { Map metadata = new HashMap(); - JSONArray fields = scenario.getJSONObject("upload").getJSONArray("metadata"); - for (int index = 0; index < fields.length(); index += 1) { - JSONObject field = fields.getJSONObject(index); - metadata.put(field.getString("name"), String.valueOf(resolveScenarioValue( - field.getJSONObject("value"), - scenario, - createResponse))); - } - - return metadata; - } - - private static Object resolveScenarioValue( - JSONObject value, - JSONObject scenario, - JSONObject createResponse) { - if (value.has("value")) { - return value.get("value"); - } - - JSONObject source = value.getJSONObject("source"); - Object current; - if ("scenario".equals(source.getString("root"))) { - current = scenario; - } else if ("createResponse".equals(source.getString("root"))) { - current = createResponse; - } else { - throw new IllegalStateException("Unsupported scenario value root: " + source.getString("root")); + if (!uploadConfig.has("userMeta")) { + return metadata; } - JSONArray path = source.getJSONArray("path"); - for (int index = 0; index < path.length(); index += 1) { - if (!(current instanceof JSONObject)) { - throw new IllegalStateException("Cannot resolve scenario path through non-object value"); - } - - current = ((JSONObject) current).get(path.getString(index)); + JSONObject userMeta = uploadConfig.getJSONObject("userMeta"); + for (Iterator keys = userMeta.keys(); keys.hasNext();) { + String key = keys.next(); + metadata.put(key, String.valueOf(userMeta.get(key))); } - return current; + return metadata; } private static void writeResult(JSONObject result) throws Exception { diff --git a/src/main/java/com/transloadit/sdk/Transloadit.java b/src/main/java/com/transloadit/sdk/Transloadit.java index 24d9282..89c84bf 100644 --- a/src/main/java/com/transloadit/sdk/Transloadit.java +++ b/src/main/java/com/transloadit/sdk/Transloadit.java @@ -371,6 +371,114 @@ public AssemblyResponse createTusAssembly(int fileCount) // + // + + // This block is generated from Transloadit API2 contracts. If it looks wrong, + // please report the issue instead of editing this block by hand; the source fix + // belongs in the contract generator so all SDKs stay in sync. + + /** + * Create a TUS-ready Assembly, upload one file with the TUS protocol, and wait for the Assembly to finish. + */ + public UploadTusAssemblyResult uploadTusAssembly(int fileCount, byte[] content, String fieldname, String filename, Map userMeta) + throws RequestException, LocalOperationException { + AssemblyResponse createdAssembly = createTusAssembly(fileCount); + + java.net.URL endpointUrl; + try { + endpointUrl = new java.net.URL(createdAssembly.getTusUrl()); + } catch (java.net.MalformedURLException error) { + throw new LocalOperationException(error); + } + + Map metadataMap = new HashMap(); + if (userMeta != null) { + for (Map.Entry entry : userMeta.entrySet()) { + metadataMap.put(entry.getKey(), entry.getValue()); + } + } + metadataMap.put("assembly_url", String.valueOf(createdAssembly.getUrl())); + metadataMap.put("fieldname", String.valueOf(fieldname)); + metadataMap.put("filename", String.valueOf(filename)); + + okhttp3.OkHttpClient httpClient = new okhttp3.OkHttpClient(); + + String uploadUrlText; + okhttp3.Request.Builder createRequestBuilder = new okhttp3.Request.Builder() + .url(endpointUrl) + .method("POST", okhttp3.RequestBody.create(null, new byte[0])); + createRequestBuilder.addHeader("Tus-Resumable", "1.0.0"); + createRequestBuilder.addHeader("Upload-Length", String.valueOf(content.length)); + List createMetadataParts = new ArrayList(); + for (Map.Entry entry : metadataMap.entrySet()) { + createMetadataParts.add(entry.getKey() + " " + java.util.Base64.getEncoder().encodeToString(entry.getValue().getBytes(StandardCharsets.UTF_8))); + } + createRequestBuilder.addHeader("Upload-Metadata", String.join(",", createMetadataParts)); + okhttp3.Request createRequest = createRequestBuilder.build(); + + okhttp3.Response createResponse; + try { + createResponse = httpClient.newCall(createRequest).execute(); + } catch (java.io.IOException error) { + throw new RequestException(error); + } + try { + if (createResponse.code() != 201) { + throw new RequestException(String.format("TUS create returned HTTP %d, expected 201", createResponse.code())); + } + String uploadUrlLocation = createResponse.header("Location"); + if (uploadUrlLocation == null || uploadUrlLocation.isEmpty()) { + throw new RequestException("TUS create did not return a Location header"); + } + java.net.URL uploadUrl; + try { + uploadUrl = new java.net.URL(endpointUrl, uploadUrlLocation); + } catch (java.net.MalformedURLException error) { + throw new LocalOperationException(error); + } + uploadUrlText = uploadUrl.toString(); + } finally { + createResponse.close(); + } + + okhttp3.Request.Builder uploadRequestBuilder = new okhttp3.Request.Builder() + .url(uploadUrlText) + .method("PATCH", okhttp3.RequestBody.create(null, content)); + uploadRequestBuilder.addHeader("Tus-Resumable", "1.0.0"); + uploadRequestBuilder.addHeader("Upload-Offset", "0"); + uploadRequestBuilder.addHeader("Content-Type", "application/offset+octet-stream"); + okhttp3.Request uploadRequest = uploadRequestBuilder.build(); + + okhttp3.Response uploadResponse; + try { + uploadResponse = httpClient.newCall(uploadRequest).execute(); + } catch (java.io.IOException error) { + throw new RequestException(error); + } + try { + if (uploadResponse.code() != 204) { + throw new RequestException(String.format("TUS upload returned HTTP %d, expected 204", uploadResponse.code())); + } + int remoteOffset; + try { + remoteOffset = Integer.parseInt(uploadResponse.header("Upload-Offset")); + } catch (NumberFormatException error) { + throw new LocalOperationException(error); + } + if (remoteOffset != content.length) { + throw new RequestException(String.format("TUS upload offset %d, expected %d", remoteOffset, content.length)); + } + } finally { + uploadResponse.close(); + } + + AssemblyResponse completedAssembly = waitForAssembly(createdAssembly.getSslUrl()); + + return new UploadTusAssemblyResult(completedAssembly, uploadUrlText); + } + + // + /** * Returns a single assembly. * diff --git a/src/main/java/com/transloadit/sdk/UploadTusAssemblyResult.java b/src/main/java/com/transloadit/sdk/UploadTusAssemblyResult.java new file mode 100644 index 0000000..78d522b --- /dev/null +++ b/src/main/java/com/transloadit/sdk/UploadTusAssemblyResult.java @@ -0,0 +1,24 @@ +package com.transloadit.sdk; + +import com.transloadit.sdk.response.AssemblyResponse; + +/** + * Result returned after uploading one file through a TUS Assembly. + */ +public class UploadTusAssemblyResult { + private final AssemblyResponse assembly; + private final String uploadUrl; + + public UploadTusAssemblyResult(AssemblyResponse assembly, String uploadUrl) { + this.assembly = assembly; + this.uploadUrl = uploadUrl; + } + + public AssemblyResponse getAssembly() { + return assembly; + } + + public String getUploadUrl() { + return uploadUrl; + } +} From 8df12dbd3694506efc7264f012acf03d9d8b9588 Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Wed, 3 Jun 2026 01:01:35 +0200 Subject: [PATCH 08/10] Use header-derived TUS offset variable --- src/main/java/com/transloadit/sdk/Transloadit.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/transloadit/sdk/Transloadit.java b/src/main/java/com/transloadit/sdk/Transloadit.java index 89c84bf..ecb6e8a 100644 --- a/src/main/java/com/transloadit/sdk/Transloadit.java +++ b/src/main/java/com/transloadit/sdk/Transloadit.java @@ -459,14 +459,14 @@ public UploadTusAssemblyResult uploadTusAssembly(int fileCount, byte[] content, if (uploadResponse.code() != 204) { throw new RequestException(String.format("TUS upload returned HTTP %d, expected 204", uploadResponse.code())); } - int remoteOffset; + int uploadOffset; try { - remoteOffset = Integer.parseInt(uploadResponse.header("Upload-Offset")); + uploadOffset = Integer.parseInt(uploadResponse.header("Upload-Offset")); } catch (NumberFormatException error) { throw new LocalOperationException(error); } - if (remoteOffset != content.length) { - throw new RequestException(String.format("TUS upload offset %d, expected %d", remoteOffset, content.length)); + if (uploadOffset != content.length) { + throw new RequestException(String.format("TUS upload offset %d, expected %d", uploadOffset, content.length)); } } finally { uploadResponse.close(); From df3c8e38b4c49ff27b03548a5a3f352322c7d1f3 Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Wed, 3 Jun 2026 05:05:48 +0200 Subject: [PATCH 09/10] Read TUS example input from SDK feature call --- .../examples/Api2DevdockTusAssembly.java | 49 +++++++------------ 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java index 978b47b..189a907 100644 --- a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java +++ b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java @@ -29,22 +29,16 @@ public static void main(String[] args) throws Exception { requiredEnv("TRANSLOADIT_SECRET"), requiredEnv("TRANSLOADIT_ENDPOINT")); - int fileCount = featureStep( - scenario, - "preparations", - "createTusAssembly", - "feature-call") - .getJSONObject("input") - .getInt("file_count"); - - JSONObject uploadConfig = scenario.getJSONObject("upload"); - JSONObject source = uploadConfig.getJSONObject("source"); - byte[] bytes = source.getString("value").getBytes(StandardCharsets.UTF_8); + JSONObject input = sdkFeatureCall(scenario, "uploadTusAssembly").getJSONObject("input"); + int fileCount = input.getInt("file_count"); + + JSONObject uploadConfig = input.getJSONObject("upload"); + byte[] bytes = uploadConfig.getString("content").getBytes(StandardCharsets.UTF_8); UploadTusAssemblyResult uploadResult = transloadit.uploadTusAssembly( fileCount, bytes, - uploadConfig.getString("fieldName"), - uploadConfig.getString("fileName"), + uploadConfig.getString("fieldname"), + uploadConfig.getString("filename"), uploadUserMeta(uploadConfig)); JSONObject status = uploadResult.getAssembly().json(); @@ -69,27 +63,22 @@ private static JSONObject loadScenario() throws Exception { return new JSONObject(new String(contents, StandardCharsets.UTF_8)); } - private static JSONObject featureStep( - JSONObject scenario, - String collectionName, - String featureId, - String kind) { - JSONArray steps = scenario.getJSONArray(collectionName); - for (int index = 0; index < steps.length(); index += 1) { - JSONObject step = steps.getJSONObject(index); - if (!featureId.equals(step.getString("featureId"))) { + private static JSONObject sdkFeatureCall(JSONObject scenario, String featureId) { + JSONArray featureCalls = scenario.getJSONArray("sdkFeatureCalls"); + for (int index = 0; index < featureCalls.length(); index += 1) { + JSONObject featureCall = featureCalls.getJSONObject(index); + if (!featureId.equals(featureCall.getString("featureId"))) { continue; } - if (!kind.equals(step.getString("kind"))) { - throw new IllegalStateException(collectionName + "[" + index - + "] must have kind " + kind); + if (!"sdk-feature-call".equals(featureCall.getString("kind"))) { + throw new IllegalStateException("sdkFeatureCalls[" + index + + "] must have kind sdk-feature-call"); } - return step; + return featureCall; } - throw new IllegalStateException("Scenario has no " + collectionName - + " step for feature " + featureId); + throw new IllegalStateException("Scenario has no SDK feature call for feature " + featureId); } private static String requiredEnv(String name) { @@ -103,11 +92,11 @@ private static String requiredEnv(String name) { private static Map uploadUserMeta(JSONObject uploadConfig) { Map metadata = new HashMap(); - if (!uploadConfig.has("userMeta")) { + if (!uploadConfig.has("user_meta")) { return metadata; } - JSONObject userMeta = uploadConfig.getJSONObject("userMeta"); + JSONObject userMeta = uploadConfig.getJSONObject("user_meta"); for (Iterator keys = userMeta.keys(); keys.hasNext();) { String key = keys.next(); metadata.put(key, String.valueOf(userMeta.get(key))); From 174fcacdf33501c8638c0d7f61974c39a454b12c Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Wed, 3 Jun 2026 21:01:01 +0200 Subject: [PATCH 10/10] Read SDK example input projection --- .../examples/Api2DevdockTusAssembly.java | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java index 189a907..53ba376 100644 --- a/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java +++ b/examples/src/main/java/com/transloadit/examples/Api2DevdockTusAssembly.java @@ -2,7 +2,6 @@ import com.transloadit.sdk.Transloadit; import com.transloadit.sdk.UploadTusAssemblyResult; -import org.json.JSONArray; import org.json.JSONObject; import java.nio.charset.StandardCharsets; @@ -29,7 +28,10 @@ public static void main(String[] args) throws Exception { requiredEnv("TRANSLOADIT_SECRET"), requiredEnv("TRANSLOADIT_ENDPOINT")); - JSONObject input = sdkFeatureCall(scenario, "uploadTusAssembly").getJSONObject("input"); + JSONObject exampleInput = scenario.getJSONObject("exampleInput"); + JSONObject input = exampleInput + .getJSONObject("sdkFeatureInputs") + .getJSONObject("uploadTusAssembly"); int fileCount = input.getInt("file_count"); JSONObject uploadConfig = input.getJSONObject("upload"); @@ -48,7 +50,7 @@ public static void main(String[] args) throws Exception { result.put("uploadUrl", uploadResult.getUploadUrl()); writeResult(result); - System.out.println("Java SDK devdock scenario " + scenario.getString("scenarioId") + System.out.println("Java SDK devdock scenario " + exampleInput.getString("scenarioId") + " uploaded to " + uploadResult.getUploadUrl() + " and finished with " + status.getString("ok")); } @@ -63,24 +65,6 @@ private static JSONObject loadScenario() throws Exception { return new JSONObject(new String(contents, StandardCharsets.UTF_8)); } - private static JSONObject sdkFeatureCall(JSONObject scenario, String featureId) { - JSONArray featureCalls = scenario.getJSONArray("sdkFeatureCalls"); - for (int index = 0; index < featureCalls.length(); index += 1) { - JSONObject featureCall = featureCalls.getJSONObject(index); - if (!featureId.equals(featureCall.getString("featureId"))) { - continue; - } - if (!"sdk-feature-call".equals(featureCall.getString("kind"))) { - throw new IllegalStateException("sdkFeatureCalls[" + index - + "] must have kind sdk-feature-call"); - } - - return featureCall; - } - - throw new IllegalStateException("Scenario has no SDK feature call for feature " + featureId); - } - private static String requiredEnv(String name) { String value = System.getenv(name); if (value == null || value.isEmpty()) {