Repository: finos/tracdap (language: python)

Issue:
0.6 API Updates - Remove unused platform APIs
There are duplicate APIs available for metadata batch writes. We recently added writeBatch() in the metadata API (TracMetadataApi and TrustedMetadataApi). Before this we had several separate batch methods - createObjectBatch(), updateObjectBatch() etc. This ticket is to remove the old APIs, so we will only have one writeBatch() method which can handle all types of write operation.

The change is needed in both TracMetadataApi and TrustedMetadataApi. The service .proto files need to be modified, the the uneeded implementations and service methods can be removed.
0.6 API Updates - Remove unused platform APIs
There are duplicate APIs available for metadata batch writes. We recently added writeBatch() in the metadata API (TracMetadataApi and TrustedMetadataApi). Before this we had several separate batch methods - createObjectBatch(), updateObjectBatch() etc. This ticket is to remove the old APIs, so we will only have one writeBatch() method which can handle all types of write operation.

The change is needed in both TracMetadataApi and TrustedMetadataApi. The service .proto files need to be modified, the the uneeded implementations and service methods can be removed.


Discussion / hints:

Fix patch:
diff --git a/tracdap-api/tracdap-services/src/main/proto/tracdap/api/metadata.proto b/tracdap-api/tracdap-services/src/main/proto/tracdap/api/metadata.proto
index 95d3572df..7d91e8d0d 100644
--- a/tracdap-api/tracdap-services/src/main/proto/tracdap/api/metadata.proto
+++ b/tracdap-api/tracdap-services/src/main/proto/tracdap/api/metadata.proto
@@ -160,40 +160,6 @@ service TracMetadataApi {
         };
     }
 
-    /**
-     * Create new objects in batch in the TRAC metadata store.
-     *
-     * To create an object, supply a metadata write request with the tenant code,
-     * object type and a definition for the item you want to save. The tag should contain
-     * all the attributes you want to set and the object definition, without an
-     * object header. TRAC will create a header when it assigns and ID for the new
-     * object. Only FLOW and CUSTOM objects can be created by this API call.
-     *
-     * Validation is performed on new objects before they are saved. Objects can
-     * fail validation because they are semantically invalid (e.g. missing
-     * required fields or inconsistencies within the object definition) or due to
-     * consistency errors (referring to other metadata items that don't exist or
-     * don't meet certain requirements).
-     *
-     * NOTE: Validation is only partially implemented in the current release.
-     *
-     * The call returns an ID responses to indicate the IDs of the newly created
-     * objects, as well as the objects and tag versions (which will always be 1).
-     *
-     * Error conditions include: Invalid request, unknown tenant, object type does
-     * not match the supplied definition, validation failure.
-     *
-     * @see TracMetadataApi.createObject()
-     * @see tracdap.metadata.ObjectDefinition
-     * @see tracdap.metadata.TagUpdate
-     */
-    rpc createObjectBatch (MetadataWriteBatchRequest) returns (MetadataWriteBatchResponse) {
-        option (google.api.http) = {
-            post: "/{tenant}/create-object-batch"
-            body: "*"
-        };
-    }
-
     /**
      * Update an existing object in the TRAC metadata store.
      *
@@ -232,44 +198,6 @@ service TracMetadataApi {
         };
     }
 
-    /**
-     * Update existing objects in batch in the TRAC metadata store.
-     *
-     * To save a new version, supply a metadata write request with the tenant code,
-     * object type and a tag for the item you want to save. The tag should contain
-     * all the attributes you want to set and the updated object definition, which
-     * must be supplied with the header from the *previous* version of the object.
-     * TRAC will apply the new version number when it creates the new version of
-     * the object. Only the latest version of an object can be updated and only
-     * CUSTOM objects can be updated by this API call.
-     *
-     * Validation is performed on new objects before they are saved. Objects can
-     * fail validation because they are semantically invalid (e.g. missing
-     * required fields or inconsistencies within the object definition) or due to
-     * consistency errors (referring to other metadata items that don't exist or
-     * don't meet certain requirements). For new versions, validation also covers
-     * checking for compatibility with the previous version of the object.
-     *
-     * NOTE: Validation is only partially implemented in the current release.
-     *
-     * The call returns an ID response with the ID object and the newly assigned
-     * version number, as well as the tag version (which will always be 1).
-     *
-     * Error conditions include: Invalid request, unknown tenant, unknown object
-     * ID or version, wrong object type (not the same as the prior version), object
-     * version is superseded.
-     *
-     * @see TracMetadataApi.updateObject()
-     * @see tracdap.metadata.ObjectDefinition
-     * @see tracdap.metadata.TagUpdate
-     */
-    rpc updateObjectBatch (MetadataWriteBatchRequest) returns (MetadataWriteBatchResponse) {
-        option (google.api.http) = {
-            post: "/{tenant}/update-object-batch"
-            body: "*"
-        };
-    }
-
     /**
      * Update the tag for an existing object in the TRAC metadata store.
      *
@@ -298,35 +226,6 @@ service TracMetadataApi {
         };
     }
 
-    /**
-     * Update tags in batch for existing objects in the TRAC metadata store.
-     *
-     * To save a new tag, supply a metadata write request with the tenant code,
-     * object type and a tag for the item you want to save. The tag should contain
-     * all the attributes you want to include in the new tag, even if they have
-     * not been changed. The object definition must be supplied with a header only,
-     * supplying an object body is an invalid request. Tags for any type of object
-     * can be updated by this API call. Only the latest version of a tag can be
-     * updated.
-     *
-     * The call returns an ID response with the ID and version of the object and
-     * the newly assigned tag number.
-     *
-     * Error conditions include: Invalid request, unknown tenant, unknown object
-     * ID, object version or tag version, wrong object type (does not match what
-     * is stored in TRAC), tag version is superseded.
-     *
-     * @see TracMetadataApi.updateTag()
-     * @see tracdap.metadata.ObjectDefinition
-     * @see tracdap.metadata.TagUpdate
-     */
-    rpc updateTagBatch (MetadataWriteBatchRequest) returns (MetadataWriteBatchResponse) {
-        option (google.api.http) = {
-            post: "/{tenant}/update-batch-tag"
-            body: "*"
-        };
-    }
-
     /**
      * Read a single object from the TRAC metadata store using a tag selector.
      *
@@ -646,6 +545,7 @@ message UniversalMetadataWriteBatchRequest {
     repeated MetadataWriteRequest createObjects = 3;
     repeated MetadataWriteRequest updateObjects = 4;
     repeated MetadataWriteRequest updateTags = 5;
+    repeated MetadataWriteRequest preallocateIds = 6;
 };
 
 
@@ -681,6 +581,7 @@ message MetadataWriteBatchResponse {
  * @see UniversalMetadataWriteBatchRequest
  */
 message UniversalMetadataWriteBatchResponse {
+     repeated metadata.TagHeader preallocatedIdHeaders = 1;
      repeated metadata.TagHeader preallocatedObjectHeaders = 2;
      repeated metadata.TagHeader createObjectHeaders = 3;
      repeated metadata.TagHeader updateObjectHeaders = 4;
diff --git a/tracdap-api/tracdap-services/src/main/proto/tracdap/api/metadata_trusted.proto b/tracdap-api/tracdap-services/src/main/proto/tracdap/api/metadata_trusted.proto
index 648562fc2..c5d35c2e0 100644
--- a/tracdap-api/tracdap-services/src/main/proto/tracdap/api/metadata_trusted.proto
+++ b/tracdap-api/tracdap-services/src/main/proto/tracdap/api/metadata_trusted.proto
@@ -72,22 +72,6 @@ service TrustedMetadataApi {
     };
   }
 
-  /**
-   * Create new objects in the TRAC metadata store.
-   *
-   * This call behaves identically to the equivalent public API call, without the
-   * restriction on which types of object can be saved.
-   *
-   * @see TracMetadataApi.createObjectBatch()
-   * @see TracMetadataApi.createObject()
-   */
-  rpc createObjectBatch (MetadataWriteBatchRequest) returns (MetadataWriteBatchResponse) {
-    option (google.api.http) = {
-      post: "/{tenant}/trusted/create-object-batch"
-      body: "*"
-    };
-  }
-
   /**
    * Update an existing object in the TRAC metadata store.
    *
@@ -104,23 +88,6 @@ service TrustedMetadataApi {
     };
   }
 
-  /**
-   * Update existing objects in the TRAC metadata store.
-   *
-   * This call behaves identically to the equivalent public API call, however it
-   * can be used with any type of object that supports versioning (currently DATA
-   * and CUSTOM).
-   *
-   * @see TracMetadataApi.updateObjectBatch()
-   * @see TracMetadataApi.updateObject()
-   */
-  rpc updateObjectBatch (MetadataWriteBatchRequest) returns (MetadataWriteBatchResponse) {
-    option (google.api.http) = {
-      post: "/{tenant}/trusted/update-object-batch"
-      body: "*"
-    };
-  }
-
   /**
    * Update the tag for an existing object in the TRAC metadata store.
    *
@@ -135,23 +102,6 @@ service TrustedMetadataApi {
     };
   }
 
-  /**
-   * Update tags for existing objects in the TRAC metadata store.
-   *
-   * This call behaves identically to the equivalent public API call, however it
-   * can be used with any type of object that supports versioning (currently DATA
-   * and CUSTOM).
-   *
-   * @see TracMetadataApi.updateTagBatch()
-   * @see TracMetadataApi.updateTag()
-   */
-  rpc updateTagBatch (MetadataWriteBatchRequest) returns (MetadataWriteBatchResponse) {
-    option (google.api.http) = {
-      post: "/{tenant}/trusted/update-batch-tag"
-      body: "*"
-    };
-  }
-
   /**
    * Preallocate an object ID for an object that will be created later.
    *
@@ -209,27 +159,6 @@ service TrustedMetadataApi {
     };
   }
 
-  /**
-   * Create objects in batch using IDs that were previously preallocated.
-   *
-   * This call behaves essentially the same as saveNewObject(), with all the
-   * same validation. The only difference is that the new definition must be
-   * supplied with an object header, including the object ID returned from a
-   * prior call to preallocateId().
-   *
-   * Error conditions include all the error conditions for saveNewObject().
-   * Using a preallocated ID twice, attempting to save to an unknown ID or the
-   * ID of another object that already exists are also errors.
-   *
-   * @see TrustedMetadataApi.createPreallocatedObject()
-   */
-  rpc createPreallocatedObjectBatch (MetadataWriteBatchRequest) returns (MetadataWriteBatchResponse) {
-    option (google.api.http) = {
-      post: "/{tenant}/trusted/create-preallocated-batch"
-      body: "*"
-    };
-  }
-
   /**
    * Read a single object from the TRAC metadata store using a tag selector.
    *
diff --git a/tracdap-libs/tracdap-lib-validation/src/main/java/org/finos/tracdap/common/validation/api/MetadataApiValidator.java b/tracdap-libs/tracdap-lib-validation/src/main/java/org/finos/tracdap/common/validation/api/MetadataApiValidator.java
index 68a5e7761..e64c89850 100644
--- a/tracdap-libs/tracdap-lib-validation/src/main/java/org/finos/tracdap/common/validation/api/MetadataApiValidator.java
+++ b/tracdap-libs/tracdap-lib-validation/src/main/java/org/finos/tracdap/common/validation/api/MetadataApiValidator.java
@@ -56,7 +56,6 @@ public class MetadataApiValidator {
     private static final Descriptors.Descriptor BATCH_WRITE_REQUEST;
     private static final Descriptors.FieldDescriptor BWR_TENANT;
     private static final Descriptors.FieldDescriptor BWR_REQUESTS;
-    private static final Descriptors.FieldDescriptor BWR_BATCH_ATTRS;
 
     private static final Descriptors.Descriptor METADATA_SEARCH_REQUEST;
     private static final Descriptors.FieldDescriptor MSR_TENANT;
@@ -92,7 +91,6 @@ public class MetadataApiValidator {
         BATCH_WRITE_REQUEST = MetadataWriteBatchRequest.getDescriptor();
         BWR_TENANT = field(BATCH_WRITE_REQUEST, MetadataWriteBatchRequest.TENANT_FIELD_NUMBER);
         BWR_REQUESTS = field(BATCH_WRITE_REQUEST, MetadataWriteBatchRequest.REQUESTS_FIELD_NUMBER);
-        BWR_BATCH_ATTRS = field(BATCH_WRITE_REQUEST, MetadataWriteBatchRequest.BATCHATTRS_FIELD_NUMBER);
 
         METADATA_SEARCH_REQUEST = MetadataSearchRequest.getDescriptor();
         MSR_TENANT = field(METADATA_SEARCH_REQUEST, MetadataSearchRequest.TENANT_FIELD_NUMBER);
@@ -169,33 +167,6 @@ static ValidationContext createObject(MetadataWriteRequest msg, ValidationContex
         return ctx;
     }
 
-    @Validator(method = "createObjectBatch")
-    public static ValidationContext createObjectBatch(MetadataWriteBatchRequest msg, ValidationContext ctx) {
-
-        return createObjectBatch(msg, ctx, PUBLIC_API);
-    }
-
-    static ValidationContext createObjectBatch(MetadataWriteBatchRequest msg, ValidationContext ctx, boolean apiTrust) {
-        ctx = ctx.push(BWR_TENANT)
-                .apply(CommonValidators::required)
-                .apply(CommonValidators::identifier)
-                .pop();
-
-        ctx = ctx.pushRepeated(BWR_REQUESTS)
-                .apply(CommonValidators::listNotEmpty)
-                .applyRepeated((MetadataWriteRequest r, ValidationContext c) ->
-                        MetadataApiValidator.createObject(r, c, apiTrust, false), MetadataWriteRequest.class)
-                .pop();
-
-        ctx = ctx.pushRepeated(BWR_BATCH_ATTRS)
-                .applyRepeated(TagUpdateValidator::tagUpdate, TagUpdate.class)
-                // Only allow reserved attrs for requests on the trusted API
-                .applyRepeated(TagUpdateValidator::reservedAttrs, TagUpdate.class, (apiTrust == TRUSTED_API))
-                .pop();
-
-        return ctx;
-    }
-
     @Validator(method = "updateObject")
     public static ValidationContext updateObject(MetadataWriteRequest msg, ValidationContext ctx) {
 
@@ -222,33 +193,6 @@ static ValidationContext updateObject(MetadataWriteRequest msg, ValidationContex
         return ctx;
     }
 
-    @Validator(method = "updateObjectBatch")
-    public static ValidationContext updateObjectBatch(MetadataWriteBatchRequest msg, ValidationContext ctx) {
-
-        return updateObjectBatch(msg, ctx, PUBLIC_API);
-    }
-
-    static ValidationContext updateObjectBatch(MetadataWriteBatchRequest msg, ValidationContext ctx, boolean apiTrust) {
-        ctx = ctx.push(BWR_TENANT)
-                .apply(CommonValidators::required)
-                .apply(CommonValidators::identifier)
-                .pop();
-
-        ctx = ctx.pushRepeated(BWR_REQUESTS)
-                .apply(CommonValidators::listNotEmpty)
-                .applyRepeated((MetadataWriteRequest r, ValidationContext c) ->
-                        MetadataApiValidator.updateObject(r, c, apiTrust, false), MetadataWriteRequest.class)
-                .pop();
-
-        ctx = ctx.pushRepeated(BWR_BATCH_ATTRS)
-                .applyRepeated(TagUpdateValidator::tagUpdate, TagUpdate.class)
-                // Only allow reserved attrs for requests on the trusted API
-                .applyRepeated(TagUpdateValidator::reservedAttrs, TagUpdate.class, (apiTrust == TRUSTED_API))
-                .pop();
-
-        return ctx;
-    }
-
     @Validator(method = "updateTag")
     public static ValidationContext updateTag(MetadataWriteRequest msg, ValidationContext ctx) {
 
@@ -274,33 +218,6 @@ static ValidationContext updateTag(MetadataWriteRequest msg, ValidationContext c
         return ctx;
     }
 
-    @Validator(method = "updateTagBatch")
-    public static ValidationContext updateTagBatch(MetadataWriteBatchRequest msg, ValidationContext ctx) {
-
-        return updateTagBatch(msg, ctx, PUBLIC_API);
-    }
-
-    static ValidationContext updateTagBatch(MetadataWriteBatchRequest msg, ValidationContext ctx, boolean apiTrust) {
-        ctx = ctx.push(BWR_TENANT)
-                .apply(CommonValidators::required)
-                .apply(CommonValidators::identifier)
-                .pop();
-
-        ctx = ctx.pushRepeated(BWR_REQUESTS)
-                .apply(CommonValidators::listNotEmpty)
-                .applyRepeated((MetadataWriteRequest r, ValidationContext c) ->
-                        MetadataApiValidator.updateTag(r, c, apiTrust, false), MetadataWriteRequest.class)
-                .pop();
-
-        ctx = ctx.pushRepeated(BWR_BATCH_ATTRS)
-                .applyRepeated(TagUpdateValidator::tagUpdate, TagUpdate.class)
-                // Only allow reserved attrs for requests on the trusted API
-                .applyRepeated(TagUpdateValidator::reservedAttrs, TagUpdate.class, (apiTrust == TRUSTED_API))
-                .pop();
-
-        return ctx;
-    }
-
     static ValidationContext preallocateId(MetadataWriteRequest msg, ValidationContext ctx) {
 
         return preallocateId(msg, ctx, true);
@@ -363,28 +280,6 @@ private static ValidationContext createPreallocatedObject(MetadataWriteRequest m
         return ctx;
     }
 
-    static ValidationContext createPreallocatedObjectBatch(MetadataWriteBatchRequest msg, ValidationContext ctx) {
-
-        ctx = ctx.push(BWR_TENANT)
-                .apply(CommonValidators::required)
-                .apply(CommonValidators::identifier)
-                .pop();
-
-        ctx = ctx.pushRepeated(BWR_REQUESTS)
-                .apply(CommonValidators::listNotEmpty)
-                .applyRepeated((MetadataWriteRequest r, ValidationContext c) ->
-                        MetadataApiValidator.createPreallocatedObject(r, c, false), MetadataWriteRequest.class)
-                .pop();
-
-        ctx = ctx.pushRepeated(BWR_BATCH_ATTRS)
-                .applyRepeated(TagUpdateValidator::tagUpdate, TagUpdate.class)
-                // Only allow reserved attrs for requests on the trusted API
-                .applyRepeated(TagUpdateValidator::reservedAttrs, TagUpdate.class, true)
-                .pop();
-
-        return ctx;
-    }
-
     private static ValidationContext createOrUpdate(ValidationContext ctx, boolean isVersioned, boolean apiTrust, boolean tenantRequired) {
 
         if (tenantRequired) {
diff --git a/tracdap-libs/tracdap-lib-validation/src/main/java/org/finos/tracdap/common/validation/api/MetadataTrustedApiValidator.java b/tracdap-libs/tracdap-lib-validation/src/main/java/org/finos/tracdap/common/validation/api/MetadataTrustedApiValidator.java
index 4b72cc0d7..3aeb3be7d 100644
--- a/tracdap-libs/tracdap-lib-validation/src/main/java/org/finos/tracdap/common/validation/api/MetadataTrustedApiValidator.java
+++ b/tracdap-libs/tracdap-lib-validation/src/main/java/org/finos/tracdap/common/validation/api/MetadataTrustedApiValidator.java
@@ -39,31 +39,16 @@ public static ValidationContext createObject(MetadataWriteRequest msg, Validatio
         return MetadataApiValidator.createObject(msg, ctx, MetadataApiValidator.TRUSTED_API, true);
     }
 
-    @Validator(method = "createObjectBatch")
-    public static ValidationContext createObjectBatch(MetadataWriteBatchRequest msg, ValidationContext ctx) {
-        return MetadataApiValidator.createObjectBatch(msg, ctx, MetadataApiValidator.TRUSTED_API);
-    }
-
     @Validator(method = "updateObject")
     public static ValidationContext updateObject(MetadataWriteRequest msg, ValidationContext ctx) {
         return MetadataApiValidator.updateObject(msg, ctx, MetadataApiValidator.TRUSTED_API, true);
     }
 
-    @Validator(method = "updateObjectBatch")
-    public static ValidationContext updateObjectBatch(MetadataWriteBatchRequest msg, ValidationContext ctx) {
-        return MetadataApiValidator.updateObjectBatch(msg, ctx, MetadataApiValidator.TRUSTED_API);
-    }
-
     @Validator(method = "updateTag")
     public static ValidationContext updateTag(MetadataWriteRequest msg, ValidationContext ctx) {
         return MetadataApiValidator.updateTag(msg, ctx, MetadataApiValidator.TRUSTED_API, true);
     }
 
-    @Validator(method = "updateTagBatch")
-    public static ValidationContext updateTagBatch(MetadataWriteBatchRequest msg, ValidationContext ctx) {
-        return MetadataApiValidator.updateTagBatch(msg, ctx, MetadataApiValidator.TRUSTED_API);
-    }
-
     @Validator(method = "preallocateId")
     public static ValidationContext preallocateId(MetadataWriteRequest msg, ValidationContext ctx) {
         return MetadataApiValidator.preallocateId(msg, ctx);  // always a trusted call
@@ -79,11 +64,6 @@ public static ValidationContext createPreallocatedObject(MetadataWriteRequest ms
         return MetadataApiValidator.createPreallocatedObject(msg, ctx);  // always a trusted call
     }
 
-    @Validator(method = "createPreallocatedObjectBatch")
-    public static ValidationContext createPreallocatedObjectBatch(MetadataWriteBatchRequest msg, ValidationContext ctx) {
-        return MetadataApiValidator.createPreallocatedObjectBatch(msg, ctx);  // always a trusted call
-    }
-
     @Validator(method = "writeBatch")
     public static ValidationContext writeBatch(UniversalMetadataWriteBatchRequest msg, ValidationContext ctx) {
         return MetadataApiValidator.writeBatch(msg, ctx, MetadataApiValidator.TRUSTED_API);
diff --git a/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/MetadataApiImpl.java b/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/MetadataApiImpl.java
index 5b14d4333..f76187565 100644
--- a/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/MetadataApiImpl.java
+++ b/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/MetadataApiImpl.java
@@ -29,13 +29,13 @@
 import io.grpc.Status;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
 import static org.finos.tracdap.common.metadata.MetadataConstants.PUBLIC_WRITABLE_OBJECT_TYPES;
 import static org.finos.tracdap.svc.meta.api.TracMetadataApi.*;
 import static org.finos.tracdap.svc.meta.api.TrustedMetadataApi.CREATE_PREALLOCATED_OBJECT_METHOD;
-import static org.finos.tracdap.svc.meta.api.TrustedMetadataApi.CREATE_PREALLOCATED_OBJECT_BATCH_METHOD;
 import static org.finos.tracdap.svc.meta.api.TrustedMetadataApi.PREALLOCATE_ID_METHOD;
 import static org.finos.tracdap.svc.meta.api.TrustedMetadataApi.PREALLOCATE_ID_BATCH_METHOD;
 import static org.finos.tracdap.svc.meta.services.MetadataConstants.PUBLIC_API;
@@ -90,26 +90,6 @@ TagHeader createObject(MetadataWriteRequest request) {
                 Collections.emptyList()).get(0);
     }
 
-    MetadataWriteBatchResponse createObjectBatch(MetadataWriteBatchRequest request) {
-
-        validateRequest(CREATE_OBJECT_BATCH_METHOD, request);
-
-        var requestsList = request.getRequestsList();
-
-        for (var rq : requestsList) {
-            validateObjectType(rq.getObjectType());
-        }
-
-        var tagHeaders = writeService.createObjects(
-                request.getTenant(),
-                requestsList,
-                request.getBatchAttrsList()
-        );
-        return MetadataWriteBatchResponse.newBuilder()
-                .addAllHeaders(tagHeaders)
-                .build();
-    }
-
     TagHeader updateObject(MetadataWriteRequest request) {
 
         validateRequest(UPDATE_OBJECT_METHOD, request);
@@ -121,26 +101,6 @@ TagHeader updateObject(MetadataWriteRequest request) {
                 Collections.emptyList()).get(0);
     }
 
-    MetadataWriteBatchResponse updateObjectBatch(MetadataWriteBatchRequest request) {
-
-        validateRequest(UPDATE_OBJECT_BATCH_METHOD, request);
-
-        var requestsList = request.getRequestsList();
-
-        for (var rq : requestsList) {
-            validateObjectType(rq.getObjectType());
-        }
-
-        var tagHeaders = writeService.updateObjects(
-                request.getTenant(),
-                requestsList,
-                request.getBatchAttrsList()
-        );
-        return MetadataWriteBatchResponse.newBuilder()
-                .addAllHeaders(tagHeaders)
-                .build();
-    }
-
     private void validateObjectType(ObjectType objectType) {
         if (apiTrustLevel == PUBLIC_API && !PUBLIC_WRITABLE_OBJECT_TYPES.contains(objectType)) {
             var message = String.format("Object type %s cannot be created via the TRAC public API", objectType);
@@ -159,22 +119,6 @@ TagHeader updateTag(MetadataWriteRequest request) {
                 Collections.emptyList()).get(0);
     }
 
-    MetadataWriteBatchResponse updateTagBatch(MetadataWriteBatchRequest request) {
-
-        validateRequest(UPDATE_TAG_BATCH_METHOD, request);
-
-        var requestsList = request.getRequestsList();
-
-        var tagHeaders = writeService.updateTagBatch(
-                request.getTenant(),
-                requestsList,
-                request.getBatchAttrsList()
-        );
-        return MetadataWriteBatchResponse.newBuilder()
-                .addAllHeaders(tagHeaders)
-                .build();
-    }
-
     TagHeader preallocateId(MetadataWriteRequest request) {
 
         validateRequest(PREALLOCATE_ID_METHOD, request);
@@ -210,24 +154,23 @@ TagHeader createPreallocatedObject(MetadataWriteRequest request) {
                 Collections.emptyList()).get(0);
     }
 
-    MetadataWriteBatchResponse createPreallocatedObjectBatch(MetadataWriteBatchRequest request) {
-
-        validateRequest(CREATE_PREALLOCATED_OBJECT_BATCH_METHOD, request);
+    private void validateListForObjectType(List<MetadataWriteRequest> requestsList) {
+        for (var rq : requestsList) {
+            validateObjectType(rq.getObjectType());
+        }
 
-        var tagHeaders = writeService.createPreallocatedObjectBatch(
-                request.getTenant(),
-                request.getRequestsList(),
-                request.getBatchAttrsList()
-        );
-        return MetadataWriteBatchResponse.newBuilder()
-                .addAllHeaders(tagHeaders)
-                .build();
     }
 
     public UniversalMetadataWriteBatchResponse writeBatch(UniversalMetadataWriteBatchRequest request) {
 
         validateRequest(TrustedMetadataApiGrpc.getWriteBatchMethod(), request);
 
+        validateListForObjectType(request.getPreallocateObjectsList());
+        validateListForObjectType(request.getCreateObjectsList());
+        validateListForObjectType(request.getUpdateObjectsList());
+        validateListForObjectType(request.getPreallocateObjectsList());
+        validateListForObjectType(request.getPreallocateIdsList());
+
         return writeService.writeBatch(request);
     }
 
diff --git a/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/TracMetadataApi.java b/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/TracMetadataApi.java
index 35f5cb6e9..3bc04697a 100644
--- a/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/TracMetadataApi.java
+++ b/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/TracMetadataApi.java
@@ -36,11 +36,8 @@ public class TracMetadataApi extends TracMetadataApiGrpc.TracMetadataApiImplBase
     private static final Descriptors.ServiceDescriptor TRAC_METADATA_SERVICE = Metadata.getDescriptor().findServiceByName(SERVICE_NAME);
 
     static final MethodDescriptor<MetadataWriteRequest, TagHeader> CREATE_OBJECT_METHOD = TracMetadataApiGrpc.getCreateObjectMethod();
-    static final MethodDescriptor<MetadataWriteBatchRequest, MetadataWriteBatchResponse> CREATE_OBJECT_BATCH_METHOD = TracMetadataApiGrpc.getCreateObjectBatchMethod();
     static final MethodDescriptor<MetadataWriteRequest, TagHeader> UPDATE_OBJECT_METHOD = TracMetadataApiGrpc.getUpdateObjectMethod();
-    static final MethodDescriptor<MetadataWriteBatchRequest, MetadataWriteBatchResponse> UPDATE_OBJECT_BATCH_METHOD = TracMetadataApiGrpc.getUpdateObjectBatchMethod();
     static final MethodDescriptor<MetadataWriteRequest, TagHeader> UPDATE_TAG_METHOD = TracMetadataApiGrpc.getUpdateTagMethod();
-    static final MethodDescriptor<MetadataWriteBatchRequest, MetadataWriteBatchResponse> UPDATE_TAG_BATCH_METHOD = TracMetadataApiGrpc.getUpdateTagBatchMethod();
 
     static final MethodDescriptor<MetadataReadRequest, Tag> READ_OBJECT_METHOD = TracMetadataApiGrpc.getReadObjectMethod();
     static final MethodDescriptor<MetadataBatchRequest, MetadataBatchResponse> READ_BATCH_METHOD = TracMetadataApiGrpc.getReadBatchMethod();
@@ -89,36 +86,18 @@ public void createObject(MetadataWriteRequest request, StreamObserver<TagHeader>
         grpcWrap.unaryCall(request, response, apiImpl::createObject);
     }
 
-    @Override
-    public void createObjectBatch(MetadataWriteBatchRequest request, StreamObserver<MetadataWriteBatchResponse> response) {
-
-        grpcWrap.unaryCall(request, response, apiImpl::createObjectBatch);
-    }
-
     @Override
     public void updateObject(MetadataWriteRequest request, StreamObserver<TagHeader> response) {
 
         grpcWrap.unaryCall(request, response, apiImpl::updateObject);
     }
 
-    @Override
-    public void updateObjectBatch(MetadataWriteBatchRequest request, StreamObserver<MetadataWriteBatchResponse> response) {
-
-        grpcWrap.unaryCall(request, response, apiImpl::updateObjectBatch);
-    }
-
     @Override
     public void updateTag(MetadataWriteRequest request, StreamObserver<TagHeader> response) {
 
         grpcWrap.unaryCall(request, response, apiImpl::updateTag);
     }
 
-    @Override
-    public void updateTagBatch(MetadataWriteBatchRequest request, StreamObserver<MetadataWriteBatchResponse> response) {
-
-        grpcWrap.unaryCall(request, response, apiImpl::updateTagBatch);
-    }
-
     @Override
     public void readObject(MetadataReadRequest request, StreamObserver<Tag> response) {
 
diff --git a/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/TrustedMetadataApi.java b/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/TrustedMetadataApi.java
index e146f5df9..e23fe11c0 100644
--- a/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/TrustedMetadataApi.java
+++ b/tracdap-services/tracdap-svc-meta/src/main/java/org/finos/tracdap/svc/meta/api/TrustedMetadataApi.java
@@ -38,7 +38,6 @@ public class TrustedMetadataApi extends TrustedMetadataApiGrpc.TrustedMetadataAp
     static final MethodDescriptor<MetadataWriteRequest, TagHeader> PREALLOCATE_ID_METHOD = TrustedMetadataApiGrpc.getPreallocateIdMethod();
     static final MethodDescriptor<MetadataWriteBatchRequest, MetadataWriteBatchResponse> PREALLOCATE_ID_BATCH_METHOD = TrustedMetadataApiGrpc.getPreallocateIdBatchMethod();
     static final MethodDescriptor<MetadataWriteRequest, TagHeader> CREATE_PREALLOCATED_OBJECT_METHOD = TrustedMetadataApiGrpc.getCreatePreallocatedObjectMethod();
-    static final MethodDescriptor<MetadataWriteBatchRequest, MetadataWriteBatchResponse> CREATE_PREALLOCATED_OBJECT_BATCH_METHOD = TrustedMetadataApiGrpc.getCreatePreallocatedObjectBatchMethod();
 
 
 
@@ -69,36 +68,18 @@ public void createObject(MetadataWriteRequest request, StreamObserver<TagHeader>
         grpcWrap.unaryCall(request, response, apiImpl::createObject);
     }
 
-    @Override
-    public void createObjectBatch(MetadataWriteBatchRequest request, StreamObserver<MetadataWriteBatchResponse> response) {
-
-        grpcWrap.unaryCall(request, response, apiImpl::createObjectBatch);
-    }
-
     @Override
     public void updateObject(MetadataWriteRequest request, StreamObserver<TagHeader> response) {
 
         grpcWrap.unaryCall(request, response, apiImpl::updateObject);
     }
 
-    @Override
-    public void updateObjectBatch(MetadataWriteBatchRequest request, StreamObserver<MetadataWriteBatchResponse> response) {
-
-        grpcWrap.unaryCall(request, response, apiImpl::updateObjectBatch);
-    }
-
     @Override
     public void updateTag(MetadataWriteRequest request, StreamObserver<TagHeader> response) {
 
         grpcWrap.unaryCall(request, response, apiImpl::updateTag);
     }
 
-    @Override
-    public void updateTagBatch(MetadataWriteBatchRequest request, StreamObserver<MetadataWriteBatchResponse> response) {
-
-        grpcWrap.unaryCall(request, response, apiImpl::updateTagBatch);
-    }
-
     @Override
     public void preallocateId(MetadataWriteRequest request, StreamObserver<TagHeader> response) {
 
@@ -117,12 +98,6 @@ public void createPreallocatedObject(MetadataWriteRequest request, StreamObserve
         grpcWrap.unaryCall(request, response, apiImpl::createPreallocatedObject);
     }
 
-    @Override
-    public void createPreallocatedObjectBatch(MetadataWriteBatchRequest request, StreamObserver<MetadataWriteBatchResponse> response) {
-
-        grpcWrap.unaryCall(request, response, apiImpl::createPreallocatedObjectBatch);
-    }
-
     @Override
     public void readObject(MetadataReadRequest request, StreamObserver<Tag> response) {
 


Test patch:
diff --git a/tracdap-services/tracdap-svc-meta/src/test/java/org/finos/tracdap/svc/meta/api/MetadataWriteApiTest.java b/tracdap-services/tracdap-svc-meta/src/test/java/org/finos/tracdap/svc/meta/api/MetadataWriteApiTest.java
index 94ec672e8..796c44870 100644
--- a/tracdap-services/tracdap-svc-meta/src/test/java/org/finos/tracdap/svc/meta/api/MetadataWriteApiTest.java
+++ b/tracdap-services/tracdap-svc-meta/src/test/java/org/finos/tracdap/svc/meta/api/MetadataWriteApiTest.java
@@ -356,24 +356,6 @@ void createObject_reservedAttrs() {
     // CREATE BATCH
     // -----------------------------------------------------------------------------------------------------------------
 
-    @ParameterizedTest
-    @EnumSource(value = ObjectType.class, mode = EnumSource.Mode.EXCLUDE,
-            names = {"OBJECT_TYPE_NOT_SET", "UNRECOGNIZED"})
-    void createObjectBatch_trustedTypesOk(ObjectType objectType) {
-
-        createObjectBatch_ok(objectType, request -> trustedApi.createObjectBatch(request));
-    }
-
-    @ParameterizedTest
-    @EnumSource(value = ObjectType.class, mode = EnumSource.Mode.INCLUDE,
-            names = {"SCHEMA", "FLOW", "CUSTOM"})
-    void createObjectBatch_publicTypesOk(ObjectType objectType) {
-
-        // All object types should be either in this test, or publicTypesNotAllowed
-
-        createObjectBatch_ok(objectType, request -> publicApi.createObjectBatch(request));
-    }
-
     @ParameterizedTest
     @EnumSource(value = ObjectType.class, mode = EnumSource.Mode.EXCLUDE,
             names = {"OBJECT_TYPE_NOT_SET", "UNRECOGNIZED", "SCHEMA", "FLOW", "CUSTOM"})
@@ -401,7 +383,7 @@ void createObjectBatch_publicTypesNotAllowed(ObjectType objectType) {
                 .build();
 
         // noinspection ResultOfMethodCallIgnored
-        var error = assertThrows(StatusRuntimeException.class, () -> publicApi.createObjectBatch(writeRequest));
+        var error = assertThrows(StatusRuntimeException.class, () ->  publicApi.writeBatch(universalizeCreateObjectBatchRequest(writeRequest)));
         assertEquals(Status.Code.PERMISSION_DENIED, error.getStatus().getCode());
     }
 
@@ -505,11 +487,11 @@ void createObjectBatch_inconsistentType() {
                 .build();
 
         // noinspection ResultOfMethodCallIgnored
-        var error = assertThrows(StatusRuntimeException.class, () -> trustedApi.createObjectBatch(writeRequest));
+        var error = assertThrows(StatusRuntimeException.class, () -> trustedApi.writeBatch(universalizeCreateObjectBatchRequest(writeRequest)));
         assertEquals(Status.Code.INVALID_ARGUMENT, error.getStatus().getCode());
 
         // noinspection ResultOfMethodCallIgnored
-        var error2 = assertThrows(StatusRuntimeException.class, () -> publicApi.createObjectBatch(writeRequest));
+        var error2 = assertThrows(StatusRuntimeException.class, () -> publicApi.writeBatch(universalizeCreateObjectBatchRequest(writeRequest)));
         assertEquals(Status.Code.INVALID_ARGUMENT, error2.getStatus().getCode());
     }
 
@@ -551,11 +533,11 @@ void createObjectBatch_invalidContent() {
                 .build();
 
         // noinspection ResultOfMethodCallIgnored
-        var error = assertThrows(StatusRuntimeException.class, () -> trustedApi.createObjectBatch(writeRequest));
+        var error = assertThrows(StatusRuntimeException.class, () -> trustedApi.writeBatch(universalizeCreateObjectBatchRequest(writeRequest)));
         assertEquals(Status.Code.INVALID_ARGUMENT, error.getStatus().getCode());
 
         // noinspection ResultOfMethodCallIgnored
-        var error2 = assertThrows(StatusRuntimeException.class, () -> publicApi.createObjectBatch(writeRequest));
+        var error2 = assertThrows(StatusRuntimeException.class, () -> publicApi.writeBatch(universalizeCreateObjectBatchRequest(writeRequest)));
         assertEquals(Status.Code.INVALID_ARGUMENT, error2.getStatus().getCode());
     }
 
@@ -590,11 +572,11 @@ void createObjectBatch_invalidAttrs() {
                 .build();
 
         // noinspection ResultOfMethodCallIgnored
-        var error = assertThrows(StatusRuntimeException.class, () -> publicApi.createObjectBatch(writeRequest));
+        var error = assertThrows(StatusRuntimeException.class, () -> publicApi.writeBatch(universalizeCreateObjectBatchRequest(writeRequest)));
         assertEquals(Status.Code.INVALID_ARGUMENT, error.getStatus().getCode());
 
         // noinspection ResultOfMethodCallIgnored
-        var error2 = assertThrows(StatusRuntimeException.class, () -> trustedApi.createObjectBatch(writeRequest));
+        var error2 = assertThrows(StatusRuntimeException.class, () -> trustedApi.writeBatch(universalizeCreateObjectBatchRequest(writeRequest)));
         assertEquals(Status.Code.INVALID_ARGUMENT, error2.getStatus().getCode());
     }
 
@@ -634,10 +616,10 @@ void createObjectBatch_reservedAttrs() {
         // Then the result would be PERMISSION_DENIED instead
 
         // noinspection ResultOfMethodCallIgnored
-        var error = assertThrows(StatusRuntimeException.class, () -> publicApi.createObjectBatch(writeRequest));
+        var error = assertThrows(StatusRuntimeException.class, () -> publicApi.writeBatch(universalizeCreateObjectBatchRequest(writeRequest)));
         assertEquals(Status.Code.INVALID_ARGUMENT, error.getStatus().getCode());
 
-        assertDoesNotThrow(() -> trustedApi.createObjectBatch(writeRequest));
+        assertDoesNotThrow(() -> trustedApi.writeBatch(universalizeCreateObjectBatchRequest(writeRequest)));
     }
     
     // -----------------------------------------------------------------------------------------------------------------
@@ -1143,7 +1125,7 @@ void updateObject_reservedAttrs() {
             names = {"DATA", "FILE", "STORAGE", "SCHEMA", "CUSTOM"})
     void updateObjectBatch_trustedTypesOk(ObjectType objectType) {
 
-        updateObjectBatch_ok(objectType, request -> trustedApi.updateObjectBatch(request));
+        updateObjectBatch_ok(objectType, request -> trustedApi.writeBatch(request));
     }
 
     // Versioned types that are also publicly writable
@@ -1152,7 +1134,7 @@ void updateObjectBatch_trustedTypesOk(ObjectType objectType) {
             names = {"SCHEMA", "CUSTOM"})
     void updateObjectBatch_publicTypesOk(ObjectType objectType) {
 
-        updateObjectBatch_ok(objectType, request -> publicApi.updateObjectBatch(request));
+        updateObjectBatch_ok(objectType, request -> publicApi.writeBatch(request));
     }
 
     // Versioned types that are not publicly writable
@@ -1177,17 +1159,17 @@ void updateObjectBatch_publicTypesNotAllowed(ObjectType objectType) {
             );
         }
 
-        var v2WriteRequest = MetadataWriteBatchRequest.newBuilder()
+        var v2WriteRequest = UniversalMetadataWriteBatchRequest.newBuilder()
                 .setTenant(TEST_TENANT)
-                .addAllRequests(requests)
+                .addAllUpdateObjects(requests)
                 .build();
 
         // noinspection ResultOfMethodCallIgnored
-        var error = assertThrows(StatusRuntimeException.class, () -> publicApi.updateObjectBatch(v2WriteRequest));
+        var error = assertThrows(StatusRuntimeException.class, () -> publicApi.writeBatch(v2WriteRequest));
         assertEquals(Status.Code.PERMISSION_DENIED, error.getStatus().getCode());
     }
 
-    void updateObjectBatch_ok(ObjectType objectType, Function<MetadataWriteBatchRequest, MetadataWriteBatchResponse> saveApiCall) {
+    void updateObjectBatch_ok(ObjectType objectType, Function<UniversalMetadataWriteBatchRequest, UniversalMetadataWriteBatchResponse> saveApiCall) {
         class RequestData {
             MetadataWriteRequest writeRequest;
             UUID v1ObjectId;
@@ -1224,14 +1206,14 @@ class RequestData {
             requests.add(r);
         }
 
-        var updateRequest = MetadataWriteBatchRequest.newBuilder()
+        var updateRequest = UniversalMetadataWriteBatchRequest.newBuilder()
                 .setTenant(TEST_TENANT)
-                .addAllRequests(
+                .addAllUpdateObjects(
                         requests.stream().map(r -> r.writeRequest).collect(Collectors.toList())
                 )
                 .build();
 
-        var v2TagHeaders = saveApiCall.apply(updateRequest).getHeadersList();
+        var v2TagHeaders = saveApiCall.apply(updateRequest).getUpdateObjectHeadersList();
 
         assertEquals(7, v2TagHeaders.size());
 
@@ -1395,7 +1377,7 @@ void updateTag_isLatestObject_ok() {
                 .setDefinition(v2Obj)
                 .build();
 
-        trustedApi.updateObject(v2WriteRequest);
+        var v2Tag = trustedApi.updateObject(v2WriteRequest);
 
         var v1MetadataReadRequest = MetadataReadRequest.newBuilder()
                 .setTenant(TEST_TENANT)
@@ -1419,6 +1401,7 @@ void updateTag_isLatestObject_ok() {
         var v2ReadObject = readApi.readObject(v2MetadataReadRequest);
 
         assertTrue(v2ReadObject.getHeader().getIsLatestObject());
+        assertTrue(v2Tag.getIsLatestObject());
         assertFalse(v1ReadObject.getHeader().getIsLatestObject());
     }
 
@@ -1441,7 +1424,7 @@ void updateTag_isLatestTag_ok() {
                 .addTagUpdates(t2Update)
                 .build();
 
-        trustedApi.updateTag(v1t2WriteRequest);
+        var v1t2Tag = trustedApi.updateTag(v1t2WriteRequest);
 
         var v1t1MetadataReadRequest = MetadataReadRequest.newBuilder()
                 .setTenant(TEST_TENANT)
@@ -1465,6 +1448,7 @@ void updateTag_isLatestTag_ok() {
         var v1t2ReadHeader = readApi.readObject(v1t2MetadataReadRequest).getHeader();
 
         assertTrue(v1t2ReadHeader.getIsLatestTag());
+        assertTrue(v1t2Tag.getIsLatestTag());
         assertFalse(v1t1ReadHeader.getIsLatestTag());
     }
 
@@ -1487,7 +1471,7 @@ void updateTag_isLatestTagAndObject_ok() {
                 .addTagUpdates(t2Update)
                 .build();
 
-        trustedApi.updateTag(v1t2WriteRequest);
+        var v1t2Tag = trustedApi.updateTag(v1t2WriteRequest);
 
         var v2Obj = TestData.dummyVersionForType(v1SavedTag.getDefinition());
 
@@ -1498,7 +1482,7 @@ void updateTag_isLatestTagAndObject_ok() {
                 .setDefinition(v2Obj)
                 .build();
 
-        trustedApi.updateObject(v2WriteRequest);
+        var v2Tag = trustedApi.updateObject(v2WriteRequest);
 
         var v1t1MetadataReadRequest = MetadataReadRequest.newBuilder()
                 .setTenant(TEST_TENANT)
@@ -1534,7 +1518,8 @@ void updateTag_isLatestTagAndObject_ok() {
         assertTrue(v1t2ReadHeader.getIsLatestTag());
         assertFalse(v1t1ReadHeader.getIsLatestTag());
         assertTrue(v2t1ReadHeader.getIsLatestTag());
-
+        assertTrue(v1t2Tag.getIsLatestTag());
+        assertTrue(v2Tag.getIsLatestObject());
         assertFalse(v1t1ReadHeader.getIsLatestObject());
         assertFalse(v1t2ReadHeader.getIsLatestObject());
         assertTrue(v2t1ReadHeader.getIsLatestObject());
@@ -1905,14 +1890,14 @@ class RequestData {
             requests.add(r);
         }
 
-        var t2WriteRequest = MetadataWriteBatchRequest.newBuilder()
+        var t2WriteRequest = UniversalMetadataWriteBatchRequest.newBuilder()
                 .setTenant(TEST_TENANT)
-                .addAllRequests(
+                .addAllUpdateTags(
                         requests.stream().map(r -> r.writeRequest).collect(Collectors.toList())
                 )
                 .build();
 
-        var t2headers = trustedApi.updateTagBatch(t2WriteRequest).getHeadersList();
+        var t2headers = trustedApi.writeBatch(t2WriteRequest).getUpdateTagHeadersList();
 
         assertEquals(7, t2headers.size());
 
@@ -1948,7 +1933,6 @@ class RequestData {
         // Write tag update via the public API
         for (int i = 0; i < 7; i++) {
             var r = requests.get(i);
-            var t2header = t2headers.get(i);
 
             r.t3AttrName = "extra_attr_v3";
             r.t3Update = TagUpdate.newBuilder()
@@ -1963,14 +1947,14 @@ class RequestData {
                     .build();
         }
 
-        var t3WriteRequest = MetadataWriteBatchRequest.newBuilder()
+        var t3WriteRequest = UniversalMetadataWriteBatchRequest.newBuilder()
                 .setTenant(TEST_TENANT)
-                .addAllRequests(
+                .addAllUpdateTags(
                         requests.stream().map(r -> r.t3WriteRequest).collect(Collectors.toList())
                 )
                 .build();
 
-        var t3Headers = publicApi.updateTagBatch(t3WriteRequest).getHeadersList();
+        var t3Headers = publicApi.writeBatch(t3WriteRequest).getUpdateTagHeadersList();
 
         assertEquals(7, t3Headers.size());
 
@@ -2432,12 +2416,12 @@ class RequestData {
             return r;
         }).collect(Collectors.toList());
 
-        var writeRequest = MetadataWriteBatchRequest.newBuilder()
+        var writeRequest = UniversalMetadataWriteBatchRequest.newBuilder()
                 .setTenant(TEST_TENANT)
-                .addAllRequests(requestsData.stream().map(r -> r.writeRequest).collect(Collectors.toList()))
+                .addAllPreallocateObjects(requestsData.stream().map(r -> r.writeRequest).collect(Collectors.toList()))
                 .build();
 
-        var tagHeaders = trustedApi.createPreallocatedObjectBatch(writeRequest).getHeadersList();
+        var tagHeaders = trustedApi.writeBatch(writeRequest).getPreallocatedObjectHeadersList();
         assertEquals(13, tagHeaders.size());
 
         for (int i = 0; i < 13; i++) {
@@ -2504,13 +2488,13 @@ void preallocateObjectBatch_idNotReserved() {
                     .build();
         }).collect(Collectors.toList());
 
-        var writeBatchRequest = MetadataWriteBatchRequest.newBuilder()
+        var writeBatchRequest = UniversalMetadataWriteBatchRequest.newBuilder()
                 .setTenant(TEST_TENANT)
-                .addAllRequests(writeRequests)
+                .addAllPreallocateObjects(writeRequests)
                 .build();
 
         // noinspection ResultOfMethodCallIgnored
-        var error = assertThrows(StatusRuntimeException.class, () -> trustedApi.createPreallocatedObjectBatch(writeBatchRequest));
+        var error = assertThrows(StatusRuntimeException.class, () -> trustedApi.writeBatch(writeBatchRequest));
         assertEquals(Status.Code.NOT_FOUND, error.getStatus().getCode());
     }