diff --git a/CHANGELOG.md b/CHANGELOG.md index fefa538..2e0e2cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,19 @@ # Changelog -Starting from version 3.0.0, all notable changes to this project will be documented in this file. - The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [3.0.0] +## 3.2.0 + +### Changed +- Updated to Shapeshifter specification version 3.1.0 + +## 3.1.0 + +### Changed +- Update Spring Boot version to 3.4.1 + +## 3.0.0 ### Added diff --git a/api/src/main/resources/UFTP-agr-cro.xsd b/api/src/main/resources/UFTP-agr-cro.xsd index 60e91c7..0544ac8 100644 --- a/api/src/main/resources/UFTP-agr-cro.xsd +++ b/api/src/main/resources/UFTP-agr-cro.xsd @@ -1,184 +1,167 @@ - - - - - - - - - - - - - - - - Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period - referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing - implementations to reject messages with an errant UTC offset. - - - - - - - - - - - - A connection that the AGR want the CRO to update - - - - EntityAddress of the Connection entity being updated. - - - - - The first Period hat the AGR represents the prosumer at this Connection. - - - - - The last Period that the AGR represents the prosumer at this Connection, if applicable. - - - - - - - - - - - - MessageID of the AGRPortfolioUpdate that has just been accepted or rejected - - - - - - - - - - - - - - Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period - referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing - implementations to reject messages with an errant UTC offset. - - - - - - The Period for which the AGR requests the portfolio information. - - - - - - - - - - - - - - - - - Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period - referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing - implementations to reject messages with an errant UTC offset. - - - - - - MessageID of the AGRPortfolioQuery that has just been accepted or rejected - - - - - The Period that the portfolio is valid. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - EntityAddress of the CongestionPoint. - - - - - Indicates whether the DSO accepts mutual exclusive FlexOffers on this CongestionPoint. - - - - - Indicates which party is responsible for day-ahead redispatch. - - - - - Indicates which party is responsible for intraday ahead redispatch, AGR or DSO. If not specified, there will be no intraday trading on this CongestionPoint. - - - - - - - - - - - EntityAddress of the Connection. - - - - - - - - - - + + + + + + + + + + + + + Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant UTC offset. + + + + + + + + + + + A connection that the AGR want the CRO to update + + + + EntityAddress of the Connection entity being updated. + + + + + The first Period hat the AGR represents the prosumer at this Connection. + + + + + The last Period that the AGR represents the prosumer at this Connection, if applicable. + + + + + + + + + + + + MessageID of the AGRPortfolioUpdate that has just been accepted or rejected + + + + + + + + + + + + + + Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant UTC offset. + + + + + The Period for which the AGR requests the portfolio information. + + + + + + + + + + + + + + + + + Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant UTC offset. + + + + + MessageID of the AGRPortfolioQuery that has just been accepted or rejected + + + + + The Period that the portfolio is valid. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EntityAddress of the CongestionPoint. + + + + + Indicates whether the DSO accepts mutual exclusive FlexOffers on this CongestionPoint. + + + + + Indicates which party is responsible for day-ahead redispatch. + + + + + Indicates which party is responsible for intraday ahead redispatch, AGR or DSO. If not specified, there will be no intraday trading on this CongestionPoint. + + + + + + + + + + EntityAddress of the Connection. + + + + + + + + + + diff --git a/api/src/main/resources/UFTP-agr-dso.xsd b/api/src/main/resources/UFTP-agr-dso.xsd index 06e2ead..37dda14 100644 --- a/api/src/main/resources/UFTP-agr-dso.xsd +++ b/api/src/main/resources/UFTP-agr-dso.xsd @@ -1,718 +1,705 @@ - - - - - - - - - - - - - - ISO 8601 time interval (minutes only, for example PT15M) indicating the duration of the ISPs referenced in this message. Although the ISP length is a market-wide fixed - value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant ISP duration. - - - - - - Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period - referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing - implementations to reject messages with an errant UTC offset. - - - - - - Day (in yyyy-mm-dd format) the ISPs referenced in this Flex* message belong to. - - - - - Entity Address of the Congestion Point this D-Prognosis applies to. - - - - - - - - - - - - - - The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. - - - - - - Revision of this message. A sequence number that must be incremented each time a new revision of a - prognosis is sent. The combination of SenderDomain and PrognosisSequence should be unique - - - - - - - - - - - - - Power specified for this ISP in Watts. Also see the important notes about the sign of this attribute in the main documentation entry for the ISP element. - - - - - Number of the first ISPs this element refers to. The first ISP of a day has number 1. - - - - - The number of the ISPs this element represents. Optional, default value is 1. - - - - - - - - - - - - - - - - + + + + + + + + + + + ISO 8601 time interval (minutes only, for example PT15M) indicating the duration of the ISPs referenced in this message. Although the ISP length is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant ISP duration. + + + + + Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant UTC offset. + + + + + Day (in yyyy-mm-dd format) the ISPs referenced in this Flex* message belong to. + + + + + Entity Address of the Congestion Point this D-Prognosis applies to. + + + + + + + + + + + + + + The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. + + + + + + Revision of this message. A sequence number that must be incremented each time a new revision of a + prognosis is sent. The combination of SenderDomain and PrognosisSequence should be unique + + + + + + + + + + + + Power specified for this ISP in Watts. Also see the important notes about the sign of this attribute in the main documentation entry for the ISP element. + + + + + Number of the first ISPs this element refers to. The first ISP of a day has number 1. + + + + + The number of the ISPs this element represents. Optional, default value is 1. + + + + + + + + + + + + + + + + + + + + + + MessageID of the D-Prognosis message this request is based on. + + + + + + + + + + + + + + The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. + + + + + + Reference to the bilateral contract in question. + + + + + Message reference, assigned by the DSO originating the FlexReservationUpdate. + + + + + + + + + + + + Remaining reserved power specified for this ISP in Watts. + + + + + Number of the first ISPs this element refers to. The first ISP of a day has number 1. + + + + + The number of the ISPs this element represents. Optional, default value is 1. + + + + + + + + + + + + MessageID of the FlexReservationUpdate message this request is based on. + + + + + + + + + + + + + + The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. + + + + + + Revision of this message, a sequence number that must be incremented each time a new revision of a + FlexRequest message is sent. + + + + + Date and time, including the time zone (ISO 8601 formatted as per http://www.w3.org/TR/NOTE-datetime) until which the FlexRequest message is valid. + + + + + Reference to the concerning contract, if applicable. The contract may be either bilateral or commoditized market contract. Each contract may specify multiple service-types. + + + + + Service type for this request, the service type determines response characteristics such as latency or asset participation type. + + + + + + + + + + + + + Power specified for this ISP in Watts. Also see the important notes about the sign of this attribute in the main documentation entry for the ISP element. + + + + + Power specified for this ISP in Watts. Also see the important notes about the sign of this attribute in the main documentation entry for the ISP element. + + + + + Number of the first ISPs this element refers to. The first ISP of a day has number 1. + + + + + The number of the ISPs this element represents. Optional, default value is 1. + + + + + + + + + + + + MessageID of the FlexRequest message this request is based on. + + + + + + + + + + + + + + If the DSO does not support mutually exclusive offers it will reject FlexOffers that contain more than one OfferOption. + + + + + + Date and time, including the time zone (ISO 8601 formatted as per http://www.w3.org/TR/NOTE-datetime) until which the FlexOffer is valid. + + + + + Indicates whether this FlexOffer is intended to be unsolicited (i.e. without a preceding FlexRequest). + + + + + MessageID of the FlexRequest message this request is based on. Mandatory if Unsolicited=false. + + + + + Reference to the concerning contract, if applicable. The contract may be either bilateral or commoditized market contract. + + + + + MessageID of the D-Prognosis this request is based on, if it has been agreed that the baseline is based on D-prognoses. + + + + + Identification of the baseline prognosis, if another baseline methodology is used than based on D-prognoses + + + + + ISO 4217 code indicating the currency that applies to the price of the FlexOffer. + + + + + + + + + - + + The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. + + - - - MessageID of the D-Prognosis message this request is based on. - - - - - - - - - - - - - - The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. - - - - - - Reference to the bilateral contract in question. - - - - - Message reference, assigned by the DSO originating the FlexReservationUpdate. - - - - - - - - - - - - Remaining reserved power specified for this ISP in Watts. - - - - - Number of the first ISPs this element refers to. The first ISP of a day has number 1. - - - - - The number of the ISPs this element represents. Optional, default value is 1. - - - - - - - - - - - - MessageID of the FlexReservationUpdate message this request is based on. - - - - - - - - - - - - - - The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. - - - - - - Revision of this message, a sequence number that must be incremented each time a new revision of a - FlexRequest message is sent. - - - - - - Date and time, including the time zone (ISO 8601 formatted as per http://www.w3.org/TR/NOTE-datetime) until which the FlexRequest message is valid. - + + + The identification of this option. + - - - Reference to the concerning contract, if applicable. The contract may be either bilateral or commoditized market contract. Each contract may specify multiple - service-types. - - - - - - Service type for this request, the service type determines response characteristics such as latency or asset participation type. - - - - - - - - - - - - - Power specified for this ISP in Watts. Also see the important notes about the sign of this attribute in the main documentation entry for the ISP element. - - - - - Power specified for this ISP in Watts. Also see the important notes about the sign of this attribute in the main documentation entry for the ISP element. - - - - - Number of the first ISPs this element refers to. The first ISP of a day has number 1. - - - - - The number of the ISPs this element represents. Optional, default value is 1. - - - - - - - - - - - - MessageID of the FlexRequest message this request is based on. - - - - - - - - - - - + + + The asking price for the flexibility offered in this option. + + + + + The minimal activation factor for this OfferOption. An AGR may choose to include MinActivationFactor in FlexOffers even if the DSO is not interested in partial activation. In that case the DSO will simply use an ActivationFactor of 1.00 in every FlexOrder. + + + + + + + + + + Power specified for this ISP in Watts. Also see the important notes about the sign of this attribute in the main documentation entry for the ISP element. + + + + + Number of the first ISPs this element refers to. The first ISP of a day has number 1. + + + + + The number of the ISPs this element represents. Optional, default value is 1. + + + + + + + + + + MessageID of the FlexOffer message this request is based on, if applicable. + + + + + + + + + + + + MessageID of the FlexOffer message that is being revoked: this FlexOffer must have been accepted previously. + + + + + + + + + + + + MessageID of the FlexOfferRevocation message this request is based on. + + + + + + + + + + + + The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. + + + + + + Indicates whether this FlexOrder is intended to be unsolicited (i.e. without a preceding FlexOffer). + + + + + MessageID of the FlexOffer message this order is based on. Mandatory if Unsolicited=false. + + + + + Service type for this order, the service type determines response characteristics such as latency or asset participation type. + + + + + Reference to the concerning bilateral contract, if applicable. + + + + + MessageID of the D-Prognosis this request is based on, if it has been agreed that the baseline is based on D-prognoses. + + + + + Identification of the baseline prognosis, if another baseline methodology is used than based on D-prognoses + + + + + The price for the flexibility ordered. Usually, the price should match the price of the related FlexOffer. + + + + + ISO 4217 code indicating the currency that applies to the price of the FlexOffer. + + + + + Order number assigned by the DSO originating the FlexOrder. To be stored by the AGR and used in the settlement phase. + + + + + The OptionReference from the OfferOption chosen from the FlexOffer, if applicable. + + + + + The activation factor for this OfferOption. The ActivationFactor must be greater than or equal to the MinActivationFactor in the OfferOption chosen from the FlexOffer. + + + + + + + + + + Power specified for this ISP in Watts. Also see the important notes about the sign of this attribute in the main documentation entry for the ISP element. + + + + + Number of the first ISPs this element refers to. The first ISP of a day has number 1. + + + + + The number of the ISPs this element represents. Optional, default value is 1. + + + + + + + + + + MessageID of the FlexOrder that has just been accepted or rejected. + + + + + + + + + + + + + + + + First Period of the settlement period this message applies to. + + + + + Last Period of the settlement period this message applies to. + + + + + ISO 4217 code indicating the currency that applies to all amounts (flex price, penalty and net settlement) in this message. + + + + + + + - - If the DSO does not support mutually exclusive offers it will reject FlexOffers that contain more than one OfferOption. - - + - - - Date and time, including the time zone (ISO 8601 formatted as per http://www.w3.org/TR/NOTE-datetime) until which the FlexOffer is valid. - + + + Order reference assigned by the DSO when originating the FlexOrder. + - - - MessageID of the FlexRequest message this request is based on. Mandatory if and only if solicited. - + + + + - - Reference to the concerning contract, if applicable. The contract may be either bilateral or commoditized market contract. - + + Reference to the concerning bilateral contract, if it is linked to it + - - MessageID of the D-Prognosis this request is based on, if it has been agreed that the baseline is based on D-prognoses. - + + MessageID of the Prognosis message (more specifically: the D-Prognosis) the FlexOrder is based on, if it has been agreed that the baseline is based on D-prognoses. + - - Identification of the baseline prognosis, if another baseline methodology is used than based on D-prognoses - - - - - ISO 4217 code indicating the currency that applies to the price of the FlexOffer. - - - - - - - - - - - - The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. - - - - - - The identification of this option. - - - - - The asking price for the flexibility offered in this option. - - - - - The minimal activation factor for this OfferOption. An AGR may choose to include MinActivationFactor in FlexOffers even if the DSO is not interested in partial activation. In - that case the DSO will simply use an ActivationFactor of 1.00 in every FlexOrder. - - - - - - - - - - - Power specified for this ISP in Watts. Also see the important notes about the sign of this attribute in the main documentation entry for the ISP element. - - - - - Number of the first ISPs this element refers to. The first ISP of a day has number 1. - - - - - The number of the ISPs this element represents. Optional, default value is 1. - - - - - - - - - - MessageID of the FlexOffer message this request is based on. - - - - - - - - - - - - MessageID of the FlexOffer message that is being revoked: this FlexOffer must have been accepted previously. - - - - - - - - - - - - MessageID of the FlexOfferRevocation message this request is based on. - - - - - - - - - - - - The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. - - - - - - MessageID of the FlexOffer message this order is based on. - - - - - Reference to the concerning bilateral contract, if applicable. - + + Identification of the baseline prognosis, if another baseline methodology is used than based on D-prognoses. + - - - MessageID of the D-Prognosis this request is based on, if it has been agreed that the baseline is based on D-prognoses. - - - - - Identification of the baseline prognosis, if another baseline methodology is used than based on D-prognoses - + + + Entity Address of the Congestion Point the FlexOrder applies to. + - - The price for the flexibility ordered. Usually, the price should match the price of the related FlexOffer. - - - - - ISO 4217 code indicating the currency that applies to the price of the FlexOffer. - - - - - Order number assigned by the DSO originating the FlexOrder. To be stored by the AGR and used in the settlement phase. - - - - - The OptionReference from the OfferOption chosen from the FlexOffer. - - - - - The activation factor for this OfferOption. The ActivationFactor must be greater than or equal to the MinActivationFactor in the OfferOption chosen from the FlexOffer. - - - - - - - - - - - Power specified for this ISP in Watts. Also see the important notes about the sign of this attribute in the main documentation entry for the ISP element. - - - - - Number of the first ISPs this element refers to. The first ISP of a day has number 1. - - - - - The number of the ISPs this element represents. Optional, default value is 1. - - - - - - - - - - MessageID of the FlexOrder that has just been accepted or rejected. - - - - - - - - - + + The price accepted for supplying the ordered amount of flexibility as per the referenced FlexOrder messages. + + + + + Penalty due a non-zero PowerDeficiency + + + + + Net settlement amount for this Period: Price minus Penalty. + + + + + + + + Number of the first ISPs this element refers to. The first ISP of a day has number 1. + + + + + The number of the ISPs this element represents. Optional, default value is 1. + + + + + Power originally forecast (as per the referenced baseline) for this ISP in Watts. + + + + + Amount of flex power ordered (as per the referenced FlexOrder message) for this ISP in Watts. + + + + + Actual amount of power for this ISP in Watts, as measured/determined by the DSO and allocated to the AGR. + + + + + + Actual amount of flex power delivered for this ISP in Watts, as determined by the DSO. + + + + + Amount of flex power sold but not delivered for this ISP in Watts, as determined by the DSO. + + + + + + + + + + The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. + + + + + + MessageID of the FlexSettlement message that has just been accepted or rejected + + + + + + + + + + Order reference assigned by the DSO when originating the FlexOrder. + + + + + Indication whether the AGR accepts the order settlement details provided by the DSO (and will invoice accordingly), or disputes these details. + + + + + In case the order settlement was disputed, this attribute must contain a human-readable description of the reason. + + + + + - - + + + + - - - First Period of the settlement period this message applies to. - - - - - Last Period of the settlement period this message applies to. - - - - - ISO 4217 code indicating the currency that applies to all amounts (flex price, penalty and net settlement) in this message. - - - - - - - - - - - - - Order reference assigned by the DSO when originating the FlexOrder. - - - - - - - - - - Reference to the concerning bilateral contract, if it is linked to it - - - - - MessageID of the Prognosis message (more specifically: the D-Prognosis) the FlexOrder is based on, if it has been agreed that the baseline is based on D-prognoses. - - - - - - Identification of the baseline prognosis, if another baseline methodology is used than based on D-prognoses. - - - - - Entity Address of the Congestion Point the FlexOrder applies to. - - - - - The price accepted for supplying the ordered amount of flexibility as per the referenced FlexOrder messages. - - - - - Penalty due a non-zero PowerDeficiency - - - - - Net settlement amount for this Period: Price minus Penalty. - - - - - - - - Number of the first ISPs this element refers to. The first ISP of a day has number 1. - - - - - The number of the ISPs this element represents. Optional, default value is 1. - - - - - Power originally forecast (as per the referenced baseline) for this ISP in Watts. - - - - - Amount of flex power ordered (as per the referenced FlexOrder message) for this ISP in Watts. - - - - - Actual amount of power for this ISP in Watts, as measured/determined by the DSO and allocated to the AGR. - - - - - - Actual amount of flex power delivered for this ISP in Watts, as determined by the DSO. - - - - - Amount of flex power sold but not delivered for this ISP in Watts, as determined by the DSO. - - - - - - - + + + Reference to the concerning bilateral contract. + + + + + - - The ISP represents one or more Imbalance Settlement Periods and is used by Prognosis and Flex-related messages. - - + - - - MessageID of the FlexSettlement message that has just been accepted or rejected - - - - - - - - - - Order reference assigned by the DSO when originating the FlexOrder. - - - - - Indication whether the AGR accepts the order settlement details provided by the DSO (and will invoice accordingly), or disputes these details. - - - - - In case the order settlement was disputed, this attribute must contain a human-readable description of the reason. - - - - - - - - - - - - - - Reference to the concerning bilateral contract. - - - - - - - - - - - Period the being settled. - - - - - - - - Number of the first ISPs this element refers to. The first ISP of a day has number 1. - - - - - The number of the ISPs this element represents. Optional, default value is 1. - - - - - Amount of flex power that has been reserved (and not released using a FlexReservationUpdate message). - - - - - Amount of flex power that has been both reserved in advance and has been requested using a - FlexRequest (i.e. the lowest amount of flex power for this ISP). If there was no FlexRequest, - this field is omitted. - - - - - - Amount of flex power that is considered available based on the FlexRequest in question. In case RequestedPower=0, AvailablePower is defined so that the offered power is - allowed to be between 0 and AvailablePower in terms of compliancy (see Appendix 3 for details). In case RequestedPower ≠0, AvailablePower is defined so that the offered power is allowed to - exceed the amount of requested power up to AvailablePower. If this is relevant for settlement, the DSO can include this field. - - - - - - Amount of flex power that has been reserved in advance, requested using a FlexRequest and covered in an offer from the AGR. If there was no offer, this field is omitted. If - there were multiple offers, only the one is considered that is most compliant . - - - - - - Amount of flex power that has been ordered using a FlexOrder message that was based on a FlexOffer, both linked to this contract. If there was no order, this field is - omitted. - - - - - - - - - - - - - - - - - - - + + + Period the being settled. + + + + + + + + Number of the first ISPs this element refers to. The first ISP of a day has number 1. + + + + + The number of the ISPs this element represents. Optional, default value is 1. + + + + + Amount of flex power that has been reserved (and not released using a FlexReservationUpdate message). + + + + + Amount of flex power that has been both reserved in advance and has been requested using a + FlexRequest (i.e. the lowest amount of flex power for this ISP). If there was no FlexRequest, + this field is omitted. + + + + + Amount of flex power that is considered available based on the FlexRequest in question. In case RequestedPower=0, AvailablePower is defined so that the offered power is allowed to be between 0 and AvailablePower in terms of compliancy (see Appendix 3 for details). In case RequestedPower ≠0, AvailablePower is defined so that the offered power is allowed to exceed the amount of requested power up to AvailablePower. If this is relevant for settlement, the DSO can include this field. + + + + + Amount of flex power that has been reserved in advance, requested using a FlexRequest and covered in an offer from the AGR. If there was no offer, this field is omitted. If there were multiple offers, only the one is considered that is most compliant . + + + + + Amount of flex power that has been ordered using a FlexOrder message that was based on a FlexOffer, both linked to this contract. If there was no order, this field is omitted. + + + + + + + + + + + + + + + + + + diff --git a/api/src/main/resources/UFTP-agr.xsd b/api/src/main/resources/UFTP-agr.xsd index cf81c2c..0c1c920 100644 --- a/api/src/main/resources/UFTP-agr.xsd +++ b/api/src/main/resources/UFTP-agr.xsd @@ -1,13 +1,6 @@ - - - - - - + + + diff --git a/api/src/main/resources/UFTP-common.xsd b/api/src/main/resources/UFTP-common.xsd index 178e90f..69dd8dc 100644 --- a/api/src/main/resources/UFTP-common.xsd +++ b/api/src/main/resources/UFTP-common.xsd @@ -1,251 +1,229 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The SignedMessage element represents the secure wrapper used to submit USEF XML messages from the local message queue to the message queue of a remote participant. It contains - minimal metadata (which is distinct from the common metadata used for all other messages), allowing the recipient to look up the sender's cryptographic scheme and public keys, and the actual - XML message, as transformed (signed/sealed) using that cryptographic scheme. - - - - - The Internet domain of the USEF participant sending this message. Upon receiving a message, the recipient should validate that its value matches the corresponding attribute - value specified in the inner XML message, once un-sealed: if not, the message must be rejected as invalid. - - - - - - The USEF role of the participant sending this message: AGR, BRP, CRO, DSO or MDC. Receive-time validation should take place as described for the SenderDomain attribute - above. - - - - - - The Base-64 encoded inner XML message contained in this wrapper, as transformed (signed/sealed) using the sender's cryptographic scheme. The recipient can determine which - scheme applies using a DNS or configuration file lookup, based on the combination of SenderDomain and SenderRole. - - - - - - - - - - - Version of the Shapeshifter specification used by the USEF participant sending this message. - - - - - The Internet domain of the USEF participant sending this message. When receiving a message, its value should match the value specified in the SignedMessage wrapper: - otherwise, the message must be rejected as invalid. When replying to this message, this attribute is used to look up the USEF endpoint the reply message should be delivered to. - - - - - - Internet domain of the participant this message is intended for. When sending a message, this attribute, combined with the RecipientRole, is used to look up the USEF endpoint - the message should be delivered to. - - - - - - Date and time this message was created, including the time zone (ISO 8601 formatted as per http://www.w3.org/TR/NOTE-datetime). - - - - - Unique identifier (UUID/GUID as per IETF RFC 4122) for this message, to be generated when composing each message. - - - - - Unique identifier (UUID/GUID as per IETF RFC 4122) used to correlate responses with requests, to be generated when composing the first message in a conversation and - subsequently copied from the original message to each reply message. - - - - - - - - - - - - - Indication whether the query was executed successfully or failed. - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The SignedMessage element represents the secure wrapper used to submit USEF XML messages from the local message queue to the message queue of a remote participant. It contains minimal metadata (which is distinct from the common metadata used for all other messages), allowing the recipient to look up the sender's cryptographic scheme and public keys, and the actual XML message, as transformed (signed/sealed) using that cryptographic scheme. + + + + The Internet domain of the USEF participant sending this message. Upon receiving a message, the recipient should validate that its value matches the corresponding attribute value specified in the inner XML message, once un-sealed: if not, the message must be rejected as invalid. + - - - In case the query failed, this attribute must contain a human-readable description of the failure reason. - + + + The USEF role of the participant sending this message: AGR, BRP, CRO, DSO or MDC. Receive-time validation should take place as described for the SenderDomain attribute above. + - - - - - - - - - - - - - - - - - - - - - - - - - + + + The Base-64 encoded inner XML message contained in this wrapper, as transformed (signed/sealed) using the sender's cryptographic scheme. The recipient can determine which scheme applies using a DNS or configuration file lookup, based on the combination of SenderDomain and SenderRole. + + + + + + + + + + Version of the Shapeshifter specification used by the USEF participant sending this message. + + + + + The Internet domain of the USEF participant sending this message. When receiving a message, its value should match the value specified in the SignedMessage wrapper: otherwise, the message must be rejected as invalid. When replying to this message, this attribute is used to look up the USEF endpoint the reply message should be delivered to. + + + + + Internet domain of the participant this message is intended for. When sending a message, this attribute, combined with the RecipientRole, is used to look up the USEF endpoint the message should be delivered to. + + + + + Date and time this message was created, including the time zone (ISO 8601 formatted as per http://www.w3.org/TR/NOTE-datetime). + + + + + Unique identifier (UUID/GUID as per IETF RFC 4122) for this message, to be generated when composing each message. + + + + + Unique identifier (UUID/GUID as per IETF RFC 4122) used to correlate responses with requests, to be generated when composing the first message in a conversation and subsequently copied from the original message to each reply message. + + + + + + + + + + + + Indication whether the query was executed successfully or failed. + + + + + In case the query failed, this attribute must contain a human-readable description of the failure reason. + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api/src/main/resources/UFTP-cro-dso.xsd b/api/src/main/resources/UFTP-cro-dso.xsd index a45083e..163fc51 100644 --- a/api/src/main/resources/UFTP-cro-dso.xsd +++ b/api/src/main/resources/UFTP-cro-dso.xsd @@ -1,205 +1,188 @@ - - - - - - - - - - - - - - - - Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period - referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing - implementations to reject messages with an errant UTC offset. - - - - - - - - - - - - A congestion point that the DSO wants the CRO to update - - - - - - - EntityAddress of the Connection. - - - - - The first Period that the Connection is part of this CongestionPoint. - - - - - The last Period that the Connection is part of this CongestionPoint. - - - - - Indicates whether the DSO accepts mutual exclusive FlexOffers on this CongestionPoint. - - - - - Indicates which party is responsible for day-ahead redispatch. - - - - - Indicates which party is responsible for intraday ahead redispatch, AGR or DSO. If not specified, there will be no intraday trading on this CongestionPoint. - - - - - - - - - - A connection that the DSO wants the CRO to update - - - - EntityAddress of the Connection. - - - - - The first Period that the Connection is part of this CongestionPoint. - - - - - The last Period that the Connection is part of this CongestionPoint. - - - - - - - - - - - - MessageID of the DSOPortfolioUpdateResponse that has just been accepted or rejected - - - - - - - - - - - - - - Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period - referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing - implementations to reject messages with an errant UTC offset. - - - - - - The Period for which the AGR requests the portfolio information. - - - - - EntityAddress of the CongestionPoint - - - - - - - - - - - - - - - - - MessageID of the AGRPortfolioQuery that has just been accepted or rejected - - - - - Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period - referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing - implementations to reject messages with an errant UTC offset. - - - - - - The Period for which the AGR requests the portfolio information. - - - - - - - - - - - - - - - EntityAddress of the Connection. - - - - - - - - - A Connection that is part of the congestion point. - - - - EntityAddress of the Connection. - - - - - The internet domain of the AGR that represents the prosumer connected on this Connection, if applicable. - - - - - - - - - - + + + + + + + + + + + + + Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant UTC offset. + + + + + + + + + + + A congestion point that the DSO wants the CRO to update + + + + + + + EntityAddress of the Connection. + + + + + The first Period that the Connection is part of this CongestionPoint. + + + + + The last Period that the Connection is part of this CongestionPoint. + + + + + Indicates whether the DSO accepts mutual exclusive FlexOffers on this CongestionPoint. + + + + + Indicates which party is responsible for day-ahead redispatch. + + + + + Indicates which party is responsible for intraday ahead redispatch, AGR or DSO. If not specified, there will be no intraday trading on this CongestionPoint. + + + + + + + + + A connection that the DSO wants the CRO to update + + + + EntityAddress of the Connection. + + + + + The first Period that the Connection is part of this CongestionPoint. + + + + + The last Period that the Connection is part of this CongestionPoint. + + + + + + + + + + + + MessageID of the DSOPortfolioUpdateResponse that has just been accepted or rejected + + + + + + + + + + + + + + Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant UTC offset. + + + + + The Period for which the AGR requests the portfolio information. + + + + + EntityAddress of the CongestionPoint + + + + + + + + + + + + + + + + + MessageID of the AGRPortfolioQuery that has just been accepted or rejected + + + + + Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant UTC offset. + + + + + The Period for which the AGR requests the portfolio information. + + + + + + + + + + + + + + + EntityAddress of the Connection. + + + + + + + + + A Connection that is part of the congestion point. + + + + EntityAddress of the Connection. + + + + + The internet domain of the AGR that represents the prosumer connected on this Connection, if applicable. + + + + + + + + + + diff --git a/api/src/main/resources/UFTP-cro.xsd b/api/src/main/resources/UFTP-cro.xsd index 0151c87..6b70660 100644 --- a/api/src/main/resources/UFTP-cro.xsd +++ b/api/src/main/resources/UFTP-cro.xsd @@ -1,13 +1,6 @@ - - - - - - + + + diff --git a/api/src/main/resources/UFTP-dso.xsd b/api/src/main/resources/UFTP-dso.xsd index a947767..3a86685 100644 --- a/api/src/main/resources/UFTP-dso.xsd +++ b/api/src/main/resources/UFTP-dso.xsd @@ -1,13 +1,6 @@ - - - - - - + + + diff --git a/api/src/main/resources/UFTP-metering.xsd b/api/src/main/resources/UFTP-metering.xsd index fb13c92..3ba52e5 100644 --- a/api/src/main/resources/UFTP-metering.xsd +++ b/api/src/main/resources/UFTP-metering.xsd @@ -1,170 +1,157 @@ - - - - + - - - - - - - - - - - - - - - - - kW must be used with Power profile values. - - - - - kWh must be used with energy profile values (ImportEnergy,ExportEnergy,ImportMeterReading,ExportMeterReading). - - - - - - - - - - - - The average active power during ISP, considering both import and export energy. Power=(ImportEnergy-ExportEnergy)*(60/ISP-Length-Minutes). - For example with a 15 minute ISP length we have a multiplier of 4, with a 30 minute ISP length we have a multiplier of 2. - Including the power profile is recommended. It is expected that in the following major version the power will become a mandatory value. - - - - - - Imported active energy, consumed during the ISP - - - - - Exported active energy, generated during the ISP - - - - - Cumulative metered imported active energy reading, at the end of the ISP - - - + + + + + + + + + + + + + + + + + kW must be used with Power profile values. + + + + + kWh must be used with energy profile values (ImportEnergy,ExportEnergy,ImportMeterReading,ExportMeterReading). + + + + + + + + + + + + The average active power during ISP, considering both import and export energy. Power=(ImportEnergy-ExportEnergy)*(60/ISP-Length-Minutes). + For example with a 15 minute ISP length we have a multiplier of 4, with a 30 minute ISP length we have a multiplier of 2. + Including the power profile is recommended. It is expected that in the following major version the power will become a mandatory value. + + + + + + Imported active energy, consumed during the ISP + + + + + Exported active energy, generated during the ISP + + + + + Cumulative metered imported active energy reading, at the end of the ISP + + + + + Cumulative metered exported active energy reading, at the end of the ISP + + + + + + + + + + + + + The message must contain one or more profiles with metering, energy or price values. + + + + + + Revision of this message. A sequence number that must be incremented each time a new revision of a metering message is sent. + + + + + ISO 8601 time interval (minutes only, for example PT15M) indicating the duration of the ISPs referenced in this message. Although the ISP length is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant ISP duration. + + + + + Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant UTC offset. + + + + + ISO 4217 code indicating the currency that applies to the price of the Tariff Rates. Only required if ImportTariff or ExportTariff profiles are included. + + + + + Day (in yyyy-mm-dd format) the ISPs referenced in this Metering message belong to. + + + + + EAN of the meter the message applies to. + + + + + + + + + - Cumulative metered exported active energy reading, at the end of the ISP + A profile carries a sequence of ISPs with a defined type of metering data - - - - - - - - - - - The message must contain one or more profiles with metering, energy or price values. - - + - - - Revision of this message. A sequence number that must be incremented each time a new revision of a metering message is sent. - - - - - ISO 8601 time interval (minutes only, for example PT15M) indicating the duration of the ISPs referenced in this message. Although the ISP length is a market-wide fixed - value, making this assumption explicit in each message is important for validation purposes, allowing implementations to reject messages with an errant ISP duration. - - - - - - Time zone ID (as per the IANA time zone database, http://www.iana.org/time-zones, for example: Europe/Amsterdam) indicating the UTC offset that applies to the Period - referenced in this message. Although the time zone is a market-wide fixed value, making this assumption explicit in each message is important for validation purposes, allowing - implementations to reject messages with an errant UTC offset. - - - - - - ISO 4217 code indicating the currency that applies to the price of the Tariff Rates. Only required if ImportTariff or ExportTariff profiles are included. - - - - - - Day (in yyyy-mm-dd format) the ISPs referenced in this Metering message belong to. - - - - - EAN of the meter the message applies to. - + + + + + + + + + + Number of the ISP this element refers to. The first ISP of a day has number 1. + - - - - - - - - - A profile carries a sequence of ISPs with a defined type of metering data - - - - - - - - - - - - - - Number of the ISP this element refers to. The first ISP of a day has number 1. - - - - - Metering, energy or price value at the end of this ISP, in the designated profile units. - - - - - - - - - - - - MessageID of the Metering message this response is based on. - + + + Metering, energy or price value at the end of this ISP, in the designated profile units. + - - - - - - + + + + + + + + + + MessageID of the Metering message this response is based on. + + + + + + + + \ No newline at end of file diff --git a/core/src/main/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOfferValidator.java b/core/src/main/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOfferValidator.java new file mode 100644 index 0000000..b3379b2 --- /dev/null +++ b/core/src/main/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOfferValidator.java @@ -0,0 +1,38 @@ +// Copyright 2025 Contributors to the Shapeshifter project +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.shapeshifter.core.service.validation.base; + +import lombok.RequiredArgsConstructor; +import org.lfenergy.shapeshifter.api.FlexOffer; +import org.lfenergy.shapeshifter.api.PayloadMessageType; +import org.lfenergy.shapeshifter.core.model.UftpMessage; +import org.lfenergy.shapeshifter.core.service.validation.UftpValidator; +import org.lfenergy.shapeshifter.core.service.validation.ValidationOrder; + +@RequiredArgsConstructor +public class UnsolicitedFlexOfferValidator implements UftpValidator { + + @Override + public boolean appliesTo(Class clazz) { + return FlexOffer.class == clazz; + } + + @Override + public int order() { + return ValidationOrder.SPEC_MESSAGE_SPECIFIC; + } + + @Override + public boolean isValid(UftpMessage uftpMessage) { + var flexOffer = uftpMessage.payloadMessage(); + + return flexOffer.isUnsolicited() == null || (flexOffer.getFlexRequestMessageID() == null) == flexOffer.isUnsolicited(); + } + + @Override + public String getReason() { + return "FlexRequestMessageID must (only) be present if not unsolicited"; + } +} diff --git a/core/src/main/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOrderValidator.java b/core/src/main/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOrderValidator.java new file mode 100644 index 0000000..daa5d9e --- /dev/null +++ b/core/src/main/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOrderValidator.java @@ -0,0 +1,39 @@ +// Copyright 2025 Contributors to the Shapeshifter project +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.shapeshifter.core.service.validation.base; + +import lombok.RequiredArgsConstructor; +import org.lfenergy.shapeshifter.api.FlexOrder; +import org.lfenergy.shapeshifter.api.PayloadMessageType; +import org.lfenergy.shapeshifter.core.model.UftpMessage; +import org.lfenergy.shapeshifter.core.service.validation.UftpValidator; +import org.lfenergy.shapeshifter.core.service.validation.ValidationOrder; + + +@RequiredArgsConstructor +public class UnsolicitedFlexOrderValidator implements UftpValidator { + + @Override + public boolean appliesTo(Class clazz) { + return FlexOrder.class == clazz; + } + + @Override + public int order() { + return ValidationOrder.SPEC_MESSAGE_SPECIFIC; + } + + @Override + public boolean isValid(UftpMessage uftpMessage) { + var flexOrder = uftpMessage.payloadMessage(); + + return (flexOrder.getFlexOfferMessageID() == null) == flexOrder.isUnsolicited(); + } + + @Override + public String getReason() { + return "FlexOfferMessageID must (only) be present if not unsolicited"; + } +} diff --git a/core/src/test/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOfferValidatorTest.java b/core/src/test/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOfferValidatorTest.java new file mode 100644 index 0000000..a35e140 --- /dev/null +++ b/core/src/test/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOfferValidatorTest.java @@ -0,0 +1,96 @@ +// Copyright 2025 Contributors to the Shapeshifter project +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.shapeshifter.core.service.validation.base; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.lfenergy.shapeshifter.api.FlexOffer; +import org.lfenergy.shapeshifter.api.TestMessage; +import org.lfenergy.shapeshifter.api.USEFRoleType; +import org.lfenergy.shapeshifter.core.model.UftpMessage; +import org.lfenergy.shapeshifter.core.model.UftpParticipant; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(MockitoExtension.class) +class UnsolicitedFlexOfferValidatorTest { + + private static final String FLEX_REQUEST_MESSAGE_ID = "FLEX_REQUEST_MESSAGE_ID"; + + private final UftpParticipant sender = new UftpParticipant("agr.org", USEFRoleType.AGR); + + private final UnsolicitedFlexOfferValidator validator = new UnsolicitedFlexOfferValidator(); + + @Test + void appliesTo() { + assertThat(validator.appliesTo(FlexOffer.class)).isTrue(); + } + + @Test + void notAppliesTo() { + // Not necessary to test with all types. Is tested on base class and by testing the map. + assertThat(validator.appliesTo(TestMessage.class)).isFalse(); + } + + @Test + void isValid_Unsolicited_null_FlexRequestMessageID_null() { + var flexOffer = new FlexOffer(); + flexOffer.setUnsolicited(null); + flexOffer.setFlexRequestMessageID(null); + + assertThat(validator.isValid(createUftpMessage(flexOffer))).isTrue(); + } + + @Test + void isValid_Unsolicited_null_FlexRequestMessageID_not_null() { + var flexOffer = new FlexOffer(); + flexOffer.setUnsolicited(null); + flexOffer.setFlexRequestMessageID(FLEX_REQUEST_MESSAGE_ID); + + assertThat(validator.isValid(createUftpMessage(flexOffer))).isTrue(); + } + + @Test + void isValid_Unsolicited_true_null_FlexRequestMessageID_null() { + var flexOffer = new FlexOffer(); + flexOffer.setUnsolicited(true); + flexOffer.setFlexRequestMessageID(null); + + assertThat(validator.isValid(createUftpMessage(flexOffer))).isTrue(); + } + + @Test + void isValid_Unsolicited_true_FlexRequestMessageID_not_null() { + var flexOffer = new FlexOffer(); + flexOffer.setUnsolicited(true); + flexOffer.setFlexRequestMessageID(FLEX_REQUEST_MESSAGE_ID); + + assertThat(validator.isValid(createUftpMessage(flexOffer))).isFalse(); + } + + @Test + void isValid_Unsolicited_false_FlexRequestMessageID_null() { + var flexOffer = new FlexOffer(); + flexOffer.setUnsolicited(false); + flexOffer.setFlexRequestMessageID(null); + + assertThat(validator.isValid(createUftpMessage(flexOffer))).isFalse(); + } + + @Test + void isValid_Unsolicited_false_FlexRequestMessageID_not_null() { + var flexOffer = new FlexOffer(); + flexOffer.setUnsolicited(false); + flexOffer.setFlexRequestMessageID(FLEX_REQUEST_MESSAGE_ID); + + assertThat(validator.isValid(createUftpMessage(flexOffer))).isTrue(); + } + + private UftpMessage createUftpMessage(FlexOffer flexOffer) { + return UftpMessage.createIncoming(sender, flexOffer, null, null); + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOrderValidatorTest.java b/core/src/test/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOrderValidatorTest.java new file mode 100644 index 0000000..1159790 --- /dev/null +++ b/core/src/test/java/org/lfenergy/shapeshifter/core/service/validation/base/UnsolicitedFlexOrderValidatorTest.java @@ -0,0 +1,96 @@ +// Copyright 2025 Contributors to the Shapeshifter project +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.shapeshifter.core.service.validation.base; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.lfenergy.shapeshifter.api.FlexOrder; +import org.lfenergy.shapeshifter.api.TestMessage; +import org.lfenergy.shapeshifter.api.USEFRoleType; +import org.lfenergy.shapeshifter.core.model.UftpMessage; +import org.lfenergy.shapeshifter.core.model.UftpParticipant; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(MockitoExtension.class) +class UnsolicitedFlexOrderValidatorTest { + + private static final String FLEX_OFFER_MESSAGE_ID = "FLEX_OFFER_MESSAGE_ID"; + + private final UftpParticipant sender = new UftpParticipant("dso.org", USEFRoleType.DSO); + + private final UnsolicitedFlexOrderValidator validator = new UnsolicitedFlexOrderValidator(); + + @Test + void appliesTo() { + assertThat(validator.appliesTo(FlexOrder.class)).isTrue(); + } + + @Test + void notAppliesTo() { + // Not necessary to test with all types. Is tested on base class and by testing the map. + assertThat(validator.appliesTo(TestMessage.class)).isFalse(); + } + + @Test + void isValid_Unsolicited_null_FlexOfferMessageID_null() { + var flexOrder = new FlexOrder(); + flexOrder.setUnsolicited(null); + flexOrder.setFlexOfferMessageID(null); + + assertThat(validator.isValid(createUftpMessage(flexOrder))).isFalse(); + } + + @Test + void isValid_backwards_compatibility_Unsolicited_null_FlexOfferMessageID_not_null() { + var flexOrder = new FlexOrder(); + flexOrder.setUnsolicited(null); + flexOrder.setFlexOfferMessageID(FLEX_OFFER_MESSAGE_ID); + + assertThat(validator.isValid(createUftpMessage(flexOrder))).isTrue(); + } + + @Test + void isValid_Unsolicited_true_null_FlexOfferMessageID_null() { + var flexOrder = new FlexOrder(); + flexOrder.setUnsolicited(true); + flexOrder.setFlexOfferMessageID(null); + + assertThat(validator.isValid(createUftpMessage(flexOrder))).isTrue(); + } + + @Test + void isValid_Unsolicited_true_FlexOfferMessageID_not_null() { + var flexOrder = new FlexOrder(); + flexOrder.setUnsolicited(true); + flexOrder.setFlexOfferMessageID(FLEX_OFFER_MESSAGE_ID); + + assertThat(validator.isValid(createUftpMessage(flexOrder))).isFalse(); + } + + @Test + void isValid_Unsolicited_false_FlexOfferMessageID_null() { + var flexOrder = new FlexOrder(); + flexOrder.setUnsolicited(false); + flexOrder.setFlexOfferMessageID(null); + + assertThat(validator.isValid(createUftpMessage(flexOrder))).isFalse(); + } + + @Test + void isValid_Unsolicited_false_FlexOfferMessageID_not_null() { + var flexOrder = new FlexOrder(); + flexOrder.setUnsolicited(false); + flexOrder.setFlexOfferMessageID(FLEX_OFFER_MESSAGE_ID); + + assertThat(validator.isValid(createUftpMessage(flexOrder))).isTrue(); + } + + private UftpMessage createUftpMessage(FlexOrder flexOrder) { + return UftpMessage.createIncoming(sender, flexOrder, null, null); + } + +} \ No newline at end of file diff --git a/spring/src/main/java/org/lfenergy/shapeshifter/spring/config/ShapeshifterConfiguration.java b/spring/src/main/java/org/lfenergy/shapeshifter/spring/config/ShapeshifterConfiguration.java index 528eee6..d81dc95 100644 --- a/spring/src/main/java/org/lfenergy/shapeshifter/spring/config/ShapeshifterConfiguration.java +++ b/spring/src/main/java/org/lfenergy/shapeshifter/spring/config/ShapeshifterConfiguration.java @@ -157,7 +157,9 @@ public UftpValidationService uftpValidationService(ParticipantSupport participan new FlexOrderPriceMatchValidator(uftpMessageSupport), new IspPowerDiscrepancyValidator(), new IspRequestedDispositionValidator(), - new MinActivationFactorValidator() + new MinActivationFactorValidator(), + new UnsolicitedFlexOfferValidator(), + new UnsolicitedFlexOrderValidator() )); validators.addAll(uftpUserDefinedValidators);