Skip to content

Commit

Permalink
REST design
Browse files Browse the repository at this point in the history
  • Loading branch information
xamde committed Aug 8, 2023
1 parent 3b316a4 commit 7e77205
Showing 1 changed file with 176 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,45 +1,204 @@
= REST API
:toc:

== Requirements

- Let user upload a single file
- Let user download converted file as GraphML

== Requirement
.Chaining
Given a URL like `http://example.org/mygraph.dot`,
construct a URL like
`http://graphinout.com/api/convert?format=graphml&url=http://example.org/mygraph.dot` => Response is a GraphML file

== Design
.Usage for a single file
. User uploads file -> gets redirected to /session/<inputId>
. User downloads, e.g. /session/<inputId>/log.txt or /session/<inputId>/result.graphml.xml

== Variant: ZIP
- Request: POST file
- Response: response.zip (log.txt, a.graphml.xml)

== Variant: Resources <--
.Reasons
* + Better timeout-resilience
* + Better concurrency (impl)
* - Several requests required
* + Better debuggability

- Request: POST file + optional sessionId
- Response: Redirect to /session/<inputId>
=== Endpoint: `/api`
==== POST with MultiPart
- Request: POST file + optional inputId
- Response: HTTP 308 Permanent Redirect to /session/<inputId>
- Errors:
** inputId is too lange -> HTTP 413 Request Entity Too Large
** at least onf of the given files is empty -> HTTP 400 Bad Request
** at least onf of the given files is too large -> HTTP 413 Payload Too Large
** Server fails to store (one of) the files -> HTTP 500 Internal Server Error

PostCondition: All required files have been saved to directory.

NOTE: Keep sessions for 2 weeks?

=== Endpoint `/api/session/<inputId>`
==== GET
- Request: GET /session/<inputId>
- Response: "still pending" or "done" (JSON or XML response)
** Response: Show links (JSON or XML response) to /session/<inputId>/log.txt and /session/<inputId>/a.graphml.xml -- can download from there
- Response as JSON:
* "still pending" or
* "failed" if there was an error processing the file(s), include links to
** `/api/sessions/<inputId>/output.log`
* "done" includes links:
** `/api/sessions/<inputId>/output.log`
** `/api/sessions/<inputId>/output.graph-xml`
** `/api/sessions/<inputId>/all.zip`

Keep sessions for 2 weeks?
- Load `status.json` from directory and use it for status?

- Request: GET /session/<inputId>/all.zip
- Response: ZIP file with log.txt and a.graphml.xml
- Errors:
** inputId is invalid/directory does not exists -> HTTP 404 Not Found
** I/O while reading files -> HTTP 500 Internal Server Error

=== Session / InputIds?
.Both
- UUID v7?
- User-supplied
CAUTION: Don't offer download of original files -- otherwise we are a file sharing site!

==== POST with MultiPart
- Delete existing input and result files from directory
- Upload new files
- Return HTTP 200 OK (not 201 Created, because resource DID already exist)

- Errors:
** at least onf of the given files is empty -> HTTP 400 Bad Request
** at least onf of the given files is too large -> HTTP 413 Payload Too Large
** Server fails to store (one of) the files -> HTTP 500 Internal Server Error


=== Endpoint `/api/session/<inputId>/output.log`
==== GET
Return file at `/configured-base-dir/encoded(<inputId>)/output.log`

- Errors:
** inputId is invalid/directory does not exists -> HTTP 404 Not Found
** processing not yet done -> HTTP 404 Not Found


=== Endpoint `/api/session/<inputId>/output.graphml.xml`
==== GET
Return file at `/configured-base-dir/encoded(<inputId>)/output.graphml.xml`

=== Endpoint `/api/session/<inputId>/all.zip`
==== GET
- Generate a streaming ZIP file containing both output files


=== Session / InputIds?
.Both
- UUID v7?
- User-supplied

== File Management

.Save
Two variants
User supplies us with a inputId:: base64 encode userId as valid directory name
We generate a inputId:: UUID

NOTE: Storing a new file at an existing inputId overwrite the existing file AND must delete existing result files.

TIP: Use (encoded) inputId as directory name (I.e. use only `[0-9a-zA-Z-=]`)

.Load
Encode inputId with base64, look in directory.

.Encode Algorithm
----
IF input is a valid directory name
RETURN it
ELSE RETURN base64encode it
----

.Encode example:
Input:: `http://graphdrawing.de/contest2018/got-graph.graphml`
Encoded:: `aHR0cDovL2dyYXBoZHJhd2luZy5kZS9jb250ZXN0MjAxOC9nb3QtZ3JhcGguZ3JhcGhtbA==`

.Directory Mapping
[options="header"]
|===
| File type | File name
| Input files | (use original file name with extensions)
| Log file | `output.log`
| GraphML result file | `output.graphml.xml`
| Status file | `status.json`
|===

.Our Directory
[source]
----
/configured-base-dir
/3a5a4b40-fe30-4696-8e90-73cd16dac1f6 <1>
/my-graph.dot
/output.log
/output.graphml.xml
/my-input-id <2>
/MY-LARGE-grAph.tgf
/output.log
/output.graphml.xml
/aHR0cDovL2dyYXBoZHJhd2luZy5kZS9jb250ZXN0MjAxOC9nb3QtZ3JhcGguZ3JhcGhtbA== <3>
/got-graph.graphml
/output.log
/output.graphml.xml
----
<1> user had no inputId, we generated a UUIDv4
<2> user supplied `my-input-id` as inputId
<3> user supplied a URL as inputId, we encoded it

.Export as ZIP
- Include only log file and graphML result file
- Create ZIPs on the fly, don't store them

=== What should be in `output.log` ?
- ContentErrors
- (Logger output? If possible?)


== Concurrency
What happens if

- user uploads (larger) graph input
- user requests /session/<inputId>/output.log while processing
** (1) did not start,
** (2) is running,
** (3) is done
- user requests /session/<inputId>/output.result.graph while processing
** (1) did not start,
** (2) is running,
** (3) is done
- user requests /session/<inputId>/all.zip while processing
** (1) did not start,
** (2) is running,
** (3) is done

== Metadata management
.`status.json`
- For each task step:
** timestamp
** Caller IP address
** file size
** HTTP result

- Current status:
** "initial" -- directory prepared, no files yet
** "uploading" -- files are being uploaded
** "processing" -- files are being processed
** "success"
** "failed"

.States
[plantuml]
....
state initial
[*] --> initial
initial -> uploading : POST /api
uploading -> processing
uploading --> failed : upload fails
processing --> success
processing --> failed
success --> done
failed --> done
done --> uploading : POST /<inputId>
done --> [*]
....

0 comments on commit 7e77205

Please sign in to comment.