[Specifications] Another take on non-RDF payloads (aka file upload) (#199)

tpluscode has just created a new issue for https://github.com/HydraCG/Specifications:

== Another take on non-RDF payloads (aka file upload) ==
## Describe the requirement

We've approached the problem of non-RDF media types a few times already. Unfortunately it seems that each time it was not focused enough. Either mixed with collections (#187) or lacking broader context (#186).

Looking back at both, I think they are on track, but need a little more refinement.

_For this issues, I'd like to focus on `expects` only and not returned representations_

## Hydra-agnostic example

I would distinguish 3 kinds of requests coming to a Hydra API:

1. RDF payloads - such that are currently described by `expects` and `Class`.

2. Non RDF-payloads

   Directly uploading an image instead of RDF:

   ```http
   POST /movie/123/poster-image HTTP/2
   Content-Type: image/png
   
   ...File bytes...
   ```

3. `multipart/form-data`

   Submitting multiple images **and** RDF data:

   ```http
   PUT /movie/123/image-gallery HTTP/2
   Content-Type: multipart/form-data; boundary=----hydra-content
   
   ----hydra-content
   Content-Disposition: form-data; filename="poster.png"
   Content-Type: image/png
   
   ...Poster image...
   ----hydra-content
   Content-Disposition: form-data; filename="cast.jpeg"
   Content-Type: image/jpeg
   
   ...Image of actors...
   ----hydra-content
   Content-Type: application/ld+json
   
   {
     "@type": "mov:Gallery",
     "description": {
       "@value": "Pictures for movie /movie/123"
     }
   }
   ----hydra-content
   ```

Hydra should allow describing operations which `expect` both kinds of file uploads. 

## Proposed solutions

It is important to keep support for the current `expect` semantics. 

I propose that we extend the existing structure with a media-type description. Unfortunately it is not possible to have it both ways without revolutionising the structure, so the vocab will have to remove `rdfs:range` from `expects` and use `schema:rangeIncludes` instead.

```diff
{
  "@id": "hydra:expects",
-  "range": "hydra:Class",
+  "schema:rangeIncludes": [
+    "hydra:Class",
+    "hydra:RequestSpecification"
+  ]
},
```

### Example of `hydra:Class` usage

```json
{
  "@type": "Operation",
  "expects": {
    "@type": "RequestSpecification",
    "content": {
      "@type": "SupportedClassContent",
      "class": "mov:Movie"
    }
  }
}
```

This would be equivalent to `"expects": "mov:Movie"` and both should be supported at least for a while.

### Example of non-RDF payload

```json
{
  "@type": "Operation",
  "expects": {
    "@type": "RequestSpecification",
    "content": {
      "@type": "RawContent",
      "supportedContentType": [ "image/png", "image/jpeg" ]
    }
  }
}
```

- `supportedContentType` could also use a more elaborate structure though I'm not conviced it's necessary. 

### Example of multipart

```json
{
  "@type": "Operation",
  "expects": {
    "@type": "RequestSpecification",
    "content": {
      "@type": "MultipartContent",
      "allowedParts": [
        {
          "supportedContentType": [ "image/png", "image/jpeg" ],
          "maxCount": 2
        },
        {
          "@type": "SupportedClassContent",
          "class": "mov:Movie",
          "minCount": 1,
          "maxCount": 1
        }
      ]
    }
  }
}
```

Above interpreted as:

- allowing 0-2 image parts
- requiring one RDF-part with `mov:Movie` 
-  `allowedParts` has same domain as `content`, extended with multipart-specific bit such as the min/max count

`MultipartContent` would have to become part of the core vocabulary.

## Implications

The consequences of such design are far reaching:

1. By introducing `RequestSpecification` we can directly describe HTTP requests (such as by using `expectHeader`)

2. The `content` predicat can be an extension point we've talked about, allowing 3rd party vocab to describe bodies using SHACL. Something like

   ​    `ShaclContentSpecification subclassOf ContentSpecification`

3. It will even be possible to define operations which expect markdown, plain text or any other textual format

## Alternative solutions

Here's how Open API does that for [file uploads](https://swagger.io/docs/specification/describing-request-body/file-upload/) and [multipart requests](https://swagger.io/docs/specification/describing-request-body/multipart-requests/). For example

```
requestBody:
  content: 
    multipart/form-data: # Media type
      schema:            # Request payload
        type: object
        properties:      # Request parts
          id:            # Part 1 (string value)
            type: string
            format: uuid
          address:       # Part2 (object)
            type: object
            properties:
              street:
                type: string
              city:
                type: string
          profileImage:  # Part 3 (an image)
            type: string
            format: binary
```

Note that `id` , `address` and `profileImage` will be separate request parts.

Please view or discuss this issue at https://github.com/HydraCG/Specifications/issues/199 using your GitHub account

Received on Wednesday, 17 July 2019 07:35:51 UTC