Closed darnjo closed 1 year ago
More detailed notes and examples for review.
This is a proposed solution to media upload.
These are the required fields to represent the upload process for media
<EntityType Name="Media">
...
<Property Name="PresignedUrl" Type="Edm.String" />
<Property Name="PresignedUrlExpiry" Type="Edm.DateTimeOffset" />
<Property Name="MediaStatus" Type="Edm.String" />
<Property Name="MediaStatusDescription" Type="Edm.String" />
...
</EntityType>
The status of the media will be tracked on the Media
resource. The MediaStatus
field will show the state of the media.
Incomplete
- the Media
record that does not have a complete record (typically missing byte array)Processing
- the Media
record is currently being processed by the backendComplete
- the Media
record is complete and can be used with it's related resourceDeleted
- the Media
is no longer published with its related resourcesRejected
- the Media
has failed post processing for some reason. Details of the problem will be presented in the MediaStatusDescription
field.Some examples of Rejected
reason could be:
MediaType
(Sent a video for a photo Media record)The creation of the media resource will be done using as a standard Odate Update Process. (Defined in RCP-10). The post should included all known metadata about the media with the exception of only the media byte stream.
If the media is a reference to external URL reference, the server should ingest the MediaURL
field in the post. The PresignedUrl
related fields will not be populated in these cases.
Create the initial media record using the oData standard. This could be an expanded record on another resource a secondary approach but the example will be with Media
as in tier 1 Resource/Model.
REQUEST
POST https://api.my-webapi.io/Media
OData-Version: 4.01
Content-Type: application/json
Accept: application/json
Prefer: return=representation
{
...
"Caption": "Ipsum Lorum",
"Order": 1,
"ImageType": "image/jpeg",
...
]
}
RESPONSE
HTTP/2 201 Created
OData-Version: 4.01
EntityId: "12345"
Location: https://api.my-webapi.io/Media('12345')
Content-Length: 200
Content-Type: application/json
Preference-Applied: return=representation
{
"@odata.context":"https://api.my-webapi.io/$metadata#Media/$entity",
"@odata.id":"https://api.my-webapi.io/Media('12345')",
"@odata.editLink":"https://api.webapi.io/Media('12345')",
"@odata.etag": "W/\"aBcDeFgHiJkLmNoPqRsTuVwXyz\"",
"MediaObjectID": "12345",
"Caption": "Ipsum Lorum",
"Order": 1,
"ImageType": "image/jpeg",
...
"MediaStatus": "Incomplete",
"MediaStatsDescription": "Awaiting Byte Stream",
"PresignedUrl": "https://storage.my-webapi.io/media/12345.jpg?authentication_token=my-one-time-use-auth-token-12345-zyxwut",
"PresignedUrlExpiry": "2023-08-01T13:00:00+00:00",
}
The return from the POST will return a pre-signed URL with an validity window for that URL.
Thie bytestrem upload will be a simple HTTP POST transaction to the provided to the endpoint with no other required data. No external fields, headers or payloads are required to post the media stream. All requirements for the POST must be part of the PresignedUrl
REQUEST
POST https://storage.my-webapi.io/media/12345.jpg?authentication_token=my-one-time-use-auth-token=12345-zyxwut
Content-Type: image/jpeg
<<Byte stream of media object>>
RESPONSE
HTTP/2 200 OK
The original media record will transition to Processing
state and blank the PresignedUrl
field while the server prepares the media for distribute downstream. The implementation will do all work required for distribution before changing the state to Complete
If any processing fails, the media record will be have a status of Rejected
and the reasoning for the Media
submitter will be populated in the MediaStatusDescription
field for actions to be taken.
Rules can be written against the MediaStatus
field
The media record can up queries to confirm the media has successfully been processed.
REQUEST
POST https://api.my-webapi.io/Media('12345')
OData-Version: 4.01
Content-Type: application/json
Accept: application/json
Prefer: return=representation
RESPONSE
HTTP/2 200
OData-Version: 4.01
EntityId: "12345"
Location: https://api.my-webapi.io/Media('12345')
Content-Length: 200
Content-Type: application/json
Preference-Applied: return=representation
{
"@odata.context":"https://api.my-webapi.io/$metadata#Media/$entity",
"@odata.id":"https://api.my-webapi.io/Media('12345')",
"@odata.editLink":"https://api.webapi.io/Media('12345')",
"@odata.etag": "W/\"aBcDeFgHiJkLmNoPqRsTuVwXyz\"",
"MediaObjectID": "12345",
"Caption": "Ipsum Lorum",
"Order": 1,
"ImageType": "image/jpeg",
...
"MediaStatus": "Complete",
"MediaStatsDescription": "Processing Complete",
"PresignedUrl": null,
"PresignedUrlExpiry": null,
}
These are the cases where the media record will not reach the Complete
state without additional actions.
This will leave the media record in a Incomplete
state and the vendor can do culling/clean up as required. The client can requery the Media
resource for the PresignedUrl
to attempt to re-use the Media
record. Rules can be used to prevent the transition of the listing until valid media is provided if required.
REQUEST
POST https://api.my-webapi.io/Media('12345')
OData-Version: 4.01
Content-Type: application/json
Accept: application/json
Prefer: return=representation
RESPONSE
HTTP/2 200
OData-Version: 4.01
EntityId: "12345"
Location: https://api.my-webapi.io/Media('12345')
Content-Length: 200
Content-Type: application/json
Preference-Applied: return=representation
{
"@odata.context":"https://api.my-webapi.io/$metadata#Media/$entity",
"@odata.id":"https://api.my-webapi.io/Media('12345')",
"@odata.editLink":"https://api.webapi.io/Media('12345')",
"@odata.etag": "W/\"aBcDeFgHiJkLmNoPqRsTuVwXyz\"",
"MediaObjectID": "12345",
"Caption": "Ipsum Lorum",
"Order": 1,
"ImageType": "image/jpeg",
...
"MediaStatus": "Incomplete",
"MediaStatsDescription": "Awaiting Byte Stream",
"PresignedUrl": "https://storage.my-webapi.io/media/12345.jpg?authentication_token=my-one-time-use-auth-token-12345-zyxwut",
"PresignedUrlExpiry": "2023-08-01T13:00:00+00:00",
}
If a Media
record's PresignedUrlExpiry
time is reached, the server can invalidate the media record by blanking the PresignedUrl
and PresignedUrlExpiry
fields and setting the MediaStatus
to Rejected
or providing updated the Presigned*
fields to re-use the Media
record
REQUEST
POST https://api.my-webapi.io/Media('12345')
OData-Version: 4.01
Content-Type: application/json
Accept: application/json
Prefer: return=representation
RESPONSE
HTTP/2 200
OData-Version: 4.01
EntityId: "12345"
Location: https://api.my-webapi.io/Media('12345')
Content-Length: 200
Content-Type: application/json
Preference-Applied: return=representation
{
"@odata.context":"https://api.my-webapi.io/$metadata#Media/$entity",
"@odata.id":"https://api.my-webapi.io/Media('12345')",
"@odata.editLink":"https://api.webapi.io/Media('12345')",
"@odata.etag": "W/\"aBcDeFgHiJkLmNoPqRsTuVwXyz\"",
"MediaObjectID": "12345",
"Caption": "Ipsum Lorum",
"Order": 1,
"ImageType": "image/jpeg",
...
"MediaStatus": "Incomplete",
"MediaStatsDescription": "Awaiting Byte Stream",
"PresignedUrl": "https://storage.my-webapi.io/media/12345.jpg?authentication_token=my-one-time-use-auth-token-12345-abcdefg",
"PresignedUrlExpiry": "2023-09-01T14:00:00+00:00",
}
REQUEST
POST https://api.my-webapi.io/Media('12345')
OData-Version: 4.01
Content-Type: application/json
Accept: application/json
Prefer: return=representation
RESPONSE
HTTP/2 200
OData-Version: 4.01
EntityId: "12345"
Location: https://api.my-webapi.io/Media('12345')
Content-Length: 200
Content-Type: application/json
Preference-Applied: return=representation
{
"@odata.context":"https://api.my-webapi.io/$metadata#Media/$entity",
"@odata.id":"https://api.my-webapi.io/Media('12345')",
"@odata.editLink":"https://api.webapi.io/Media('12345')",
"@odata.etag": "W/\"aBcDeFgHiJkLmNoPqRsTuVwXyz\"",
"MediaObjectID": "12345",
"Caption": "Ipsum Lorum",
"Order": 1,
"ImageType": "image/jpeg",
...
"MediaStatus": "Rejected",
"MediaStatsDescription": "Media record has timed out",
"PresignedUrl": null,
"PresignedUrlExpiry": null,
}
If the media is rejected, the vendor can eithe allow for the record update by providing the Presigned*
fields or leave them blanked to indicate the record has write-once behaviour. Rules can enforce the media records be corrected/deleted before state of the parent resource can be transitioned.
REQUEST
POST https://api.my-webapi.io/Media('12345')
OData-Version: 4.01
Content-Type: application/json
Accept: application/json
Prefer: return=representation
RESPONSE
HTTP/2 200
OData-Version: 4.01
EntityId: "12345"
Location: https://api.my-webapi.io/Media('12345')
Content-Length: 200
Content-Type: application/json
Preference-Applied: return=representation
{
"@odata.context":"https://api.my-webapi.io/$metadata#Media/$entity",
"@odata.id":"https://api.my-webapi.io/Media('12345')",
"@odata.editLink":"https://api.webapi.io/Media('12345')",
"@odata.etag": "W/\"aBcDeFgHiJkLmNoPqRsTuVwXyz\"",
"MediaObjectID": "12345",
"Caption": "Ipsum Lorum",
"Order": 1,
"ImageType": "image/jpeg",
...
"MediaStatus": "Rejected",
"MediaStatsDescription": "Byte stream is not of type image/jpeg",
"PresignedUrl": "https://storage.my-webapi.io/media/12345-attempt2.jpg?authentication_token=my-one-time-use-auth-token-12345-abcdefg",
"PresignedUrlExpiry": "2023-09-01T14:00:00+00:00",
}
REQUEST
POST https://api.my-webapi.io/Media('12345')
OData-Version: 4.01
Content-Type: application/json
Accept: application/json
Prefer: return=representation
RESPONSE
HTTP/2 200
OData-Version: 4.01
EntityId: "12345"
Location: https://api.my-webapi.io/Media('12345')
Content-Length: 200
Content-Type: application/json
Preference-Applied: return=representation
{
"@odata.context":"https://api.my-webapi.io/$metadata#Media/$entity",
"@odata.id":"https://api.my-webapi.io/Media('12345')",
"@odata.editLink":"https://api.webapi.io/Media('12345')",
"@odata.etag": "W/\"aBcDeFgHiJkLmNoPqRsTuVwXyz\"",
"MediaObjectID": "12345",
"Caption": "Ipsum Lorum",
"Order": 1,
"ImageType": "image/jpeg",
...
"MediaStatus": "Rejected",
"MediaStatsDescription": "Byte stream is not of type image/jpeg",
"PresignedUrl": null,
"PresignedUrlExpiry": null,
}
Thanks for putting this together. I think we should talk about some of this a bit.
Step 1
Post data to Media resource with all the metadata available for the image prior to uploading the base64 encoded image data.
Note: In this step there are two cases ... either the record that the Media is attached to is included in the initial creation of the Media metadata (ResourceName, ResourceRecordKey), or it isn't. If not, then that information needs to be provided at some point (after step 2).
Step 2
Once the Media record has been created, the client can upload the binary image data by obtaining the pre-signed URL attached to the Media item that was created. The pre-signed URLs will be generated as a result of step (1), and will have an expiration timestamp associated with them.
Media Statuses
We also need to define statuses in the Media Resource to reflect the various stages of processing.
If the record has been rejected for some reason, either processing failed or there was a compliance issue (for example), there will also be a StatusDescription to let clients know what failed.