From 72932f8a6dcb3dcc4f1e426dde6ed529769cab90 Mon Sep 17 00:00:00 2001 From: Christopher Atkins Date: Thu, 21 Mar 2024 20:18:19 +0000 Subject: [PATCH] tolerate partial success --- cspell.json | 1 + .../schemas/forecast-limit-period.yaml | 4 +- docs/_data/components/schemas/headers.yaml | 3 + .../components/schemas/period-start.yaml | 25 +++--- docs/articles/forecast-windows.md | 6 +- .../forecast-ratings-proposal-patch.json | 1 + ...cast-ratings-proposal-status-complete.json | 11 +++ .../forecast-ratings-proposal-status.json | 2 +- .../submitting-forecasts.md | 88 +++++++++++++++---- 9 files changed, 106 insertions(+), 35 deletions(-) create mode 100644 docs/example-narratives/examples/forecast-ratings-proposal-status-complete.json diff --git a/cspell.json b/cspell.json index ed67f06..6fea313 100644 --- a/cspell.json +++ b/cspell.json @@ -26,6 +26,7 @@ ,"openapi" ,"overvoltage" ,"owasp" + ,"Postel" ,"pseudocode" ,"redoc" ,"Redocly" diff --git a/docs/_data/components/schemas/forecast-limit-period.yaml b/docs/_data/components/schemas/forecast-limit-period.yaml index 27fbf60..e37acab 100644 --- a/docs/_data/components/schemas/forecast-limit-period.yaml +++ b/docs/_data/components/schemas/forecast-limit-period.yaml @@ -1,10 +1,10 @@ period: type: object properties: - period-end: - $ref: ./period-start.yaml period-start: $ref: ./period-start.yaml + period-end: + $ref: ./period-start.yaml required: - period-start - period-end diff --git a/docs/_data/components/schemas/headers.yaml b/docs/_data/components/schemas/headers.yaml index 7955ff8..a18098e 100644 --- a/docs/_data/components/schemas/headers.yaml +++ b/docs/_data/components/schemas/headers.yaml @@ -10,6 +10,8 @@ proposal-header: &header properties: source: $ref: ./data-provenance.yaml + proposal-begins: + $ref: ./period-start.yaml default-emergency-durations: $ref: ./array-max-emergency-durations.yaml#/emergency-durations power-system-resources: @@ -17,6 +19,7 @@ proposal-header: &header required: - source + - proposal-begins - default-emergency-durations - power-system-resources additionalProperties: false diff --git a/docs/_data/components/schemas/period-start.yaml b/docs/_data/components/schemas/period-start.yaml index 9df4bed..780e690 100644 --- a/docs/_data/components/schemas/period-start.yaml +++ b/docs/_data/components/schemas/period-start.yaml @@ -1,27 +1,22 @@ -description: > - RFC 3339 date-time string with *no fractional seconds component* that - designates an operating period (such as an hour) that - - starts at a specified time. This will frequently be at the start of an hour, - but may be finer-grained, such as - - every 30 minutes, should the Transmission Provider choose. +description: | - If the Transmission Provider is operating in EST, these are + RFC 3339 date-time string with *no fractional seconds component* that + designates a start or end to an operating period (such as an hour) that starts + at a specified time. This will frequently be at the start of an hour, but may + be finer-grained, such as every 30 minutes, should the Clearinghouse Provider + choose. - valid and equivalent values: + If the Transmission Provider is operating in EST, these are valid and + equivalent values: * 2023-01-01T06:00Z - * 2023-01-01T01:00-5:00 - * 2023-01-01T00:00-6:00 - * 2023-01-01T11:30+5:30 - The server should uniformly represent date-times in the operational time zone - of the Transmission Provider. + of the Clearinghouse Provider. + type: string format: date-time maxLength: 25 diff --git a/docs/articles/forecast-windows.md b/docs/articles/forecast-windows.md index cd2be0f..603fe48 100644 --- a/docs/articles/forecast-windows.md +++ b/docs/articles/forecast-windows.md @@ -42,6 +42,11 @@ Forecast Window deadline, the TROLIE server returns `202 Accepted` to confirm that the `PATCH` contained a valid proposal and will be processed by the Clearinghouse. +{: .nb } +> **NOTE**: The Ratings Provider populates the `proposal-header.proposal-begins` +> to indicate the intended Forecast Window. Accordingly, the TROLIE server +> checks `proposal-header.proposal-begins` to determine if the proposal is late or not. + ### Late `409 Conflict` Again, submitting a Ratings Forecast Proposal to TROLIE is done by `PATCH`ing @@ -55,7 +60,6 @@ with the server's state, so a `409 Conflict` client error with an appropriate {% include_relative examples/forecast-proposal-invalid-units.json %} ``` - ## Forecast Processing State Machine > When one window closes, another one opens. diff --git a/docs/example-narratives/examples/forecast-ratings-proposal-patch.json b/docs/example-narratives/examples/forecast-ratings-proposal-patch.json index 3d47908..a08532f 100644 --- a/docs/example-narratives/examples/forecast-ratings-proposal-patch.json +++ b/docs/example-narratives/examples/forecast-ratings-proposal-patch.json @@ -5,6 +5,7 @@ "provider": "UTILITY-A", "origin-id": "5aeacb25-9b65-4738-8a00-ac10afa63640" }, + "proposal-begins": "2025-11-01T01:00:00-05:00", "default-emergency-durations": [ { "name": "lte", diff --git a/docs/example-narratives/examples/forecast-ratings-proposal-status-complete.json b/docs/example-narratives/examples/forecast-ratings-proposal-status-complete.json new file mode 100644 index 0000000..fc22382 --- /dev/null +++ b/docs/example-narratives/examples/forecast-ratings-proposal-status-complete.json @@ -0,0 +1,11 @@ +{ + "forecast-provider": { + "provider": "UTILITY-A", + "last-updated": "2023-07-12T15:05:43.044267100-07:00", + "origin-id": "5aeacb25-9b65-4738-8a00-ac10afa63640" + }, + "incomplete-obligation-count": 0, + "incomplete-obligations": [], + "invalid-proposal-count": 0, + "proposal-validation-errors": [] +} diff --git a/docs/example-narratives/examples/forecast-ratings-proposal-status.json b/docs/example-narratives/examples/forecast-ratings-proposal-status.json index e341380..78880f2 100644 --- a/docs/example-narratives/examples/forecast-ratings-proposal-status.json +++ b/docs/example-narratives/examples/forecast-ratings-proposal-status.json @@ -4,7 +4,7 @@ "last-updated": "2023-07-12T15:05:43.044267100-07:00", "origin-id": "5aeacb25-9b65-4738-8a00-ac10afa63640" }, - "incomplete-obligation-count": 150, + "incomplete-obligation-count": 1, "incomplete-obligations": [ { "resource-id": "8badf00d", "alternate-identifiers": [ diff --git a/docs/example-narratives/submitting-forecasts.md b/docs/example-narratives/submitting-forecasts.md index 383df69..882f5ec 100644 --- a/docs/example-narratives/submitting-forecasts.md +++ b/docs/example-narratives/submitting-forecasts.md @@ -11,7 +11,7 @@ This article assume some familiarity with HTTP in general and RESTful APIs in particular ([background](../articles/trolie-for-ems-and-ot)). -### Quick Links +### Scenario Quick Links {:.no_toc} * toc @@ -21,25 +21,24 @@ APIs in particular ([background](../articles/trolie-for-ems-and-ot)). ## Simplified Example: Transmission Owner Sends Forecast with `curl` -If a Transmission Owner is their own Ratings Provider, they must regularly -regularly (at least hourly) send an Ratings Forecast to their Transmission -Provider. TROLIE provides the +If a Transmission Owner is their own Ratings Provider, they must regularly send +an Ratings Forecast to their Transmission Provider. TROLIE provides the [patchRatingForecastProposal](../spec#tag/Rating-Proposals/operation/patchRatingForecastProposal) operation for this purpose. Assume the Transmission Owner creates a file called `input.json` containing -their forecast. An example of the required format for the file is given below. The format is -one of TROLIE's [supported media types](../articles/supported-media-types) -named `application/vnd.trolie.rating-forecast-proposal.v1+json`. - +their forecast. An example of the required format for the file is given below. ```json {% include_relative examples/forecast-ratings-proposal-patch.json %} ``` -(This example also illustrates handling the fall Daylight Savings transition -in the Central timezone.) +This example also illustrates handling the fall Daylight Savings transition in +the Central timezone. The format is one of TROLIE's [supported media +types](../articles/supported-media-types) named +`application/vnd.trolie.rating-forecast-proposal.v1+json`. + ### Pushing `input.json` to TROLIE with `curl` {:.no_toc} @@ -55,16 +54,73 @@ curl -d @input.json \ "https://trolie.example.com/rating-proposals/forecast" ``` -If this submission is successful, `output.json` will contain a the contents of -the response from TROLIE. The format of the response is defined by another -TROLIE media type: -`application/vnd.trolie.rating-forecast-proposal-status.v1+json`. An example of -this response format is given below: +If this submission is successful, `output.json` will contain the contents of the +response from TROLIE. The format of the response is defined by another TROLIE +media type: `application/vnd.trolie.rating-forecast-proposal-status.v1+json`. An +example of this response format is given below: ```json +{% include_relative examples/forecast-ratings-proposal-status-complete.json %} +``` + + +## Invalid Forecasts for Individual Resources Should be Tolerated + +The Robustness Principle, or Postel's Law, states: Be conservative in what you +send and liberal in what you accept. In keeping with this principle, TROLIE +implementations should *not* reject a forecast proposal `PATCH` when only the +forecast for any individual resource is incomplete or invalid. In other words +the Clearinghouse Provider should abide a best effort policy when validating +proposals, and this specification aims to support that policy in an unambiguous +way. + +A specific example will help illustrate the idea. Suppose the Ratings Provider +submits a Forecast Proposal for two resources--`8badf00d` and `d34dc0d3`. +Further suppose that there's nothing wrong at all with the `d34dc0d3` forecast, +but the `8badf00d` forecast is missing an hour, with everything else about the +request being valid. In this case TROLIE should a return response like the +following: + +```http +HTTP/1.1 202 Accepted +Content-Type: application/vnd.trolie.rating-forecast-proposal-status.v1+json +Server: trolie.example.com +Date: Wed, 29 Feb 2024 12:03:20 GMT +ETag: "123e4567e89b12d3a456426614174000" +X-Rate-Limit-Limit: 100 +X-Rate-Limit-Remaining: 97 +X-Rate-Limit-Reset: 3400 + + {% include_relative examples/forecast-ratings-proposal-status.json %} ``` -### Pushing `input.json` to TROLIE with `curl` +### Client Errors are Not Acceptable {:.no_toc} +Bear in mind the proposal must always be [on-time](/articles/forecast-windows.html#on-time--202-accepted). Moreover, there are other client errors that are not tolerated, including: + +* Malformed requests, i.e., the JSON provided is not valid according to the + media type schema. +* Unprocessable content error: when the Forecast Proposal is well-formed, but + the [units provided in any of the forecasts are + invalid](/articles/how-units-are-handled#validation). +* Unprocessable content error: when none of the individual resource Forecast + Proposals are valid, but the request is otherwise well-formed. + +Additional client errors are identified in the [patchRatingForecastProposal spec](../spec#tag/Rating-Proposals/operation/patchRatingForecastProposal). + +### Clients Should Check the `incomplete-obligation-count` +{:.no_toc} + +The flip-side of this accommodative approach is that clients will not receive an +error response when one of their resource forecasts is invalid, so the spec +defines `incomplete-obligation-count`: + +> {{ site.data.components.schemas["array-max-monitored-elements"].forecast-proposal-status.properties.incomplete-obligation-count.description }} + +## Multiple Submissions per Forecast Window + +{{ site.data.paths["rating-proposals_forecasts"].patch.description }} + +