Skip to content

Commit a824bcc

Browse files
author
Benjamin Geer
authored
feat(api-v1): Change API v1 file uploads to work like API v2 (DSP-41, PR 3) (#1722)
1 parent 68e67f8 commit a824bcc

File tree

58 files changed

+1105
-3118
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1105
-3118
lines changed

docs/03-apis/api-v1/adding-resources.md

+72-88
Original file line numberDiff line numberDiff line change
@@ -47,67 +47,53 @@ The request header's content type has to be set to `application/json`.
4747

4848
## Adding Resources with Image Files
4949

50-
Certain resource classes can have attached image files. There are two ways to
51-
attach a file to a resource: Either by submitting directly the binaries of the file in a
52-
an HTTP Multipart request, or by indicating the location of the file. The two cases are referred to
53-
as non-GUI case and GUI case (see [Sipi and Knora](../../07-sipi/sipi-and-knora.md)).
54-
55-
### Including the binaries (non-GUI case)
56-
57-
In order to include the binaries, a HTTP Multipart request has to be
58-
sent. One part contains the JSON (same format as described for
59-
[Adding Resources Without Images Files](#adding-resources-without-a-digital-representation))
60-
and has to be named `json`. The other part contains the file's name, its binaries, and its mime type
61-
and has to be named `file`. The following example illustrates how to
62-
make this type of request using Python 3:
63-
64-
```python
65-
#!/usr/bin/env python3
66-
67-
import requests, json
68-
69-
# a Python dictionary that will be turned into a JSON object
70-
resourceParams = {
71-
'restype_id': 'http://www.knora.org/ontology/test#testType',
72-
'properties': {
73-
'http://www.knora.org/ontology/test#testtext': [
74-
{'richtext_value': {'utf8str': "test"}}
75-
],
76-
'http://www.knora.org/ontology/test#testnumber': [
77-
{'int_value': 1}
78-
]
79-
},
80-
'label': "test resource",
81-
'project_id': 'http://rdfh.ch/projects/testproject'
82-
}
83-
84-
# the name of the file to be submitted
85-
filename = "myimage.jpg"
50+
The first step is to upload an image file to Sipi, using a
51+
`multipart/form-data` request, where `sipihost` represents the host and
52+
port on which Sipi is running:
8653

87-
# a tuple containing the file's name, its binaries and its mimetype
88-
file = {'file': (filename, open(filename, 'rb'), "image/jpeg")} # use name "file"
54+
```
55+
HTTP POST to http://sipihost/upload?token=TOKEN
56+
```
8957

90-
# do a POST request providing both the JSON and the binaries
91-
r = requests.post("http://host/v1/resources",
92-
data={'json': json.dumps(resourceParams)}, # use name "json"
93-
files=file,
94-
auth=('user', 'password'))
58+
The `TOKEN` is the `sid` returned by Knora in response to the
59+
client's login request (see [Authentication](authentication.md)).
60+
The request must contain a body part providing the file as well as a parameter
61+
`filename`, providing the file's original filename, which both Knora and Sipi will
62+
store; these filenames can be descriptive and need not be unique.
63+
64+
Sipi will then convert the uploaded image file to JPEG 2000 format and store
65+
it in a temporary location. If this is successful, it will return a JSON
66+
response that looks something like this:
67+
68+
```json
69+
{
70+
"uploadedFiles": [{
71+
"originalFilename": "manuscript-1234-page-1.tiff",
72+
"internalFilename": "3UIsXH9bP0j-BV0D4sN51Xz.jp2",
73+
"temporaryBaseIIIFUrl": "http://sipihost/tmp"
74+
}]
75+
}
9576
```
9677

97-
Please note that the file has to be read in binary mode (by default it
98-
would be read in text mode).
78+
This provides:
9979

100-
### Indicating the location of a file (GUI case)
80+
- the `originalFilename`, which we submitted when uploading the file
81+
- the unique `internalFilename` that Sipi has randomly generated for the file
82+
- the `temporaryBaseIIIFUrl`, which we can use to construct a IIIF URL for
83+
previewing the file
10184

102-
This request works similarly to
103-
[Adding Resources Without Image Files](#adding-resources-without-a-digital-representation). The JSON format is described
104-
in the TypeScript interface `createResourceWithRepresentationRequest` in
105-
module `createResourceFormats`. The request header's content type has to
106-
set to `application/json`.
85+
The client may now wish to get a thumbnail of the uploaded image, to allow
86+
the user to confirm that the correct files have been uploaded. This can be done
87+
by adding the filename and IIIF parameters to `temporaryBaseIIIFUrl`. For example, to get
88+
a JPG thumbnail image whose width and height are at most 128 pixels wide, you would request
89+
`http://sipihost/tmp/3UIsXH9bP0j-BV0D4sN51Xz.jp2/full/!128,128/0/default.jpg`.
10790

108-
In addition to [Adding Resources Without Image Files](#adding-resources-without-a-digital-representation), the
109-
(temporary) name of the file, its original name, and mime type have to
110-
be provided (see [GUI Case](../../07-sipi/sipi-and-knora.md#gui-case)).
91+
The request to Knora works similarly to
92+
[Adding Resources Without Image Files](#adding-resources-without-image-files),
93+
with the addition of `file`, whose value is the `internalFilename` that Sipi returned.
94+
See the TypeScript interface `createResourceWithRepresentationRequest` in
95+
module `createResourceFormats` for details. The request header's content type must be
96+
set to `application/json`.
11197

11298
## Response to a Resource Creation
11399

@@ -122,7 +108,7 @@ The JSON format of the response is described in the TypeScript interface
122108
## Changing a Resource's Label
123109

124110
A resource's label can be changed by making a PUT request to the path
125-
segments `resources/label`. The resource's Iri has to be provided in the
111+
segments `resources/label`. The resource's IRI has to be provided in the
126112
URL (as its last segment). The new label has to submitted as JSON in the
127113
HTTP request's body.
128114

@@ -150,18 +136,24 @@ Only system or project administrators may use the bulk import.
150136
The procedure for using this feature is as follows
151137
(see the [example below](#bulk-import-example)).
152138

153-
1. Make an HTTP GET request to Knora to [get XML schemas](#1-get-xml-schemas) describing
154-
the XML to be provided for the import.
155-
2. [Generate an XML import document](#2-generate-xml-import-document) representing the
156-
data to be imported, following the Knora import schemas that were generated in step 1.
157-
You will probably want to write a script to do this. Knora is not involved in this step.
158-
If you are also importing image files, this XML document needs to
159-
[contain the filesystem paths](#bulk-import-with-image-files) of those files.
160-
3. [Validate your XML import document](#3-validate-xml-import-document), using an XML schema validator such as
161-
[Apache Xerces](http://xerces.apache.org) or [Saxon](http://www.saxonica.com), or an
162-
XML development environment such as [Oxygen](https://www.oxygenxml.com). This will
163-
help ensure that the data you submit to Knora is correct. Knora is not involved in this step.
164-
4. [Submit the XML import document to Knora](#4-submit-xml-import-document-to-knora).
139+
1. Make an HTTP GET request to Knora to [get XML schemas](#1-get-xml-schemas) describing
140+
the XML to be provided for the import.
141+
142+
2. If you are importing image files, [upload files to Sipi](#2-upload-files-to-sipi).
143+
144+
3. [Generate an XML import document](#3-generate-xml-import-document) representing the
145+
data to be imported, following the Knora import schemas that were generated in step 1.
146+
You will probably want to write a script to do this. Knora is not involved in this step.
147+
If you are also importing image files, this XML document needs to
148+
[contain the filenames](#bulk-import-with-image-files) that Sipi returned
149+
for the files you uploaded in step 2.
150+
151+
4. [Validate your XML import document](#4-validate-xml-import-document), using an XML schema validator such as
152+
[Apache Xerces](http://xerces.apache.org) or [Saxon](http://www.saxonica.com), or an
153+
XML development environment such as [Oxygen](https://www.oxygenxml.com). This will
154+
help ensure that the data you submit to Knora is correct. Knora is not involved in this step.
155+
156+
5. [Submit the XML import document to Knora](#5-submit-xml-import-document-to-knora).
165157

166158
In this procedure, the person responsible for generating the XML import
167159
data need not be familiar with RDF or with the ontologies involved.
@@ -206,7 +198,12 @@ containing three files:
206198

207199
- `knoraXmlImport.xsd`: The standard Knora XML import schema, used by all XML imports.
208200

209-
#### 2. Generate XML Import Document
201+
#### 2. Upload Files to Sipi
202+
203+
See [Upload Files to Sipi](../api-v2/editing-values.md#upload-files-to-sipi) in
204+
the Knora API v2 documentation.
205+
206+
#### 3. Generate XML Import Document
210207

211208
We now convert our existing data to XML, probably by writing a custom
212209
script. The resulting XML import document could look like this:
@@ -305,18 +302,6 @@ This illustrates several aspects of XML imports:
305302

306303
- The type of each value must be specified using the attribute
307304
`knoraType`.
308-
309-
- If a property has `knoraType="date_value"`, the date value must have the following format:
310-
311-
```
312-
(GREGORIAN|JULIAN|ISLAMIC):\d{1,4}(-\d{1,2}(-\d{1,2})?)?( BC| AD| BCE| CE)?(:\d{1,4}(-\d{1,2}(-\d{1,2})?)?( BC| AD| BCE| CE)?)?
313-
```
314-
315-
E.g. an exact date like `GREGORIAN:2015-12-03` or a period like `GREGORIAN:2015-12-03:2015-12-04`.
316-
Dates may also have month or year precision, e.g. `ISLAMIC:1407-02` (the whole month of december) or `JULIAN:1330`
317-
(the whole year 1330). An optional ERA indicator term (`BCE`, `CE`, or `BC`, `AD`) can be added to the date, when no
318-
era is provided the default era `AD` will be considered. Era can be given as `GREGORIAN:1220 BC` or in range as
319-
`GREGORIAN:600 BC:480 BC`.
320305

321306
- A link to another resource described in the XML import is
322307
represented as a child element of a property element, with
@@ -351,7 +336,7 @@ This illustrates several aspects of XML imports:
351336
- A text value can have a `lang` attribute, whose value is an ISO 639-1
352337
code specifying the language of the text.
353338

354-
#### 3. Validate XML Import Document
339+
#### 4. Validate XML Import Document
355340

356341
You can use an XML schema validator such as [Apache Xerces](http://xerces.apache.org) or
357342
[Saxon](http://saxon.sourceforge.net/), or an XML development environment
@@ -364,7 +349,7 @@ For example, using Saxon:
364349
java -cp ./saxon9ee.jar com.saxonica.Validate -xsd:p0801-biblio.xsd -s:data.xml
365350
```
366351

367-
#### 4. Submit XML Import Document to Knora
352+
#### 5. Submit XML Import Document to Knora
368353

369354
To create these resources in Knora, make an HTTP post request with the XML import document
370355
as the request body. The URL must specify the (URL-encoded) IRI of the project in which
@@ -421,8 +406,8 @@ contains the IRI of the target resource.
421406

422407
To attach an image file to a resource, we must provide the
423408
element `knoraXmlImport:file` before the property elements. In this
424-
element, we must give the absolute filesystem path to the file that
425-
should be attached to the resource, along with its MIME type:
409+
element, we must provide a `filename` attribute, containing the `internalFilename`
410+
that Sipi returned for the file in [2. Upload Files to Sipi](#2-upload-files-to-sipi).
426411

427412
```xml
428413
<?xml version="1.0" encoding="UTF-8"?>
@@ -437,7 +422,7 @@ should be attached to the resource, along with its MIME type:
437422
</incunabula:book>
438423
<incunabula:page id="test_page">
439424
<knoraXmlImport:label>a page with an image</knoraXmlImport:label>
440-
<knoraXmlImport:file path="/usr/local/share/import-images/incunabula/12345.tiff" mimetype="image/tiff"/>
425+
<knoraXmlImport:file filename="67SEfNU1wK2-CSf5abe2eh3.jp2"/>
441426
<incunabula:origname knoraType="richtext_value">Chlaus</incunabula:origname>
442427
<incunabula:pagenum knoraType="richtext_value">1a</incunabula:pagenum>
443428
<incunabula:partOf>
@@ -448,6 +433,5 @@ should be attached to the resource, along with its MIME type:
448433
</knoraXmlImport:resources>
449434
```
450435

451-
During the processing of the bulk import, Knora will
452-
communicate the location of file to Sipi, which will convert it to JPEG 2000
453-
for storage.
436+
During the processing of the bulk import, Knora will ask Sipi for the rest of the
437+
file's metadata, and store that metadata in a file value attached to the resource.

docs/03-apis/api-v1/changing-values.md

+10-41
Original file line numberDiff line numberDiff line change
@@ -56,52 +56,21 @@ has to be used in order to create a new value (all these TypeScript interfaces a
5656

5757
## Modifying a File Value
5858

59-
In order to exchange a file value (digital representation of a
60-
resource), the path segment `filevalue` has to be used. The IRI of the
61-
resource whose file value is to be exchanged has to be appended:
59+
To change a file value, the client first uploads the new file to
60+
Sipi, following the procedure described in
61+
[Adding Resources with Image Files](adding-resources.md#adding-resources-with-image-files).
62+
63+
Then the client sends a request to Knora, using this following route:
6264

6365
```
6466
HTTP PUT to http://host/filevalue/resourceIRI
6567
```
6668

67-
Please note that the resource IRI has to be URL encoded.
68-
69-
There are two ways to change a file of a resource: Either by submitting
70-
directly the binaries of the file in a HTTP Multipart request or by
71-
indicating the location of the file. The two cases are referred to as
72-
non-GUI case and GUI case (TODO: add a link to "Sipi and Knora").
73-
74-
### Including the binaries (non-GUI case)
75-
76-
Here, a HTTP MULTIPART request has to be made simply providing the
77-
binaries (without JSON):
78-
79-
```python
80-
#!/usr/bin/env python3
81-
82-
import requests, json, urllib
83-
84-
# the name of the file to be submitted
85-
filename = 'myimage.tif'
86-
87-
# a tuple containing the file's name, its binaries and its mimetype
88-
files = {'file': (filename, open(filename, 'rb'), "image/tiff")}
89-
90-
resIri = urllib.parse.quote_plus('http://rdfh.ch/xy')
91-
92-
r = requests.put("http://host/filevalue/" + resIri,
93-
files=files)
94-
```
95-
96-
Please note that the file has to be read in binary mode (by default it
97-
would be read in text mode).
98-
99-
### Indicating the location of a file (GUI case)
100-
101-
Here, simply the location of the new file has to be submitted as JSON.
102-
The JSON format is described in the TypeScript interface
103-
`changeFileValueRequest` in module `changeValueFormats`. The request
104-
header's content type has to set to `application/json`.
69+
Here, `resourceIRI` is the URL-encoded IRI of the resource whose file value is
70+
to be changed. The body of the request is a JSON object described in the TypeScript
71+
interface `changeFileValueRequest` in module `changeValueFormats`, and contains
72+
`file`, whose value is the `internalFilename` that Sipi returned. The request header's
73+
content type must be set to `application/json`.
10574

10675
## Response on Value Change
10776

docs/03-apis/api-v2/editing-values.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,11 @@ Sipi then returns a JSON response that looks something like this:
262262
"uploadedFiles": [{
263263
"originalFilename": "manuscript-1234-page-1.tiff",
264264
"internalFilename": "3UIsXH9bP0j-BV0D4sN51Xz.jp2",
265-
"temporaryBaseIIIFUrl": "http://sipihost/tmp/3UIsXH9bP0j-BV0D4sN51Xz.jp2"
265+
"temporaryBaseIIIFUrl": "http://sipihost/tmp"
266266
}, {
267267
"originalFilename": "manuscript-1234-page-2.tiff",
268268
"internalFilename": "2RvJgguglpe-B45EOk0Gx8H.jp2",
269-
"temporaryBaseIIIFUrl": "http://sipihost/tmp/2RvJgguglpe-B45EOk0Gx8H.jp2"
269+
"temporaryBaseIIIFUrl": "http://sipihost/tmp"
270270
}]
271271
}
272272
```

docs/05-internals/design/api-v2/sipi.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ are described below.
4141
The `upload.lua` script is available at Sipi's `upload` route. It processes one
4242
or more file uploads submitted to Sipi. It converts uploaded images to JPEG 2000
4343
format, and stores them in Sipi's `tmp` directory. The usage of this script is described in
44-
[Creating File Values](../../../03-apis/api-v2/editing-values.md#creating-file-values).
44+
[Upload Files to Sipi](../../../03-apis/api-v2/editing-values.md#upload-files-to-sipi).
4545

4646
Each time `upload.lua` processes a request, it also deletes old temporary files
4747
from `tmp` and (recursively) from any subdirectories. The maximum allowed age of
@@ -94,7 +94,7 @@ If it encounters an error, it returns `SipiException`.
9494
to create or change a file value. The request includes Sipi's internal filename.
9595
3. During parsing of this JSON-LD request, a `StillImageFileValueContentV2`
9696
is constructed to represent the file value. During the construction of this
97-
object, a `GetImageMetadataRequestV2` is sent to `SipiConnector`, which
97+
object, a `GetFileMetadataRequestV2` is sent to `SipiConnector`, which
9898
uses Sipi's built-in `knora.json` route to get the rest of the file's
9999
metadata.
100100
4. A responder (`ResourcesResponderV2` or `ValuesResponderV2`) validates

docs/07-sipi/setup-sipi-for-knora.md

+7-39
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,10 @@ Whenever a file is requested from Sipi (e.g. a browser trying to
4545
dereference an image link served by Knora), a preflight function is
4646
called. This function is defined in `sipi.init-knora.lua` present in the
4747
Sipi root directory. It takes three parameters: `prefix`, `identifier`
48-
(the name of the requested file), and `cookie`. File links created by
49-
Knora use the prefix `knora`, e.g.
50-
`http://localhost:1024/knora/incunabula_0000000002.jp2/full/2613,3505/0/default.jpg`.
48+
(the name of the requested file), and `cookie`. The prefix is the shortcode
49+
of the project that the resource containing the file value belongs to.
5150

52-
Given these information, Sipi asks Knora about the current's users
51+
Given this information, Sipi asks Knora about the current's users
5352
permissions on the given file. The cookie contains the current user's
5453
Knora session id, so Knora can match Sipi's request with a given user
5554
profile and determine the permissions this user has on the file. If the
@@ -60,8 +59,8 @@ refuses to serve the file. However, all of this behaviour is defined in
6059
the preflight function in Sipi and not controlled by Knora. Knora only
6160
provides the permission code.
6261

63-
See [Sharing the Session ID with Sipi](sipi-and-knora.md#sharing-the-session-id-with-sipi) for more
64-
information about sharing the session id.
62+
See [Authentication of Users with Sipi](sipi-and-knora.md#authentication-of-users-with-sipi) for more
63+
information about sharing the session ID.
6564

6665
## Using Sipi in Test Mode
6766

@@ -76,42 +75,11 @@ $ docker run --rm -it --add-host webapihost:$DOCKERHOST -v $PWD/config:/sipi/con
7675
```
7776

7877
Then always the same test file will be served which is included in Sipi. In test mode, Sipi will
79-
not aks Knora about the user's permission on the requested file.
80-
81-
## Using Sipi in production behind a proxy
82-
83-
For SIPI to work with Salsah1 (non-angular) GUI, we need to define an additional set of
84-
environment variables if we want to run SIPI behind a proxy:
85-
86-
- `SIPI_EXTERNAL_PROTOCOL=https`
87-
- `SIPI_EXTERNAL_HOSTNAME=iiif.example.org`
88-
- `SIPI_EXTERNAL_PORT=443`
89-
90-
These variables are only used by `make_thumbnail.lua`:
91-
92-
```lua
93-
server.log("make_thumbnail - external_protocol: " .. get_external_protocol(), server.loglevel.LOG_DEBUG)
94-
95-
server.log("make_thumbnail - external_hostname: " .. get_external_hostname(), server.loglevel.LOG_DEBUG)
96-
97-
server.log("make_thumbnail - external_port: " .. get_external_port(), server.loglevel.LOG_DEBUG)
98-
99-
answer = {
100-
nx_thumb = dims.nx,
101-
ny_thumb = dims.ny,
102-
mimetype_thumb = 'image/jpeg',
103-
preview_path = get_external_protocol() .. "://" .. get_external_hostname() .. ":" .. get_external_port() .."/thumbs/" .. thumbName .. "/full/max/0/default.jpg",
104-
filename = tmpName, -- make this a IIIF URL
105-
original_mimetype = submitted_mimetype.mimetype,
106-
original_filename = filename,
107-
file_type = 'IMAGE'
108-
}
109-
```
110-
78+
not ask Knora about the user's permission on the requested file.
11179

11280
## Additional Sipi Environment Variables
11381

114-
Additionaly, these environment variables can be used to further configure sipi:
82+
Additionally, these environment variables can be used to further configure Sipi:
11583

11684
- `SIPI_WEBAPI_HOSTNAME=localhost`: overrides `knora_path` in Sipi's config
11785
- `SIPI_WEBAPI_PORT=3333`: overrides `knora_port` in Sipi's config

0 commit comments

Comments
 (0)