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

org.ehrbase.dao.access.jooq.ContributionAccess Maven / Gradle / Ivy

There is a newer version: 2.12.0
Show newest version
/*
 * Copyright (c) 2019 vitasystems GmbH and Hannover Medical School.
 *
 * This file is part of project EHRbase
 *
 * 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
 *
 *     https://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.ehrbase.dao.access.jooq;

import static org.ehrbase.jooq.pg.Tables.CONTRIBUTION;

import com.nedap.archie.rm.generic.AuditDetails;
import java.sql.Timestamp;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import org.ehrbase.api.definitions.ServerConfig;
import org.ehrbase.api.exception.InternalServerException;
import org.ehrbase.dao.access.interfaces.I_AuditDetailsAccess;
import org.ehrbase.dao.access.interfaces.I_ConceptAccess;
import org.ehrbase.dao.access.interfaces.I_ConceptAccess.ContributionChangeType;
import org.ehrbase.dao.access.interfaces.I_ContributionAccess;
import org.ehrbase.dao.access.interfaces.I_DomainAccess;
import org.ehrbase.dao.access.interfaces.I_SystemAccess;
import org.ehrbase.dao.access.jooq.party.PersistedPartyProxy;
import org.ehrbase.dao.access.support.DataAccess;
import org.ehrbase.dao.access.util.ContributionDef;
import org.ehrbase.dao.access.util.TransactionTime;
import org.ehrbase.ehr.knowledge.I_KnowledgeCache;
import org.ehrbase.jooq.pg.Routines;
import org.ehrbase.jooq.pg.enums.ContributionDataType;
import org.ehrbase.jooq.pg.enums.ContributionState;
import org.ehrbase.jooq.pg.tables.AdminDeleteStatusHistory;
import org.ehrbase.jooq.pg.tables.records.AdminDeleteStatusRecord;
import org.ehrbase.jooq.pg.tables.records.AdminGetLinkedCompositionsForContribRecord;
import org.ehrbase.jooq.pg.tables.records.AdminGetLinkedStatusForContribRecord;
import org.ehrbase.jooq.pg.tables.records.ContributionRecord;
import org.ehrbase.service.IntrospectService;
import org.ehrbase.util.UuidGenerator;
import org.jooq.DSLContext;
import org.jooq.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by Christian Chevalley on 4/17/2015.
 */
public class ContributionAccess extends DataAccess implements I_ContributionAccess {

    Logger log = LoggerFactory.getLogger(ContributionAccess.class);
    private ContributionRecord contributionRecord;
    private I_AuditDetailsAccess auditDetails; // audit associated with this contribution

    /**
     * Basic constructor for contribution.
     * @param context DB context object of current server context
     * @param knowledgeManager Knowledge cache object of current server context
     * @param introspectCache Introspect cache object of current server context
     * @param serverConfig Server config object of current server context
     * @param sysTenant the tenant identifier to which the ContributionAccess object belongs to
     * @param ehrId Given ID of EHR this contribution will be created for
     */
    public ContributionAccess(
            DSLContext context,
            I_KnowledgeCache knowledgeManager,
            IntrospectService introspectCache,
            ServerConfig serverConfig,
            UUID ehrId,
            Short sysTenant) {

        super(context, knowledgeManager, introspectCache, serverConfig);

        contributionRecord = context.newRecord(CONTRIBUTION);
        contributionRecord.setEhrId(ehrId);
        contributionRecord.setSysTenant(sysTenant);

        auditDetails = I_AuditDetailsAccess.getInstance(this.getDataAccess(), sysTenant);
    }

    /**
     * Constructor with convenient {@link I_DomainAccess} parameter, for better readability.
     * @param domainAccess Current domain access object
     * @param ehrId Given ID of EHR this contribution will be created for
     * @param sysTenant the tenant identifier to which the ContributionAccess object belongs to
     */
    public ContributionAccess(I_DomainAccess domainAccess, UUID ehrId, Short sysTenant) {
        this(
                domainAccess.getContext(),
                domainAccess.getKnowledgeManager(),
                domainAccess.getIntrospectService(),
                domainAccess.getServerConfig(),
                ehrId,
                sysTenant);
    }

    // internal minimal constructor - needs proper initialization before following usage
    private ContributionAccess(I_DomainAccess domainAccess, ContributionRecord record, I_AuditDetailsAccess audit) {
        super(domainAccess);
        this.contributionRecord = record;
        this.auditDetails = audit;
    }

    /**
     * @throws InternalServerException on failed fetching of contribution
     */
    public static I_ContributionAccess retrieveInstance(I_DomainAccess domainAccess, UUID contributionId) {
        try {
            return Optional.ofNullable(
                            domainAccess.getContext().fetchOne(CONTRIBUTION, CONTRIBUTION.ID.eq(contributionId)))
                    .map(rec -> new ContributionAccess(
                            domainAccess,
                            rec,
                            new AuditDetailsAccess(domainAccess.getDataAccess(), rec.getSysTenant())
                                    .retrieveInstance(domainAccess.getDataAccess(), rec.getHasAudit())))
                    .orElse(null);
        } catch (Exception e) {
            throw new InternalServerException("fetching contribution failed", e);
        }
    }

    @Override
    public UUID commit(Timestamp transactionTime) {

        // first create DB entry of auditDetails so they can get referenced in this contribution
        UUID auditId = this.auditDetails.commit();
        contributionRecord.setHasAudit(auditId);

        if (contributionRecord.getState() == ContributionState.incomplete) {
            log.warn("Contribution state has not been set");
        }

        contributionRecord.setEhrId(this.getEhrId());
        if (contributionRecord.insert() == 0) throw new InternalServerException("Couldn't store contribution");

        return contributionRecord.getId();
    }

    @Override
    public UUID commit() {
        return commit(TransactionTime.millis());
    }

    /**
     * Commit the contribution with optional values, excluding audit, which needs to be created and set beforehand.
     */
    @Override
    public UUID commit(
            Timestamp transactionTime, ContributionDataType contributionType, ContributionDef.ContributionState state) {

        if (transactionTime == null) {
            transactionTime = TransactionTime.millis();
        }

        // set contribution attributes
        setContributionDataType(Objects.requireNonNullElse(contributionType, ContributionDataType.other));

        setState(Objects.requireNonNullElse(state, ContributionDef.ContributionState.COMPLETE));

        return commit(transactionTime);
    }

    /**
     * Commit the contribution with (optional) given values (incl. audit data, which is handled embedded)
     * @throws InternalServerException when contribution couldn't be created because of an internal problem
     */
    @Override
    public UUID commit(
            Timestamp transactionTime,
            UUID committerId,
            UUID systemId,
            ContributionDataType contributionType,
            ContributionDef.ContributionState state,
            I_ConceptAccess.ContributionChangeType contributionChangeType,
            String description) {
        // create new audit_details instance for this contribution
        this.auditDetails = I_AuditDetailsAccess.getInstance(this.getDataAccess(), contributionRecord.getSysTenant());

        if (transactionTime == null) {
            transactionTime = TransactionTime.millis();
        }

        // set contribution attributes
        setContributionDataType(Objects.requireNonNullElse(contributionType, ContributionDataType.other));

        setState(Objects.requireNonNullElse(state, ContributionDef.ContributionState.COMPLETE));

        // audit attributes
        if (committerId != null) {
            auditDetails.setCommitter(committerId);
        } else {
            throw new InternalServerException("Missing mandatory committer ID");
        }

        if (systemId != null) {
            auditDetails.setSystemId(systemId);
        } else {
            throw new InternalServerException("Missing mandatory system ID");
        }

        if (contributionChangeType != null)
            auditDetails.setChangeType(
                    I_ConceptAccess.fetchContributionChangeType(this, contributionChangeType.name()));
        else
            auditDetails.setChangeType(
                    I_ConceptAccess.fetchContributionChangeType(this, I_ConceptAccess.ContributionChangeType.CREATION));

        if (description != null) {
            auditDetails.setDescription(description);
        }
        return commit(transactionTime);
    }

    @Override
    public Boolean update(
            Timestamp transactionTime,
            UUID committerId,
            UUID systemId,
            String contributionType,
            String contributionState,
            String contributionChangeType,
            String description) {
        // set contribution  attributes
        ContributionDataType type = null;
        ContributionDef.ContributionState state = null;
        I_ConceptAccess.ContributionChangeType changeType = null;

        if (contributionType == null) type = ContributionDataType.valueOf(contributionType);

        if (contributionState != null) state = ContributionDef.ContributionState.valueOf(contributionState);

        if (contributionChangeType != null)
            changeType = I_ConceptAccess.ContributionChangeType.valueOf(contributionChangeType);

        // audit handling will be executed centralized in the following called method
        return update(transactionTime, committerId, systemId, type, state, changeType, description);
    }

    @Override
    public Boolean update(
            Timestamp transactionTime,
            UUID committerId,
            UUID systemId,
            ContributionDataType contributionType,
            ContributionDef.ContributionState state,
            I_ConceptAccess.ContributionChangeType contributionChangeType,
            String description) {
        // set contribution  attributes
        if (contributionType != null) setContributionDataType(contributionType);
        if (state != null) setState(state);

        // embedded audit handling
        this.auditDetails = I_AuditDetailsAccess.getInstance(
                getDataAccess(), contributionRecord.getSysTenant()); // new audit for new action
        if (committerId != null) this.auditDetails.setCommitter(committerId);
        if (systemId != null) this.auditDetails.setSystemId(systemId);
        if (description != null) this.auditDetails.setDescription(description);
        if (contributionChangeType != null)
            this.auditDetails.setChangeType(I_ConceptAccess.fetchContributionChangeType(this, contributionChangeType));

        return update(transactionTime);
    }

    @Override
    public UUID commitWithSignature(String signature) {
        contributionRecord.setSignature(signature);
        contributionRecord.setState(ContributionState.valueOf("complete"));
        contributionRecord.store();

        return contributionRecord.getId();
    }

    @Override
    public UUID updateWithSignature(String signature) {
        contributionRecord.setSignature(signature);
        contributionRecord.setState(ContributionState.valueOf("complete"));
        contributionRecord.update();

        return contributionRecord.getId();
    }

    @Override
    public Boolean update(Timestamp transactionTime) {
        return update(transactionTime, false);
    }

    @Override
    public Boolean update(Timestamp transactionTime, boolean force) {
        boolean updated = false;

        if (force || contributionRecord.changed()) { // TODO-447: test if this creates an own audit for contributions

            if (!contributionRecord.changed()) {
                // hack: force tell jOOQ to perform updateComposition whatever...
                contributionRecord.changed(true);
            }

            // update contribution's audit with modification change type and execute update of it, too
            this.auditDetails.setChangeType(I_ConceptAccess.fetchContributionChangeType(
                    this, I_ConceptAccess.ContributionChangeType.MODIFICATION));
            if (this.auditDetails.update(transactionTime, force).equals(Boolean.FALSE))
                throw new InternalServerException("Couldn't update auditDetails");
            contributionRecord.setHasAudit(this.auditDetails.getId()); // new audit ID

            // execute update of contribution itself
            contributionRecord.setId(UuidGenerator.randomUUID()); // force to create new entry from old values
            updated = contributionRecord.insert() == 1;
        }

        return updated;
    }

    @Override
    public Boolean update() {
        return update(TransactionTime.millis());
    }

    @Override
    public Boolean update(Boolean force) {
        return update(TransactionTime.millis());
    }

    @Override
    public Integer delete() {
        int count = 0;
        // delete contribution record
        count += contributionRecord.delete();

        return count;
    }

    /**
     * @throws InternalServerException on failed fetching of contribution
     */
    public I_ContributionAccess retrieve(UUID id) {
        return retrieveInstance(this, id);
    }

    @Override
    public UUID getContributionId() {
        return contributionRecord.getId();
    }

    @Override
    public void setAuditDetailsChangeType(UUID changeType) {
        auditDetails.setChangeType(changeType);
    }

    @Override
    public ContributionDataType getContributionDataType() {
        return contributionRecord.getContributionType();
    }

    @Override
    public void setContributionDataType(ContributionDataType contributionDataType) {
        contributionRecord.setContributionType(contributionDataType);
    }

    @Override
    public void setState(ContributionDef.ContributionState state) {
        if (state != null) contributionRecord.setState(ContributionState.valueOf(state.getLiteral()));
    }

    @Override
    public void setComplete() {
        contributionRecord.setState(ContributionState.valueOf(ContributionState.complete.getLiteral()));
    }

    @Override
    public void setIncomplete() {
        contributionRecord.setState(ContributionState.valueOf(ContributionState.incomplete.getLiteral()));
    }

    @Override
    public void setDeleted() {
        contributionRecord.setState(ContributionState.valueOf(ContributionState.deleted.getLiteral()));
    }

    @Override
    public void setAuditDetailsValues(
            UUID committer, UUID system, String description, ContributionChangeType changeType) {
        if (committer == null || system == null || changeType == null)
            throw new IllegalArgumentException("arguments not optional");
        auditDetails.setCommitter(committer);
        auditDetails.setSystemId(system);
        auditDetails.setChangeType(I_ConceptAccess.fetchContributionChangeType(this, changeType));

        if (description != null) auditDetails.setDescription(description);
    }

    @Override
    public void setAuditDetailsValues(AuditDetails auditObject) {
        // parse
        UUID committer =
                new PersistedPartyProxy(this).getOrCreate(auditObject.getCommitter(), auditDetails.getSysTenant());
        UUID system = I_SystemAccess.createOrRetrieveInstanceId(this, null, auditObject.getSystemId());
        UUID changeType = I_ConceptAccess.fetchContributionChangeType(
                this, auditObject.getChangeType().getValue());

        // set
        if (committer == null || system == null) throw new IllegalArgumentException("arguments not optional");
        auditDetails.setCommitter(committer);
        auditDetails.setSystemId(system);
        auditDetails.setChangeType(changeType);

        // optional description
        if (auditObject.getDescription() != null)
            auditDetails.setDescription(auditObject.getDescription().getValue());
    }

    @Override
    public void setAuditDetailsCommitter(UUID committer) {
        auditDetails.setCommitter(committer);
    }

    @Override
    public void setAuditDetailsSystemId(UUID system) {
        auditDetails.setSystemId(system);
    }

    @Override
    public void setAuditDetailsDescription(String description) {
        auditDetails.setDescription(description);
    }

    @Override
    public UUID getAuditsCommitter() {
        return auditDetails.getCommitter();
    }

    @Override
    public UUID getAuditsSystemId() {
        return auditDetails.getSystemId();
    }

    @Override
    public String getAuditsDescription() {
        return auditDetails.getDescription();
    }

    @Override
    public ContributionChangeType getAuditsChangeType() {
        return I_ConceptAccess.ContributionChangeType.valueOf(
                auditDetails.getChangeType().getLiteral().toUpperCase());
    }

    @Override
    public ContributionDef.ContributionType getContributionType() {
        return ContributionDef.ContributionType.valueOf(
                contributionRecord.getContributionType().getLiteral());
    }

    @Override
    public ContributionDef.ContributionState getContributionState() {
        return ContributionDef.ContributionState.valueOf(
                contributionRecord.getState().getLiteral());
    }

    @Override
    public UUID getEhrId() {
        return contributionRecord.getEhrId();
    }

    @Override
    public String getDataType() {
        return contributionRecord.getContributionType().getLiteral();
    }

    @Override
    public void setDataType(ContributionDataType contributionDataType) {
        contributionRecord.setContributionType(contributionDataType);
    }

    @Override
    public UUID getId() {
        return contributionRecord.getId();
    }

    @Override
    public void setEhrId(UUID ehrId) {
        contributionRecord.setEhrId(ehrId);
    }

    @Override
    public DataAccess getDataAccess() {
        return this;
    }

    @Override
    public void setHasAuditDetails(UUID auditId) {
        contributionRecord.setHasAudit(auditId);
    }

    @Override
    public UUID getHasAuditDetails() {
        return contributionRecord.getHasAudit();
    }

    @Override
    public void adminDelete() {
        AdminApiUtils adminApi = new AdminApiUtils(getContext());

        // retrieve info on all linked versioned_objects
        Result linkedCompositions =
                Routines.adminGetLinkedCompositionsForContrib(getContext().configuration(), this.getId());
        Result linkedStatus =
                Routines.adminGetLinkedStatusForContrib(getContext().configuration(), this.getId());

        // handling of linked composition
        linkedCompositions.forEach(compo -> adminApi.deleteComposition(compo.getComposition()));

        // handling of linked status
        linkedStatus.forEach(status -> {
            Result delStatus =
                    Routines.adminDeleteStatus(getContext().configuration(), status.getStatus());
            if (delStatus.isEmpty()) {
                throw new InternalServerException("Admin deletion of Status failed! Unexpected result.");
            }
            // handle auxiliary objects
            delStatus.forEach(id -> {
                // delete status audit
                adminApi.deleteAudit(id.getStatusAudit(), "Status", false);

                // clear history
                int res = getContext()
                        .selectQuery(new AdminDeleteStatusHistory().call(status.getStatus()))
                        .execute();
                if (res != 1) throw new InternalServerException("Admin deletion of Status failed!");
            });
        });

        // delete contribution itself
        adminApi.deleteContribution(this.getId(), null, false);
    }

    @Override
    public Short getSysTenant() {
        return contributionRecord.getSysTenant();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy