public void approveContract(ContractActionDto action, String approverId) {
        ContractBean contract = getContractById(action.getContractId());

        // We probably need an optimised query :-).
        ClientVersionBean cvb = contract.getClient();
        ApiVersionBean avb = contract.getApi();
        OrganizationBean org = avb.getApi().getOrganization();
        PlanVersionBean plan = contract.getPlan();
        OrganizationBean orgA = avb.getApi().getOrganization();
        OrganizationBean orgC = cvb.getClient().getOrganization();
        UserBean approver = tryAction(() -> storage.getUser(approverId));

        // Set the contract to approved state and send approved event.
        contract.setStatus(ContractStatus.Created);
        LOGGER.debug("{0} approved a contract: {1} -> {2}", approverId, contract, action);

        // In the second phase we need to check the other contracts to see whether they are all in the 'ready' state
        // If so, then it's ready to publish.
        List<ContractBean> contracts = tryAction(
             () -> Streams.stream((storage.getAllContracts(org.getId(), cvb.getClient().getId(), cvb.getVersion())
             ))).collect(Collectors.toList());

        List<ContractBean> awaitingApprovalList = contracts
             .stream()
             .filter(c -> c.getStatus().equals(ContractStatus.AwaitingApproval))
             .collect(Collectors.toList());

        if (awaitingApprovalList.size() > 0) {
            LOGGER.debug("A contract was approved, but {0} other contracts are still awaiting approval, "
                      + "so client version {1} will remain in its pending state until the remaining contract approvals "
                      + "are granted: {2}.", awaitingApprovalList.size(), cvb.getVersion(), awaitingApprovalList);
        } else {
            LOGGER.debug("All contracts for {0} have been approved", cvb.getVersion());
            tryAction(() -> {
                if (clientValidator.isReady(cvb)) {
                    // Set client to ready status and fire change status event
                    LOGGER.debug("Client set to ready as all contracts have been approved");
                    cvb.setStatus(ClientStatus.Ready);
                    clientAppService.fireClientStatusChangeEvent(cvb, ClientStatus.AwaitingApproval);
                    // If auto-promote, then we immediately register.
                    if (action.isAutoPromote()) {
                        LOGGER.debug("Auto approving: {0}", cvb);
                        registerClient(orgC.getId(), cvb.getClient().getId(), cvb.getVersion());
                    }
                }
            });
        }
        //storage.flush();
        fireContractApprovedEvent(approver, contract, orgC, cvb, orgA, avb, plan);
    }
Actions like publish, register, re-register, etc.

NB: Might fold this into respective services later.

@author Marc Savy {@literal <marc@blackparrotlabs.io>}
manager/api/rest-impl/src/main/java/io/apiman/manager/api/service/ActionService.java