• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FIWARE / contract-management / #69

30 Oct 2025 07:38AM UTC coverage: 1.686% (+0.04%) from 1.651%
#69

Pull #12

wistefan
improve organization creation and cleanup
Pull Request #12: Support for central marketplace and policy creation

117 of 1238 new or added lines in 31 files covered. (9.45%)

5 existing lines in 2 files now uncovered.

587 of 34807 relevant lines covered (1.69%)

0.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/org/fiware/iam/dsp/RainbowProductOrderHandler.java
1
package org.fiware.iam.dsp;
2

3
import io.micronaut.context.annotation.Requires;
4
import io.micronaut.http.HttpResponse;
5
import io.micronaut.http.HttpStatus;
6
import jakarta.inject.Singleton;
7
import lombok.RequiredArgsConstructor;
8
import lombok.extern.slf4j.Slf4j;
9
import org.fiware.iam.tmforum.TMFMapper;
10
import org.fiware.iam.configuration.GeneralProperties;
11
import org.fiware.iam.handlers.ProductOrderHandler;
12
import org.fiware.iam.exception.RainbowException;
13
import org.fiware.iam.exception.TMForumException;
14
import org.fiware.iam.tmforum.TMForumAdapter;
15
import org.fiware.iam.tmforum.agreement.model.RelatedPartyTmfVO;
16
import org.fiware.iam.tmforum.productorder.model.AgreementRefVO;
17
import org.fiware.iam.tmforum.productorder.model.ProductOrderItemVO;
18
import org.fiware.iam.tmforum.productorder.model.ProductOrderVO;
19
import org.fiware.iam.tmforum.productorder.model.QuoteRefVO;
20
import org.fiware.iam.tmforum.quote.model.QuoteItemVO;
21
import org.fiware.iam.tmforum.quote.model.QuoteStateTypeVO;
22
import org.fiware.iam.tmforum.quote.model.QuoteVO;
23
import org.fiware.rainbow.model.AgreementVO;
24
import org.fiware.rainbow.model.ProviderNegotiationVO;
25
import reactor.core.publisher.Mono;
26

27
import java.util.*;
28
import java.util.function.Function;
29

30
@Requires(condition = GeneralProperties.RainbowCondition.class)
31
@RequiredArgsConstructor
32
@Singleton
NEW
33
@Slf4j
×
34
public class RainbowProductOrderHandler implements ProductOrderHandler {
35

36
    private static final String STATE_VERIFIED = "dspace:VERIFIED";
37
    private static final String STATE_FINALIZED = "dspace:FINALIZED";
38

39
    private final TMForumAdapter tmForumAdapter;
40
    private final TMFMapper tmfMapper;
41
    private final RainbowAdapter rainbowAdapter;
42

43
    @Override
44
    public Mono<HttpResponse<?>> handleProductOrderComplete(String organizationId, ProductOrderVO productOrderVO) {
45

NEW
46
        List<RelatedPartyTmfVO> relatedPartyTmfVOS = productOrderVO
×
NEW
47
                .getRelatedParty()
×
NEW
48
                .stream()
×
NEW
49
                .map(tmfMapper::map)
×
NEW
50
                .peek(rr -> rr.unknownProperties(null))
×
NEW
51
                .toList();
×
52

NEW
53
        if (!containsQuote(productOrderVO)) {
×
NEW
54
            return Mono.zipDelayError(
×
55
                            productOrderVO
NEW
56
                                    .getProductOrderItem()
×
NEW
57
                                    .stream()
×
NEW
58
                                    .map(ProductOrderItemVO::getProductOffering)
×
NEW
59
                                    .filter(Objects::nonNull)
×
NEW
60
                                    .map(offering -> rainbowAdapter.createAgreement(organizationId, offering.getId()))
×
NEW
61
                                    .toList(),
×
62
                            res -> {
NEW
63
                                List<AgreementVO> agreementVOS = Arrays.stream(res).filter(Objects::nonNull).filter(AgreementVO.class::isInstance).map(AgreementVO.class::cast).toList();
×
NEW
64
                                return updateProductOrder(productOrderVO, agreementVOS, relatedPartyTmfVOS);
×
65
                            })
NEW
66
                    .flatMap(Function.identity())
×
NEW
67
                    .map(po -> (HttpResponse<?>) HttpResponse.noContent());
×
68
        } else {
NEW
69
            return tmForumAdapter.getQuoteById(getQuoteRef(productOrderVO).getId())
×
NEW
70
                    .flatMap(quoteVO -> {
×
NEW
71
                        String offerId = getOfferIdFromQuote(quoteVO);
×
72

NEW
73
                        Mono<?> agreementMono = rainbowAdapter.getNegotiationProcess(quoteVO.getExternalId())
×
NEW
74
                                .map(ProviderNegotiationVO::getCnProcessId)
×
NEW
75
                                .flatMap(rainbowAdapter::getAgreement)
×
NEW
76
                                .map(avo -> avo.dataServiceId(offerId))
×
NEW
77
                                .flatMap(agreementVO -> updateProductOrder(productOrderVO, List.of(agreementVO), relatedPartyTmfVOS));
×
78

NEW
79
                        Mono<?> negotiationMono = rainbowAdapter.getNegotiationProcessState(quoteVO.getExternalId())
×
NEW
80
                                .flatMap(state -> {
×
NEW
81
                                    if (state.equals(STATE_FINALIZED)) {
×
82
                                        // nothing to do here, but we want the chain to continue
NEW
83
                                        return Mono.just(Optional.empty());
×
84
                                    }
NEW
85
                                    if (!state.equals(STATE_VERIFIED)) {
×
NEW
86
                                        throw new RainbowException(String.format("Negotiation process %s is in state %s. Not allowed for order completion.", quoteVO.getExternalId(), state));
×
87
                                    }
NEW
88
                                    return rainbowAdapter.updateNegotiationProcessByProviderId(quoteVO.getExternalId(), "dspace:FINALIZED");
×
89
                                });
NEW
90
                        return Mono.zipDelayError(agreementMono, negotiationMono);
×
91
                    })
NEW
92
                    .onErrorMap(t -> {
×
NEW
93
                        log.warn("Was not able to update negotiation.", t);
×
NEW
94
                        throw new RainbowException("Was not able to update the negotiation.");
×
95
                    })
NEW
96
                    .map(t -> (HttpResponse<?>) HttpResponse.noContent());
×
97
        }
98
    }
99

100
    @Override
101
    public Mono<HttpResponse<?>> handleProductOrderStop(String organizationId, ProductOrderVO productOrderVO) {
NEW
102
        List<Mono<Boolean>> deletionMonos = productOrderVO.getAgreement()
×
NEW
103
                .stream()
×
NEW
104
                .map(AgreementRefVO::getId)
×
NEW
105
                .map(rainbowAdapter::deleteAgreement)
×
NEW
106
                .toList();
×
NEW
107
        return Mono.zipDelayError(deletionMonos, deletions -> {
×
NEW
108
            if (Set.of(deletions).contains(false)) {
×
NEW
109
                log.warn("Was not able to delete the agreement for order {}.", productOrderVO);
×
NEW
110
                HttpResponse.status(HttpStatus.BAD_GATEWAY);
×
111
            }
NEW
112
            return HttpResponse.status(HttpStatus.ACCEPTED);
×
113
        });
114
    }
115

116
    @Override
117
    public Mono<HttpResponse<?>> handleProductOrderNegotiation(String organizationId, ProductOrderVO productOrderVO) {
NEW
118
        return tmForumAdapter
×
NEW
119
                .getQuoteById(getQuoteRef(productOrderVO).getId())
×
NEW
120
                .flatMap(quoteVO -> {
×
NEW
121
                    if (quoteVO.getState() != QuoteStateTypeVO.ACCEPTED) {
×
NEW
122
                        throw new TMForumException(String.format("The quote is not in state accepted, cannot be used for product ordering. %s:%s.", quoteVO.getId(), quoteVO.getState()));
×
123
                    }
NEW
124
                    return rainbowAdapter.updateNegotiationProcessByProviderId(quoteVO.getExternalId(), STATE_VERIFIED);
×
125
                })
NEW
126
                .map(t -> HttpResponse.noContent());
×
127
    }
128

129
    private String getOfferIdFromQuote(QuoteVO quoteVO) {
NEW
130
        return quoteVO
×
NEW
131
                .getQuoteItem()
×
NEW
132
                .stream()
×
NEW
133
                .filter(qi -> qi.getState().equals("accepted"))
×
NEW
134
                .map(QuoteItemVO::getProductOffering)
×
NEW
135
                .map(org.fiware.iam.tmforum.quote.model.ProductOfferingRefVO::getId)
×
NEW
136
                .findFirst()
×
NEW
137
                .orElseThrow(() -> new IllegalArgumentException("The event does not reference an offer."));
×
138
    }
139

140
    private QuoteRefVO getQuoteRef(ProductOrderVO productOrderVO) {
141
        // integration with IDSA Contract Negotiation is only supported for productOrders with a single quote.
NEW
142
        if (productOrderVO.getQuote().size() != 1) {
×
NEW
143
            throw new RainbowException("IDSA Contract Negotiation does not support the inclusion of multiple processes into one product.");
×
144
        }
NEW
145
        return productOrderVO.getQuote().get(0);
×
146
    }
147

148
    private boolean containsQuote(ProductOrderVO productOrderVO) {
NEW
149
        return productOrderVO.getQuote() != null && !productOrderVO.getQuote().isEmpty();
×
150
    }
151

152
    private Mono<ProductOrderVO> updateProductOrder(ProductOrderVO productOrderVO, List<AgreementVO> agreementVOS, List<RelatedPartyTmfVO> relatedPartyTmfVOS) {
NEW
153
        return Mono.zipDelayError(
×
NEW
154
                agreementVOS.stream()
×
NEW
155
                        .map(agreementVO ->
×
NEW
156
                                tmForumAdapter.createAgreement(productOrderVO.getId(), agreementVO.getDataServiceId(), agreementVO.getAgreementId(), relatedPartyTmfVOS))
×
NEW
157
                        .toList(),
×
158
                agreements -> {
NEW
159
                    List<String> agreementIds = Arrays.stream(agreements)
×
NEW
160
                            .filter(String.class::isInstance)
×
NEW
161
                            .map(String.class::cast)
×
NEW
162
                            .toList();
×
NEW
163
                    return tmForumAdapter.addAgreementToOrder(productOrderVO.getId(), agreementIds);
×
NEW
164
                }).flatMap(Function.identity());
×
165

166
    }
167
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc