1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-10 23:50:12 +00:00

Merge branch '255-add-user-file-upload-docs-to-the-generated-api-docs' into 'develop'

Resolve "Add user file upload docs to the generated API docs"

Closes 

See merge request 
This commit is contained in:
Nigel Gott 2022-09-07 11:29:51 +00:00
commit 31ed9ec584
11 changed files with 522 additions and 8 deletions
backend
src/baserow
api/user_files
test_utils
tests/baserow/api/user_files
changelog.md
web-frontend

View file

@ -8,6 +8,7 @@ from rest_framework.views import APIView
from baserow.api.decorators import map_exceptions, validate_body
from baserow.api.schemas import get_error_schema
from baserow.contrib.database.api.tokens.authentications import TokenAuthentication
from baserow.core.user_files.exceptions import (
FileSizeTooLargeError,
FileURLCouldNotBeReached,
@ -28,6 +29,7 @@ from .serializers import UserFileSerializer, UserFileUploadViaURLRequestSerializ
class UploadFileView(APIView):
permission_classes = (IsAuthenticated,)
parser_classes = (MultiPartParser,)
authentication_classes = APIView.authentication_classes + [TokenAuthentication]
@extend_schema(
tags=["User files"],
@ -63,6 +65,7 @@ class UploadFileView(APIView):
class UploadViaURLView(APIView):
permission_classes = (IsAuthenticated,)
authentication_classes = APIView.authentication_classes + [TokenAuthentication]
@extend_schema(
tags=["User files"],

View file

@ -343,9 +343,8 @@ def setup_interesting_test_table(
)
# multiple collaborators
getattr(row, f"field_{name_to_field_id['multiple_collaborators']}").set(
[user2.id, user3.id]
)
getattr(row, f"field_{name_to_field_id['multiple_collaborators']}").add(user2.id)
getattr(row, f"field_{name_to_field_id['multiple_collaborators']}").add(user3.id)
context = {"user2": user2, "user3": user3}

View file

@ -15,11 +15,12 @@ from rest_framework.status import (
HTTP_413_REQUEST_ENTITY_TOO_LARGE,
)
from baserow.contrib.database.tokens.handler import TokenHandler
from baserow.core.models import UserFile
@pytest.mark.django_db
def test_upload_file(api_client, data_fixture, tmpdir):
def test_upload_file_with_jwt_auth(api_client, data_fixture, tmpdir):
user, token = data_fixture.create_user_and_token(
email="test@test.nl", password="password", first_name="Test1"
)
@ -144,9 +145,137 @@ def test_upload_file(api_client, data_fixture, tmpdir):
assert thumbnail.width == 21
@pytest.mark.django_db
def test_upload_file_with_token_auth(api_client, data_fixture, tmpdir):
user, jwt_token = data_fixture.create_user_and_token(
email="test@test.nl", password="password", first_name="Test1"
)
group = data_fixture.create_group(user=user)
token = TokenHandler().create_token(user, group, "uploadFile")
response = api_client.post(
reverse("api:user_files:upload_file"),
format="multipart",
HTTP_AUTHORIZATION=f"Token {token.key}",
)
assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_INVALID_FILE"
response = api_client.post(
reverse("api:user_files:upload_file"),
data={"file": ""},
format="multipart",
HTTP_AUTHORIZATION=f"Token {token.key}",
)
assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_INVALID_FILE"
old_limit = settings.BASEROW_FILE_UPLOAD_SIZE_LIMIT_MB
settings.BASEROW_FILE_UPLOAD_SIZE_LIMIT_MB = 6
response = api_client.post(
reverse("api:user_files:upload_file"),
data={"file": SimpleUploadedFile("test.txt", b"Hello World")},
format="multipart",
HTTP_AUTHORIZATION=f"Token {token.key}",
)
settings.BASEROW_FILE_UPLOAD_SIZE_LIMIT_MB = old_limit
assert response.status_code == HTTP_413_REQUEST_ENTITY_TOO_LARGE
assert response.json()["error"] == "ERROR_FILE_SIZE_TOO_LARGE"
assert response.json()["detail"] == (
"The provided file is too large. Max 0MB is allowed."
)
response = api_client.post(
reverse("api:user_files:upload_file"),
data={"file": "not a file"},
format="multipart",
HTTP_AUTHORIZATION=f"Token {token.key}",
)
assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_INVALID_FILE"
storage = FileSystemStorage(location=str(tmpdir), base_url="http://localhost")
with patch("baserow.core.user_files.handler.default_storage", new=storage):
with freeze_time("2020-01-01 12:00"):
file = SimpleUploadedFile("test.txt", b"Hello World")
response = api_client.post(
reverse("api:user_files:upload_file"),
data={"file": file},
format="multipart",
HTTP_AUTHORIZATION=f"Token {token.key}",
)
response_json = response.json()
assert response.status_code == HTTP_200_OK
assert response_json["size"] == 11
assert response_json["mime_type"] == "text/plain"
assert response_json["is_image"] is False
assert response_json["image_width"] is None
assert response_json["image_height"] is None
assert response_json["uploaded_at"] == "2020-01-01T12:00:00Z"
assert response_json["thumbnails"] is None
assert response_json["original_name"] == "test.txt"
assert "localhost:8000" in response_json["url"]
user_file = UserFile.objects.all().last()
assert user_file.name == response_json["name"]
assert response_json["url"].endswith(response_json["name"])
file_path = tmpdir.join("user_files", user_file.name)
assert file_path.isfile()
with patch("baserow.core.user_files.handler.default_storage", new=storage):
file = SimpleUploadedFile("test.txt", b"Hello World")
response_2 = api_client.post(
reverse("api:user_files:upload_file"),
data={"file": file},
format="multipart",
HTTP_AUTHORIZATION=f"Token {token.key}",
)
# The old file should be provided.
assert response_2.json()["name"] == response_json["name"]
assert response_json["original_name"] == "test.txt"
image = Image.new("RGB", (100, 140), color="red")
file = SimpleUploadedFile("test.png", b"")
image.save(file, format="PNG")
file.seek(0)
with patch("baserow.core.user_files.handler.default_storage", new=storage):
response = api_client.post(
reverse("api:user_files:upload_file"),
data={"file": file},
format="multipart",
HTTP_AUTHORIZATION=f"Token {token.key}",
)
response_json = response.json()
assert response.status_code == HTTP_200_OK
assert response_json["mime_type"] == "image/png"
assert response_json["is_image"] is True
assert response_json["image_width"] == 100
assert response_json["image_height"] == 140
assert len(response_json["thumbnails"]) == 1
assert "localhost:8000" in response_json["thumbnails"]["tiny"]["url"]
assert "tiny" in response_json["thumbnails"]["tiny"]["url"]
assert response_json["thumbnails"]["tiny"]["width"] == 21
assert response_json["thumbnails"]["tiny"]["height"] == 21
assert response_json["original_name"] == "test.png"
user_file = UserFile.objects.all().last()
file_path = tmpdir.join("user_files", user_file.name)
assert file_path.isfile()
file_path = tmpdir.join("thumbnails", "tiny", user_file.name)
assert file_path.isfile()
thumbnail = Image.open(file_path.open("rb"))
assert thumbnail.height == 21
assert thumbnail.width == 21
@pytest.mark.django_db
@httpretty.activate(verbose=True, allow_net_connect=False)
def test_upload_file_via_url(api_client, data_fixture, tmpdir):
def test_upload_file_via_url_with_jwt_auth(api_client, data_fixture, tmpdir):
user, token = data_fixture.create_user_and_token(
email="test@test.nl", password="password", first_name="Test1"
)
@ -251,6 +380,115 @@ def test_upload_file_via_url(api_client, data_fixture, tmpdir):
assert file_path.isfile()
@pytest.mark.django_db
@httpretty.activate(verbose=True, allow_net_connect=False)
def test_upload_file_via_url_with_token_auth(api_client, data_fixture, tmpdir):
user, jwt_token = data_fixture.create_user_and_token(
email="test@test.nl", password="password", first_name="Test1"
)
group = data_fixture.create_group(user=user)
token = TokenHandler().create_token(user, group, "uploadFileViaUrl")
response = api_client.post(
reverse("api:user_files:upload_via_url"),
data={},
HTTP_AUTHORIZATION=f"Token {token.key}",
)
assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_REQUEST_BODY_VALIDATION"
response = api_client.post(
reverse("api:user_files:upload_via_url"),
data={"url": "NOT_A_URL"},
HTTP_AUTHORIZATION=f"Token {token.key}",
)
assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_REQUEST_BODY_VALIDATION"
httpretty.register_uri(
httpretty.GET,
"https://baserow.io/test2.txt",
status=404,
)
response = api_client.post(
reverse("api:user_files:upload_via_url"),
data={"url": "https://baserow.io/test2.txt"},
HTTP_AUTHORIZATION=f"Token {token.key}",
)
assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_FILE_URL_COULD_NOT_BE_REACHED"
# Only the http and https protocol are allowed.
response = api_client.post(
reverse("api:user_files:upload_via_url"),
data={"url": "ftp://baserow.io/test2.txt"},
HTTP_AUTHORIZATION=f"Token {token.key}",
)
assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_INVALID_FILE_URL"
old_limit = settings.BASEROW_FILE_UPLOAD_SIZE_LIMIT_MB
settings.BASEROW_FILE_UPLOAD_SIZE_LIMIT_MB = 6
httpretty.register_uri(
httpretty.GET,
"http://localhost/test.txt",
body="Hello World",
status=200,
content_type="text/plain",
)
response = api_client.post(
reverse("api:user_files:upload_via_url"),
data={"url": "http://localhost/test.txt"},
HTTP_AUTHORIZATION=f"Token {token.key}",
)
assert response.status_code == HTTP_413_REQUEST_ENTITY_TOO_LARGE
assert response.json()["error"] == "ERROR_FILE_SIZE_TOO_LARGE"
# If the content length is not specified then when streaming down the file we will
# check the file size.
httpretty.register_uri(
httpretty.GET,
"http://localhost/test2.txt",
body="Hello World",
forcing_headers={"Content-Length": None},
status=200,
content_type="text/plain",
)
response = api_client.post(
reverse("api:user_files:upload_via_url"),
data={"url": "http://localhost/test2.txt"},
HTTP_AUTHORIZATION=f"Token {token.key}",
)
assert response.status_code == HTTP_413_REQUEST_ENTITY_TOO_LARGE
assert response.json()["error"] == "ERROR_FILE_SIZE_TOO_LARGE"
settings.BASEROW_FILE_UPLOAD_SIZE_LIMIT_MB = old_limit
storage = FileSystemStorage(location=str(tmpdir), base_url="http://localhost")
with patch("baserow.core.user_files.handler.default_storage", new=storage):
response = api_client.post(
reverse("api:user_files:upload_via_url"),
data={"url": "http://localhost/test.txt"},
HTTP_AUTHORIZATION=f"Token {token.key}",
)
response_json = response.json()
assert response.status_code == HTTP_200_OK, response_json
assert response_json["size"] == 11
assert response_json["mime_type"] == "text/plain"
assert response_json["is_image"] is False
assert response_json["image_width"] is None
assert response_json["image_height"] is None
assert response_json["thumbnails"] is None
assert response_json["original_name"] == "test.txt"
assert "localhost:8000" in response_json["url"]
user_file = UserFile.objects.all().last()
file_path = tmpdir.join("user_files", user_file.name)
assert file_path.isfile()
@pytest.mark.django_db
def test_upload_file_via_url_within_private_network(api_client, data_fixture, tmpdir):
user, token = data_fixture.create_user_and_token(

View file

@ -28,6 +28,7 @@ For example:
* Fields can now be duplicated with their cell values also. [#964](https://gitlab.com/bramw/baserow/-/issues/964)
* Add a tooltip to applications and tables in the left sidebar to show the full name. [#986](https://gitlab.com/bramw/baserow/-/issues/986)
* Allow not creating a reversed relationship with the link row field. [#1063](https://gitlab.com/bramw/baserow/-/issues/1063)
* Add API token authentication support to multipart and via-URL file uploads. [#255](https://gitlab.com/bramw/baserow/-/issues/255)
### Bug Fixes
* Resolve circular dependency in `FieldWithFiltersAndSortsSerializer` [#1113](https://gitlab.com/bramw/baserow/-/issues/1113)

View file

@ -112,7 +112,7 @@
"createdOnReadOnly": "The created on field is a read only field.",
"url": "Accepts a string that must be a URL.",
"email": "Accepts a string that must be an email address.",
"file": "Accepts an array of objects containing at least the name of the user file.",
"file": "Accepts an array of objects containing at least the name of the user file. You can use the \"File uploads\" endpoints to upload the file. The response of those calls can be provided directly as object here. The endpoints can be found in the left sidebar.",
"singleSelect": "Accepts an integer representing the chosen select option id or null if none is selected.",
"multipleSelect": "Accepts an array of integers each representing the chosen select option id.",
"phoneNumber": "Accepts a phone number which has a maximum length of 100 characters consisting solely of digits, spaces and the following characters: Nx,._+*()#=;/- .",
@ -269,12 +269,17 @@
"moveRow": "Move row",
"deleteRow": "Delete row",
"deleteRows": "Delete rows",
"uploadFile": "Upload file",
"uploadFileDescription": "The `file` multipart containing the file contents.",
"uploadFileViaUrl": "Upload file via URL",
"uploadFileViaUrlDescription": "Uploads a file to Baserow by downloading it from the provided URL.",
"queryParameters": "Query parameters",
"pathParameters": "Path parameters",
"requestBodySchema": "Request body schema",
"userFieldNamesDescription": "When any value is provided for the `user_field_names` GET param then field names returned by this endpoint will be the actual names of the fields.\n\n If the `user_field_names` GET param is not provided, then all returned field names will be `field_` followed by the id of the field. For example `field_1` refers to the field with an id of `1`.",
"singleRow": "Single",
"batchRows": "Batch"
"batchRows": "Batch",
"fileUploads": "File uploads"
},
"exporterType": {
"csv": "Export to CSV"

View file

@ -165,6 +165,11 @@ export default {
required: false,
default: '',
},
fileRequest: {
type: String,
required: false,
default: '',
},
request: {
type: [Object, Boolean],
required: false,
@ -226,6 +231,11 @@ export default {
example += '\n-H "Content-Type: application/json" \\'
}
if (this.fileRequest !== '') {
index++
example += ` \\\n-F file=@${this.fileRequest}`
}
example += `\n${this.url}`
if (this.request !== false) {
@ -250,6 +260,12 @@ export default {
example += `${this.url} HTTP`
example += '\nAuthorization: Token YOUR_API_KEY'
if (this.fileRequest !== '') {
example += '\nContent-Length: YOUR_CONTENT_LENGTH'
example +=
'\nContent-Type: multipart/form-data; boundary=------------------------YOUR_BOUNDARY'
}
if (this.request !== false) {
index += 2
example += '\nContent-Type: application/json'
@ -263,7 +279,14 @@ export default {
},
getJavaScriptExample() {
let index = 5
let example = 'axios({'
let example = ''
if (this.fileRequest !== '') {
example += 'const formData = new FormData()'
example += `\nformData.append('file', '${this.fileRequest}')`
example += "\naxios.post('/fileupload', formData, {"
} else {
example = 'axios({'
}
if (this.type !== '') {
index++
@ -274,6 +297,11 @@ export default {
example += '\n headers: {'
example += '\n Authorization: "Token YOUR_API_KEY"'
if (this.fileRequest !== '') {
index++
example += ',\n "Content-Type": "multipart/form-data"'
}
if (this.request !== false) {
index++
example += ',\n "Content-Type": "application/json"'
@ -309,6 +337,11 @@ export default {
example += '\n }'
if (this.fileRequest !== '') {
index++
example += `\n files={'file': open('${this.fileRequest}', 'rb')}`
}
if (this.request !== false) {
index++
const data = this.format(this.request).split('\n').join('\n ')

View file

@ -125,6 +125,42 @@
</li>
</ul>
</li>
<li>
<a
class="api-docs__nav-link"
@click.prevent="navigate('section-upload-file')"
>{{ $t('apiDocs.fileUploads') }}</a
>
<ul
class="api-docs__nav-sub"
:class="{
open:
navActive === 'section-upload-file' ||
navActive === 'section-upload-file-via-url',
}"
>
<li>
<a
class="api-docs__nav-link"
:class="{
active: navActive === 'section-upload-file',
}"
@click.prevent="navigate('section-upload-file')"
>{{ $t('apiDocs.uploadFile') }}</a
>
</li>
<li>
<a
class="api-docs__nav-link"
:class="{
active: navActive === 'section-upload-file-via-url',
}"
@click.prevent="navigate('section-upload-file-via-url')"
>{{ $t('apiDocs.uploadFileViaUrl') }}</a
>
</li>
</ul>
</li>
<li>
<a
class="api-docs__nav-link"

View file

@ -0,0 +1,68 @@
<template>
<div class="api-docs__item">
<div class="api-docs__left">
<div class="api-docs__heading-wrapper">
<h3 :id="'section-upload-file'" class="api-docs__heading-3">
<span>
{{ $t('apiDocs.uploadFile') }}
</span>
</h3>
<div class="api-docs__endpoint-type"></div>
</div>
<MarkdownIt
tag="p"
class="api-docs__content"
:content="
$t('apiDocsUploadFile.description', {
PUBLIC_BACKEND_URL: `${$env.PUBLIC_BACKEND_URL}`,
})
"
/>
<h4 class="api-docs__heading-4">{{ $t('apiDocs.requestBodySchema') }}</h4>
<ul class="api-docs__parameters">
<APIDocsParameter name="file" :optional="false" type="multipart">
<MarkdownIt
class="api-docs__content"
:content="$t('apiDocs.uploadFileDescription')"
/>
</APIDocsParameter>
</ul>
</div>
<div class="api-docs__right">
<APIDocsExample
:value="value"
type="POST"
:url="getUploadFileListUrl()"
:file-request="getUploadFileExample()"
:response="getUploadFileResponse()"
:include-user-fields-checkbox="false"
@input="$emit('input', $event)"
></APIDocsExample>
</div>
</div>
</template>
<script>
import APIDocsExample from '@baserow/modules/database/components/docs/APIDocsExample'
import APIDocsParameter from '@baserow/modules/database/components/docs/APIDocsParameter'
export default {
name: 'APIDocsUploadFile',
components: {
APIDocsParameter,
APIDocsExample,
},
props: {
value: {
type: Object,
required: true,
},
getUploadFileListUrl: { type: Function, required: true },
getUploadFileExample: { type: Function, required: true },
getUploadFileResponse: { type: Function, required: true },
},
}
</script>

View file

@ -0,0 +1,68 @@
<template>
<div class="api-docs__item">
<div class="api-docs__left">
<div class="api-docs__heading-wrapper">
<h3 :id="'section-upload-file-via-url'" class="api-docs__heading-3">
<span>
{{ $t('apiDocs.uploadFileViaUrl') }}
</span>
</h3>
<div class="api-docs__endpoint-type"></div>
</div>
<MarkdownIt
tag="p"
class="api-docs__content"
:content="
$t('apiDocsUploadFileViaURL.description', {
PUBLIC_BACKEND_URL: `${$env.PUBLIC_BACKEND_URL}`,
})
"
/>
<h4 class="api-docs__heading-4">{{ $t('apiDocs.requestBodySchema') }}</h4>
<ul class="api-docs__parameters">
<APIDocsParameter name="url" :optional="false" type="string">
<MarkdownIt
class="api-docs__content"
:content="$t('apiDocs.uploadFileViaUrlDescription')"
/>
</APIDocsParameter>
</ul>
</div>
<div class="api-docs__right">
<APIDocsExample
:value="value"
type="POST"
:url="getUploadFileViaUrlListUrl()"
:response="getUploadFileResponse()"
:request="getUploadFileViaUrlRequestExample()"
:include-user-fields-checkbox="false"
@input="$emit('input', $event)"
></APIDocsExample>
</div>
</div>
</template>
<script>
import APIDocsExample from '@baserow/modules/database/components/docs/APIDocsExample'
import APIDocsParameter from '@baserow/modules/database/components/docs/APIDocsParameter'
export default {
name: 'APIDocsUploadFileViaURL',
components: {
APIDocsParameter,
APIDocsExample,
},
props: {
value: {
type: Object,
required: true,
},
getUploadFileViaUrlListUrl: { type: Function, required: true },
getUploadFileViaUrlRequestExample: { type: Function, required: true },
getUploadFileResponse: { type: Function, required: true },
},
}
</script>

View file

@ -180,6 +180,14 @@
"rowId": "Moves the row related to the value.",
"before": "Moves the row related to the given `row_id` before the row related to the provided value. If not provided, then the row will be moved to the end."
},
"apiDocsUploadFile": {
"description": "Uploads a file to Baserow by uploading the file contents directly. A `file` multipart is expected containing the file contents. The response can then be used to [upload a file to a row]({PUBLIC_BACKEND_URL}/api/redoc/#tag/Database-table-rows/operation/update_database_table_row).",
"file": "The `file` multipart containing the file contents."
},
"apiDocsUploadFileViaURL": {
"description": "Uploads a file to Baserow by downloading it from the provided URL. The response can then be used to [upload a file to a row]({PUBLIC_BACKEND_URL}/api/redoc/#tag/Database-table-rows/operation/update_database_table_row).",
"url": "The URL you would like Baserow to download and upload on your behalf."
},
"apiDocsTableUpdateRow": {
"description": "Updates an existing {name} row.",
"rowId": "The unique identifier of the row that needs to be updated.",

View file

@ -113,6 +113,20 @@
:get-batch-delete-request-example="getBatchDeleteRequestExample"
/>
</div>
<APIDocsUploadFile
v-model="exampleData"
:get-upload-file-list-url="getUploadFileListUrl"
:get-upload-file-example="getUploadFileExample"
:get-upload-file-response="getUploadFileResponse"
/>
<APIDocsUploadFileViaURL
v-model="exampleData"
:get-upload-file-response="getUploadFileResponse"
:get-upload-file-via-url-list-url="getUploadFileViaUrlListUrl"
:get-upload-file-via-url-request-example="
getUploadFileViaUrlRequestExample
"
/>
<APIDocsFilters />
<APIDocsErrors v-model="exampleData" />
</div>
@ -136,10 +150,15 @@ import APIDocsTableCreateRow from '@baserow/modules/database/components/docs/sec
import APIDocsTableUpdateRow from '@baserow/modules/database/components/docs/sections/APIDocsTableUpdateRow'
import APIDocsTableMoveRow from '@baserow/modules/database/components/docs/sections/APIDocsTableMoveRow'
import APIDocsTableDeleteRow from '@baserow/modules/database/components/docs/sections/APIDocsTableDeleteRow'
import APIDocsUploadFile from '@baserow/modules/database/components/docs/sections/APIDocsUploadFile'
import APIDocsUploadFileViaURL from '@baserow/modules/database/components/docs/sections/APIDocsUploadFileViaURL'
import APIDocsFilters from '@baserow/modules/database/components/docs/sections/APIDocsFilters'
import APIDocsErrors from '@baserow/modules/database/components/docs/sections/APIDocsErrors'
import APIDocsMenu from '@baserow/modules/database/components/docs/sections/APIDocsMenu.vue'
// Re-use the FileFieldType docs response example.
import { FileFieldType } from '../fieldTypes'
export default {
name: 'APIDocsDatabase',
components: {
@ -154,6 +173,8 @@ export default {
APIDocsTableUpdateRow,
APIDocsTableMoveRow,
APIDocsTableDeleteRow,
APIDocsUploadFile,
APIDocsUploadFileViaURL,
APIDocsFilters,
APIDocsErrors,
APIDocsMenu,
@ -372,6 +393,40 @@ export default {
getDeleteListURL(table) {
return `${this.$env.PUBLIC_BACKEND_URL}/api/database/rows/table/${table.id}/batch-delete/`
},
/**
* Generates the 'upload file' file example.
*/
getUploadFileExample() {
return 'photo.png'
},
/**
* Generates the 'upload file' and 'upload via URL' file example response.
*/
getUploadFileResponse() {
return this.$registry
.get('field', FileFieldType.getType())
.getDocsResponseExample()[0]
},
/**
* Generates the 'upload file' URI.
*/
getUploadFileListUrl() {
return this.$env.PUBLIC_BACKEND_URL + '/api/user-files/upload-file/'
},
/**
* Generates the 'upload file' request example.
*/
getUploadFileViaUrlRequestExample() {
return {
url: 'https://baserow.io/assets/photo.png',
}
},
/**
* Generates the 'upload file via URL' URI.
*/
getUploadFileViaUrlListUrl() {
return this.$env.PUBLIC_BACKEND_URL + '/api/user-files/upload-via-url/'
},
getItemURL(table, addUserFieldParam) {
return (
this.getListURL(table) +