All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.finos.tracdap.svc.meta.services.MetadataWriteService Maven / Gradle / Ivy

Go to download

TRAC D.A.P. metadata service, stateless service component to answer the TRAC metadata API

There is a newer version: 0.7.0
Show newest version
/*
 * Copyright 2022 Accenture Global Solutions Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.finos.tracdap.svc.meta.services;

import org.finos.tracdap.api.MetadataWriteRequest;
import org.finos.tracdap.api.UniversalMetadataWriteBatchRequest;
import org.finos.tracdap.api.UniversalMetadataWriteBatchResponse;
import org.finos.tracdap.metadata.*;
import org.finos.tracdap.common.auth.internal.AuthHelpers;
import org.finos.tracdap.common.auth.internal.UserInfo;
import org.finos.tracdap.common.metadata.MetadataCodec;
import org.finos.tracdap.common.metadata.MetadataConstants;
import org.finos.tracdap.common.validation.Validator;
import org.finos.tracdap.svc.meta.dal.IMetadataDal;
import org.finos.tracdap.svc.meta.dal.operations.*;

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
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.OBJECT_FIRST_VERSION;
import static org.finos.tracdap.common.metadata.MetadataConstants.TAG_FIRST_VERSION;


public class MetadataWriteService {

    private final Validator validator = new Validator();
    private final IMetadataDal dal;

    public MetadataWriteService(IMetadataDal dal) {
        this.dal = dal;
    }

    private static class WriteOperation {
        DalWriteOperation writeOperation;
        List tagHeaders;
    }

    public UniversalMetadataWriteBatchResponse writeBatch(
            UniversalMetadataWriteBatchRequest request
    ) {
        var tenant = request.getTenant();
        var writeOperations = new ArrayList();
        var resultBuilder = UniversalMetadataWriteBatchResponse.newBuilder();

        if (request.getPreallocateObjectsCount() > 0) {
            var requests = request.getPreallocateObjectsList();
            var opers = createPreallocatedObjectsWriteOperation(requests, Collections.emptyList());
            resultBuilder.addAllPreallocatedObjectHeaders(opers.tagHeaders);
            writeOperations.add(opers);
        }

        if (request.getCreateObjectsCount() > 0) {
            var requests = request.getCreateObjectsList();
            var opers = createObjectsWriteOperation(
                    requests,
                    Collections.emptyList()
            );
            resultBuilder.addAllCreateObjectHeaders(opers.tagHeaders);
            writeOperations.add(opers);
        }

        if (request.getUpdateObjectsCount() > 0) {
            var requests = request.getUpdateObjectsList();
            var opers = updateObjectsWriteOperation(
                    tenant,
                    requests,
                    Collections.emptyList()
            );
            resultBuilder.addAllUpdateObjectHeaders(opers.tagHeaders);
            writeOperations.add(opers);
        }

        if (request.getUpdateTagsCount() > 0) {
            var requests = request.getUpdateTagsList();
            var opers = updateTagsWriteOperation(
                    tenant,
                    requests,
                    Collections.emptyList()
            );
            resultBuilder.addAllUpdateTagHeaders(opers.tagHeaders);
            writeOperations.add(opers);
        }

        dal.runWriteOperations(
                tenant,
                writeOperations.stream().map(w -> w.writeOperation).collect(Collectors.toList())
        );

        return resultBuilder.build();
    }

    private List executeWriteOperation(String tenant, WriteOperation oper) {
        dal.runWriteOperations(tenant, Collections.singletonList(oper.writeOperation));
        return oper.tagHeaders;
    }

    public List createObjects(
            String tenant,
            List requests,
            List batchTagUpdates) {
        return executeWriteOperation(
                tenant,
                createObjectsWriteOperation(
                        requests,
                        batchTagUpdates
                )
        );
    }

    private WriteOperation createObjectsWriteOperation(
            List requests,
            List batchTagUpdates) {
        var newTags = new ArrayList();
        for (var request : requests) {

            var tag = prepareCreateObject(
                    UUID.randomUUID(),
                    request.getDefinition(),
                    getTagUpdatesInsideBatch(request, batchTagUpdates)
            );
            newTags.add(tag);
        }

        var result = new WriteOperation();
        result.tagHeaders = newTags.stream().map(Tag::getHeader).collect(Collectors.toList());
        result.writeOperation = new SaveNewObject(newTags);
        return result;
    }

    private Tag prepareCreateObject(UUID objectId, ObjectDefinition definition, List tagUpdates) {

        var userInfo = AuthHelpers.currentUser();
        var userId = userInfo.getUserId();
        var userName = userInfo.getDisplayName();
        var timestamp = Instant.now().atOffset(ZoneOffset.UTC);

        var newHeader = TagHeader.newBuilder()
                .setObjectType(definition.getObjectType())
                .setObjectId(objectId.toString())
                .setObjectVersion(OBJECT_FIRST_VERSION)
                .setObjectTimestamp(MetadataCodec.encodeDatetime(timestamp))
                .setTagVersion(TAG_FIRST_VERSION)
                .setTagTimestamp(MetadataCodec.encodeDatetime(timestamp))
                .setIsLatestTag(true)
                .setIsLatestObject(true)
                .build();

        var newTag = Tag.newBuilder()
                .setHeader(newHeader)
                .setDefinition(definition)
                .build();

        newTag = TagUpdateService.applyTagUpdates(newTag, tagUpdates);

        // Apply the common controlled trac_ tags for newly created objects
        var createAttrs = commonCreateAttrs(timestamp, userId, userName);
        var updateAttrs = commonUpdateAttrs(timestamp, userId, userName);
        newTag = TagUpdateService.applyTagUpdates(newTag, createAttrs);
        newTag = TagUpdateService.applyTagUpdates(newTag, updateAttrs);

        return newTag;
    }

    public List updateObjects(
            String tenant,
            List requests,
            List batchTagUpdates) {

        return executeWriteOperation(
                tenant,
                updateObjectsWriteOperation(
                        tenant,
                        requests,
                        batchTagUpdates
                )
        );
    }

    private WriteOperation updateObjectsWriteOperation(
            String tenant,
            List requests,
            List batchTagUpdates) {

        var userInfo = AuthHelpers.currentUser();

        var priorVersions = requests.stream()
                .map(MetadataWriteRequest::getPriorVersion)
                .collect(Collectors.toList());
        var priorTags = dal.loadObjects(tenant, priorVersions);

        var newTags = new ArrayList();
        for (int i = 0; i < requests.size(); i++) {
            var request = requests.get(i);

            var newTag = prepareUpdateObject(
                    userInfo,
                    priorTags.get(i),
                    request.getDefinition(),
                    getTagUpdatesInsideBatch(request, batchTagUpdates)
            );
            newTags.add(newTag);
        }

        var result = new WriteOperation();
        result.tagHeaders = newTags.stream().map(Tag::getHeader).collect(Collectors.toList());
        result.writeOperation = new SaveNewVersion(newTags);
        return result;
    }

    private Tag prepareUpdateObject(UserInfo userInfo, Tag priorTag, ObjectDefinition definition, List tagUpdates) {
        // Validate version increment on the object
        validator.validateVersion(definition, priorTag.getDefinition());

        var userId = userInfo.getUserId();
        var userName = userInfo.getDisplayName();
        var timestamp = Instant.now().atOffset(ZoneOffset.UTC);

        var oldHeader = priorTag.getHeader();

        var newHeader = oldHeader.toBuilder()
                .setObjectVersion(oldHeader.getObjectVersion() + 1)
                .setObjectTimestamp(MetadataCodec.encodeDatetime(timestamp))
                .setTagVersion(TAG_FIRST_VERSION)
                .setTagTimestamp(MetadataCodec.encodeDatetime(timestamp))
                .setIsLatestTag(true)
                .setIsLatestObject(true)
                .build();

        var newTag = priorTag.toBuilder()
                .setHeader(newHeader)
                .setDefinition(definition)
                .build();

        newTag = TagUpdateService.applyTagUpdates(newTag, tagUpdates);

        // Apply the common controlled trac_ tags for updated objects
        var commonAttrs = commonUpdateAttrs(timestamp, userId, userName);
        newTag = TagUpdateService.applyTagUpdates(newTag, commonAttrs);

        return newTag;
    }

    public List updateTagBatch(
            String tenant,
            List requests,
            List batchTagUpdates) {

        return executeWriteOperation(
                tenant,
                updateTagsWriteOperation(
                        tenant,
                        requests,
                        batchTagUpdates
                )
        );
    }

    private WriteOperation updateTagsWriteOperation(
            String tenant,
            List requests,
            List batchTagUpdates) {

        var priorVersions = requests.stream()
                .map(MetadataWriteRequest::getPriorVersion)
                .collect(Collectors.toList());
        var priorTags = dal.loadObjects(tenant, priorVersions);

        var newTags = new ArrayList();
        for (int i = 0; i < requests.size(); i++) {
            var request = requests.get(i);

            var tag = prepareUpdateTag(
                    priorTags.get(i),
                    getTagUpdatesInsideBatch(request, batchTagUpdates)
            );
            newTags.add(tag);
        }


        var result = new WriteOperation();
        result.tagHeaders = newTags.stream().map(Tag::getHeader).collect(Collectors.toList());
        result.writeOperation = new SaveNewTag(newTags);
        return result;
    }

    private static Tag prepareUpdateTag(Tag priorTag, List tagUpdates) {
        var timestamp = Instant.now().atOffset(ZoneOffset.UTC);

        var oldHeader = priorTag.getHeader();

        var newHeader = oldHeader.toBuilder()
                .setTagVersion(oldHeader.getTagVersion() + 1)
                .setTagTimestamp(MetadataCodec.encodeDatetime(timestamp))
                .setIsLatestTag(true)
                .build();

        var newTag = priorTag.toBuilder()
                .setHeader(newHeader)
                .build();

        newTag = TagUpdateService.applyTagUpdates(newTag, tagUpdates);
        return newTag;
    }

    public List preallocateIdBatch(String tenant, List objectTypes) {
        var objectIds = objectTypes.stream().map(objectType -> UUID.randomUUID()).collect(Collectors.toList());

        dal.preallocateObjectIds(tenant, objectTypes, objectIds);

        var tagHeaders = new ArrayList();
        for (int i = 0; i < objectTypes.size(); i++) {
            var tagHeader = TagHeader.newBuilder()
                    .setObjectType(objectTypes.get(i))
                    .setObjectId(objectIds.get(i).toString())
                    .build();
            tagHeaders.add(tagHeader);
        }
        return tagHeaders;
    }

    public List createPreallocatedObjectBatch(
            String tenant,
            List requests,
            List batchTagUpdates) {
        return executeWriteOperation(
                tenant,
                createPreallocatedObjectsWriteOperation(
                        requests, batchTagUpdates
                )
        );
    }

    private WriteOperation createPreallocatedObjectsWriteOperation(
            List requests,
            List batchTagUpdates) {

        var tags = new ArrayList();
        var uuids = new ArrayList();

        for (var request : requests) {
            var objectId = UUID.fromString(request.getPriorVersion().getObjectId());
            var tag = prepareCreateObject(
                    objectId,
                    request.getDefinition(),
                    getTagUpdatesInsideBatch(request, batchTagUpdates)
            );
            tags.add(tag);
            uuids.add(objectId);
        }

        var result = new WriteOperation();
        result.tagHeaders = tags.stream().map(Tag::getHeader).collect(Collectors.toList());
        result.writeOperation = new SavePreallocatedObject(tags);
        return result;
    }

    private static List getTagUpdatesInsideBatch(MetadataWriteRequest r, List batchTagUpdates) {
        var tagUpdates = new ArrayList<>(r.getTagUpdatesList());
        tagUpdates.addAll(batchTagUpdates);

        return tagUpdates;
    }

    private List commonCreateAttrs(
            OffsetDateTime createTime,
            String createUserId,
            String createUserName) {

        var createTimeAttr = TagUpdate.newBuilder()
                .setAttrName(MetadataConstants.TRAC_CREATE_TIME)
                .setValue(MetadataCodec.encodeValue(createTime))
                .build();

        var createUserIdAttr = TagUpdate.newBuilder()
                .setAttrName(MetadataConstants.TRAC_CREATE_USER_ID)
                .setValue(MetadataCodec.encodeValue(createUserId))
                .build();

        var createUserNameAttr = TagUpdate.newBuilder()
                .setAttrName(MetadataConstants.TRAC_CREATE_USER_NAME)
                .setValue(MetadataCodec.encodeValue(createUserName))
                .build();

        return List.of(createTimeAttr, createUserIdAttr, createUserNameAttr);
    }

    private List commonUpdateAttrs(
            OffsetDateTime createTime,
            String createUserId,
            String createUserName) {

        var updateTimeAttr = TagUpdate.newBuilder()
                .setAttrName(MetadataConstants.TRAC_UPDATE_TIME)
                .setValue(MetadataCodec.encodeValue(createTime))
                .build();

        var updateUserIdAttr = TagUpdate.newBuilder()
                .setAttrName(MetadataConstants.TRAC_UPDATE_USER_ID)
                .setValue(MetadataCodec.encodeValue(createUserId))
                .build();

        var updateUserNameAttr = TagUpdate.newBuilder()
                .setAttrName(MetadataConstants.TRAC_UPDATE_USER_NAME)
                .setValue(MetadataCodec.encodeValue(createUserName))
                .build();

        return List.of(updateTimeAttr, updateUserIdAttr, updateUserNameAttr);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy