Use Case - Attachments
You can attach files, blobs to any object in Jungle.
Some examples include shipping labels, login tags, and data exports.
Client workflow (Apps)
A recommended workflow for capturing a file from the user is to do the following:
- Show a file upload prompt, file selection, or take a photo.
- Once the file is selected, signature drawn, or photo taken, make the following query:
query UploadToken($path: ID!, $filename: String) {
attachmentUploadToken(input: { object: { path: $path }, filename: $filename }) {
__typename
... on UploadToken {
url
expiresAt
bytesMax
}
}
}
- Using the
url, in the client, complete a file upload tourl. - The attachment will be located at
${path}/attachments/${filename}.
If you need to do multiple file uploads, you'll need a signed URL per file.
View attachments
You may view attachments on an object:
query Attachments($path: ID!) {
attachmentListV2(input: { object: { path: $path } }) {
pageInfo {
hasMore
limit
}
results {
attachment {
path
createdAt
... on Attachment {
name
tags
contentType
md5
crc32c
etag
size
modifiedAt
}
}
}
}
}
Download attachment
When a user wants to download a file, the client should request a URL for the attachment.
❗️Avoid this when listing attachments (
.urlhas a high cost). Request the URL when the user actions a download.
query AttachmentDownloadUrl($path: ID!) {
object(input: { id: $path, typename: "Attachment" }) {
... on Attachment {
url
}
}
}
The provided download URL will be short-lived.
Location layouts
A location can have one layout: the image (PNG, JPEG or SVG, up to 20 MiB, or 20,971,520 bytes) its floor plan is traced over. When the image was made from a page of a PDF, the PDF can be kept with it as the layout's source, with the page and the part of the page the image shows. A PDF is never the layout itself: make an image of the page and send the PDF as the source. Each file is uploaded the same way as an attachment, then checked before it is kept.
SVG layouts must be well-formed UTF-8 XML with one SVG root in the SVG namespace. The following are refused with LOCATION_LAYOUT_UNSAFE_SVG:
- Scripts, event-handler attributes,
javascript:orvbscript:links, HTML elements and embedded documents such asforeignObject,iframe,objectandembed. - Doctypes with internal declarations, including entities and default attributes. Only the standard SVG 1.0 and SVG 1.1 public doctypes are accepted.
- Animation targeting a link (
href, with any namespace prefix) or any event handler (on*), including targets written with character references. - References to another file in links, images, symbols or CSS
url(). Empty references, fragments such as#aisle, and embedded PNG, JPEG, GIF or WebP data images are accepted. Embedded SVG data images are refused. - Stylesheet processing instructions. Backslashes,
@importandimage-set()are refused in style text and styling attributes, including presentation attributes and animation values targeting styles. Backslashes in descriptive values, such as accessible labels, are accepted.
Malformed XML is refused with LOCATION_LAYOUT_UNREADABLE. A drawing made only of shapes, internal gradients and internal symbols can be accepted. Each PDF source must contain page objects, and its claimed page counts must match those pages; page rendering is not checked.
- Request a place to upload to.
nullmeans you may not change the location. A layout made from a PDF needs two uploads, each with its own token: one for the image and one for the PDF.
query LocationLayoutUploadToken($locationId: ID!, $filename: String!) {
locationLayoutUploadToken(input: { locationId: $locationId, filename: $filename }) {
uploadId
url
expiresAt
bytesMax
}
}
PUTeach file's bytes to itsurlbeforeexpiresAt.- Make it the location's layout. Leave out
sourcewhen the image was not made from a PDF.
mutation LocationLayoutSet($locationId: ID!, $uploadId: ID!, $source: LocationLayoutSourceInput) {
locationLayoutSet(input: { locationId: $locationId, uploadId: $uploadId, source: $source }) {
ok
... on MutationFailure {
code
reason
}
}
}
With a source, the variables look like this. page is counted from 1. crop is the part of the page the image shows, in fractions (0 to 1) of the page as a viewer shows it, from its top left; x + width and y + height are at most 1. Leave out crop when the image shows the whole page.
{
"locationId": "123",
"uploadId": "<the image's uploadId>",
"source": {
"uploadId": "<the PDF's uploadId>",
"page": 2,
"crop": { "x": 0.1, "y": 0.2, "width": 0.5, "height": 0.6 }
}
}
The kind of each file is decided from its content, not its name. When the layout is refused, both uploaded files are deleted, the location keeps the layout it had, and reason says why in words you can show to the user:
| Code | Meaning |
|---|---|
LOCATION_LAYOUT_UPLOAD_NOT_FOUND | Nothing was uploaded for an uploadId, it expired, or it was already used. |
LOCATION_LAYOUT_TOO_LARGE | The image or the PDF is larger than 20 MB. |
LOCATION_LAYOUT_UNSUPPORTED_TYPE | The image is not a PNG, JPEG or SVG (a PDF included), or the source is not a PDF. |
LOCATION_LAYOUT_UNSAFE_SVG | The SVG has scripts, event handlers or references to other files. |
LOCATION_LAYOUT_UNREADABLE | A file is damaged or incomplete, or the PDF has no pages. |
LOCATION_LAYOUT_SOURCE_PAGE_NOT_FOUND | The PDF has fewer pages than source.page. |
INVALID_INPUT | source.page is less than 1, or source.crop is not a rectangle within the page. |
An image or source uploadId can be accepted once, including after its layout is removed or replaced. Uploading again through the same address cannot change an accepted file. Setting a layout replaces the one the location had, with its source.
- Read it back:
query LocationLayout($locationId: ID!) {
location(locationId: $locationId) {
layout {
kind
width
height
createdBy
createdAt
file {
name
size
url
}
source {
file {
name
size
url
}
pageCount
page
crop {
x
y
width
height
}
}
}
}
}
createdBy identifies who confirmed the layout: user|<user ID>, connection|<connection ID>, or unknown. Location layouts and their creator identifiers can currently be read across entities. createdAt gives the confirmation time. source is null when the image was not made from a PDF. The page and crop are as they were sent: the image is not checked against the PDF.
Each url is short-lived, and opening it directly downloads the file under the name it was uploaded with rather than showing it; load the image into an <img> and the PDF into a document viewer instead. locationLayoutRemove(input: { locationId }) removes the layout, its image and its source.
Guides
Step-by-step guides for common Jungle workflows
Configuring Jungle For Your Organisation
Learn how to configure Jungle for usage within your organisation