Aurapps-file-storage
Aurapps File Storage
El File Storage permite a una extensión almacenar archivos bajo keys, para un usuario, lote o campo (scope/alcance).
Un set de endpoints permite hacer el ABM de las keys y le proveerá al cliente las urls para que suba o descargue los achivos directamente del storage.
Esto significa que hay consistencia eventual, es decir, el cliente creará/actualizará las keys en la base de datos pero luego queda a su cargo subir el archivo, quedando una inconsistencia hasta que el archivo esté subido. Podr ía darse que el cliente no termine de subir el archivo y el sistema quede en estado inconsistente.
API
Objeto Stored-file
{
"file_name": "d5fa71aa-1f35-11ef-a2b9-ab5cffcaca66.pdf",
"file_size_kb": 150.5,
"created_at": "2024-01-01T00:00:00Z",
"url": "..."
}
Objeto POST-URL
{
"url": "....",
"fields": {
"f1": "...",
"f2": "...",
"f3": "..."
}
}
Para la subida de archivos, los endpoints proporcionan al cliente un objeto POST-URL.
El mismo deberá realizar un POST a la url indicada, con un Content-Type multipart/form-data y un Body
que incluya todos los key-value que se encuentran bajo la key fields del objeto POST-URL.
Además, se deberá incluir en el form un campo Content-MD5 con el hash MD5 del archivo a subir. Al final del documento hay un script Python a modo de ejemplo.
La url de subida tiene una vigencia de 10 segundos. El máximo tamaño de archivo está configurado en 10MB.
Para la descarga de archivos sólo se proporciona una URL y se descarga mediante un GET.
La url de descarga tiene una vigencia de 10 segundos.
GET
POST /api/mktplace/filestorage/users ? operation = GET
POST /api/mktplace/filestorage/fields ? operation = GET
POST /api/mktplace/filestorage/farms ? operation = GET
Body: application/json
en el Body:
keys:arrayde keys a consultar - Obligatorioids:arrayde ids para filtrar recursos sobre los que buscar las keys- según el scope, serán ids de usuarios, lotes o campos.
- en el scope de users, si no se incluye, se asume que se está consultando por el usuario que realiza la consulta.
- en los scopes de fields y farms, si no se incluye, se buscan las keys solicitadas en todos los lotes/campos a los que tiene acceso el usuario
get_url:boolindica si se incluye o no la url de descarga en la respuesta (en el objetoStored-file)
Respuesta:
{
"objects": {
"resource_id_1": {
"key_1": {} // objeto Stored-file
}
},
"resource_id_2": {
"key_1": {}, // objeto Stored-file
"key_2": {} // objeto Stored-file
}
}
En caso de solicitar keys para el scope de users y no indicar ids,
como la consulta se realiza para el mismo usuario, la respuesta es distinta ya que no se incluye el nivel de recurso.
{
"objects": {
"key_1": {}, // objeto Stored-file
"key_2": {} // objeto Stored-file
}
}
PUT
Para crear una key. Primero se realiza la creación en el endpoint, obteniendose la url para subir el archivo.
Notar que al momento de realizar esta consulta, la nueva key se generará y figurará en el listado de las consultas GET pero hasta que el cliente no suba el nuevo archivo con la URL provista, el mismo no estará disponible. En caso de sobreescribir una key, el archivo reemplazado se eliminará al momento de hacer la consulta.
POST /api/mktplace/filestorage/users ? operation = PUT
POST /api/mktplace/filestorage/fields ? operation = PUT
POST /api/mktplace/filestorage/farms ? operation = PUT
Body: application/json
en el Body:
key:stringkey sobre la que se quiere guardar el archivooriginal_file_name:stringnombre del archivo que se quiere subirmd5:stringhash MD5 codificado en base64 del archivo a subir. Este es el valor que luego debe enviarse al subir el archivo.file_size_kb:floattamaño en kb del archivo a subiroverwrite:booleanindica si sobreescribir la key en caso de ya existe. Por defaulf esfalsepor lo que si la key indicada ya existe, el endpoint responderá con error.user:UID-XXXXid encriptado. sólo obligatorio en el scope users, indicando el id del usuario sobre el cual se desea guardar.field:uuid/intsólo obligatorio en el scope fields, indicando el id del lote sobre el cual se desea guardar.farm:uuid/intsólo obligatorio en el scope farms, indicando el id del campo sobre el cual se desea guardar.
Respuesta:
{
"data": {
"url": {} // objeto POST-URL
}
}
Tras una respuesta correcta, la key ya se encuentra almacenada y el próximo paso es subir el archivo.
Para eso se utiliza el objeto POST-URL como se indicó previamente.
Posibles errores:
| code | |
|---|---|
| 2 | formato de key inválido |
| 3 | no se tiene permiso sobre el recurso (usuario/lote/campo) indicado |
| 5 | la key indicada ya existe para el recurso indicado, y no se envió el flag de overwrite |
| 7 | el archivo supera el tamaño máximo permitido |
PATCH
Para actualizar el archivo almacenado bajo una key. Primero se realiza la modificación en el endpoint, obteniendose la nueva url para subir el nuevo archivo.
Notar que al momento de realizar esta consulta, el nuevo valor reemplaza al anterior y el archivo reemplazado se elimina inmediatamente. En las consultas GET se listará el nuevo archivo pero hasta que el cliente no lo suba con la URL provista, el mismo no estará disponible.
POST /api/mktplace/filestorage/users ? operation = PATCH
POST /api/mktplace/filestorage/fields ? operation = PATCH
POST /api/mktplace/filestorage/farms ? operation = PATCH
Body: application/json
en el Body:
key:stringkey sobre la que se quiere guardar el archivooriginal_file_name:stringnombre del archivo que se quiere subirmd5:stringhash MD5 codificado en base64 del archivo a subir Este es el valor que luego debe enviarse al subir el archivo.file_size_kb:floattamaño en kb del archivo a subircreate:booleanindica si crear la key en caso de no existir. Por defaulf esfalsepor lo que si la key indicada no existe, el endpoint responderá con error.user:UID-XXXXid encriptado. sólo obligatorio en el scope users, indicando el id del usuario sobre el cual se desea guardar.field:uuid/intsólo obligatorio en el scope fields, indicando el id del lote sobre el cual se desea guardar.farm:uuid/intsólo obligatorio en el scope farms, indicando el id del campo sobre el cual se desea guardar.
Respuesta:
{
"data": {
"url": {} // objeto POST-URL
}
}
Tras una respuesta correcta, la key ya se encuentra actualizada y el próximo paso es subir el archivo.
Para eso se utiliza el objeto POST-URL como se indicó previamente.
Posibles errores:
| code | |
|---|---|
| 2 | formato de key inválido |
| 3 | no se tiene permiso sobre el recurso (usuario/lote/campo) indicado |
| 5 | la key indicada no existe para el recurso indicado, y no se envió el flag de create |
| 7 | el archivo supera el tamaño máximo permitido |
DELETE
Para borrar keys. Se pueden borrar una key o un grupo de keys para todos los recursos del scope o filtrar por id de recurso.
POST /api/mktplace/filestorage/users ? operation = DELETE
POST /api/mktplace/filestorage/fields ? operation = DELETE
POST /api/mktplace/filestorage/farms ? operation = DELETE
Body: application/json
en el Body:
keys:arraykeys a eliminar - Obligatorioid:string|intid del recurso - Opcional
Respuesta:
{
"data": {
"keys": [] // keys eliminadas
}
}
Posibles errores:
| code | |
|---|---|
| 2 | formato de key inválido |
| 3 | no se tiene permiso sobre el recurso (usuario/lote/campo) indicado |
Request a AWS de ejemplo (Python)
# aurapps file storage
import requests
import base64
import hashlib
extension_token = ""
api_url = 'https://api.testing.auravant.com/larry'
def create_file(key, original_file_name, md5, file_size_kb, overwrite):
"""
llamada a modo de ejemplo. Segun la operacion y el scope los parametros cambian
Parameters
----------
key: str
key to store
original_file_name: str
the filename of the file to upload
md5: the md5 in base64 of the file's content
file_size_kb: int|float
size in kb
overwrite: bool
Returns
-------
"""
resp = requests.post(
url=f'{api_url}/api/mktplace/filestorage/users?operation=PUT',
headers={'Authorization': f'Bearer {extension_token}'},
json={
"key": key,
"original_file_name": original_file_name,
"md5": md5,
"file_size_kb": file_size_kb,
"overwrite": overwrite
}
)
if resp.status_code != 200:
print(resp.content)
raise ValueError()
return resp.json()['data']
def list_files(keys: list):
payload = {
"get_url": True # puede ser falso para solo listar los objetos
}
if keys:
payload["keys"] = keys
else:
raise ValueError('es obligatorio indicar key(s)')
resp = requests.post(
url=f'{api_url}/api/mktplace/filestorage/users?operation=GET',
headers={'Authorization': f'Bearer {extension_token}'},
json=payload
)
if resp.status_code != 200:
print(resp.content)
raise ValueError()
return resp.json()['objects']
def calculate_md5_and_size(file_path):
"""
calculate file's md5 and encode it to base64
Parameters
----------
file_path
Returns
-------
"""
with open(file_path, 'rb') as _f:
_hash = hashlib.md5(_f.read()).digest()
end = _f.tell()
return base64.b64encode(_hash).decode(), end / 1000
def main(key):
file_path = "./README.md"
file_md5, file_size_kb = calculate_md5_and_size(file_path)
creation_data = create_file(key, file_path.split('/')[-1], file_md5, file_size_kb, True)
print(creation_data)
url = creation_data['url']
url['fields']['Content-MD5'] = file_md5
with open(file_path) as f:
resp = requests.post(
url['url'],
files={'file': f}, # (file_path, f)},
data=url['fields'],
)
print(resp.content)
main('key1')
a = list_files(['key1'])
print(a)
Request a AWS de ejemplo (JS)
// Esto esta pendiente, pero dejo la parte del md5:
const reader = new FileReader();
reader.readAsBinaryString(blob);
reader.onloadend = () => {
resolve(CryptoJS.enc.Base64.stringify(CryptoJS.MD5(CryptoJS.enc.Latin1.parse(reader.result.toString()))));
};