Skip to content

Commit

Permalink
Add possibility to tag objects.
Browse files Browse the repository at this point in the history
This is a continuation to the tagging buckets feature from PR #419.
The openApi spec supports tuples in 3.1 with prefixItems. We may want to change the api docs when that comes into use.
  • Loading branch information
csc-felipe committed Dec 10, 2021
1 parent 6c64bfb commit 30dd43a
Show file tree
Hide file tree
Showing 16 changed files with 553 additions and 110 deletions.
67 changes: 56 additions & 11 deletions docs/_static/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,37 @@ paths:
description: Unauthorized
404:
description: Not Found
post:
tags:
- Frontend
summary: Update user created container metadata.
parameters:
- name: container
in: query
description: The container which metadata will be updated.
schema:
type: string
example: test-container-1
required: true
requestBody:
description: Bucket metadata as a key-value object. Updates must include all the metadata for the bucket. Omitted keys are removed by the swift backend.
required: true
content:
application/json:
schema:
type: object
properties:
key:
type: string
example:
owner: project-team
responses:
204:
description: Container metadata was updated. No Content.
403:
description: Unauthorized
404:
description: Container was not found
/api/bucket/object/meta:
get:
tags:
Expand Down Expand Up @@ -750,34 +781,48 @@ paths:
post:
tags:
- Frontend
summary: Update user created container metadata.
summary: Update user created object metadata.
parameters:
- name: container
in: query
description: The container which metadata will be updated.
description: The container which contains the objects to be updated.
schema:
type: string
example: test-container-1
required: true
requestBody:
description: Bucket metadata as a key-value object. Updates must include all the metadata for the bucket. Omitted keys are removed by the swift backend.
description: Object metadata as an array of tuples with the object name and a key-value object. Updates must include all the metadata for the object. Omitted keys are removed by the swift backend.
required: true
content:
application/json:
schema:
type: object
properties:
key:
type: string
type: array
example:
owner: project-team
- - object-name
- key: value
items:
type: array
items:
oneOf:
- type: string
- type: object
minItems: 2
maxItems: 2
example:
- object-name
- key: value
example:
- - object-name
- key: value
responses:
204:
description: Container metadata was updated. No Content.
description: Object metadata was updated. No Content.
400:
description: Payload malformed. Bad request.
403:
description: Unauthorized
description: Unauthorized.
404:
description: Container was not found
description: At least one object was not found.
/api/shared/objects:
get:
tags:
Expand Down
45 changes: 43 additions & 2 deletions swift_browser_ui/ui/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import aiohttp.web
from swiftclient.exceptions import ClientException
from swiftclient.service import SwiftError
from swiftclient.service import SwiftError, SwiftPostObject
from swiftclient.service import SwiftService, get_conn # for type hints
from swiftclient.utils import generate_temp_url

Expand Down Expand Up @@ -492,7 +492,7 @@ async def get_object_metadata(
for i in res
]

# Strip unnecessary specifcations from header names and split open s3
# Strip unnecessary specifications from header names and split open s3
# information so that it doesn't have to be done in the browser
for i in res:
i[1] = {k.replace("x-object-meta-", ""): v for k, v in i[1].items()}
Expand Down Expand Up @@ -589,6 +589,47 @@ async def get_metadata_object(request: aiohttp.web.Request) -> aiohttp.web.Respo
return aiohttp.web.json_response(await get_object_metadata(conn, meta_cont, meta_obj))


async def update_metadata_object(request: aiohttp.web.Request) -> aiohttp.web.Response:
"""Update metadata for an object."""
session = api_check(request)
request.app["Log"].info(
"API cal for updating container metadata from "
f"{request.remote}, sess: {session} :: {time.ctime()}"
)

# Get required variables from query string
container = request.query.get("container", "") or None
objects = await request.json()

if not (container or objects):
raise aiohttp.web.HTTPBadRequest

objects_post = []
try:
for (name, meta) in objects:
meta = [(key, value) for key, value in meta.items() if value]
objects_post.append(
SwiftPostObject(
object_name=name,
options={
"meta": meta,
},
)
)
except ValueError as e:
request.app["Log"].error(f"Payload seems to be malformed: {e}")
raise aiohttp.web.HTTPBadRequest

conn = request.app["Sessions"][session]["ST_conn"]
ret = conn.post(container=container, objects=objects_post)

for r in ret:
if not r["success"]:
raise aiohttp.web.HTTPNotFound

return aiohttp.web.HTTPNoContent()


async def get_project_metadata(request: aiohttp.web.Request) -> aiohttp.web.Response:
"""Get the bare minimum required project metadata from OS."""
# The project metadata needs to be filtered for sensitive information, as
Expand Down
2 changes: 2 additions & 0 deletions swift_browser_ui/ui/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
swift_check_object_chunk,
swift_replicate_container,
update_metadata_bucket,
update_metadata_object,
)
from swift_browser_ui.ui.health import handle_health_check
from swift_browser_ui.ui.settings import setd
Expand Down Expand Up @@ -161,6 +162,7 @@ async def servinit() -> aiohttp.web.Application:
aiohttp.web.get("/api/bucket/meta", get_metadata_bucket),
aiohttp.web.post("/api/bucket/meta", update_metadata_bucket),
aiohttp.web.get("/api/bucket/object/meta", get_metadata_object),
aiohttp.web.post("/api/bucket/object/meta", update_metadata_object),
aiohttp.web.get("/api/project/meta", get_project_metadata),
aiohttp.web.get("/api/project/acl", get_access_control_metadata),
aiohttp.web.post("/api/access/{container}", add_project_container_acl),
Expand Down
42 changes: 40 additions & 2 deletions swift_browser_ui_frontend/src/common/api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// API fetch functions.

import { getHumanReadableSize } from "@/common/conv";
import {
getHumanReadableSize,
makeGetObjectsMetaURL,
} from "@/common/conv";

export async function getUser() {
// Function to get the username of the currently displayed user.
Expand Down Expand Up @@ -131,7 +134,42 @@ export async function getObjects(container) {
return objects;
}

export async function getSharedObjects(
export async function getObjectsMeta (
container,
objects,
url,
){
if (url === undefined) {
url = makeGetObjectsMetaURL(container, objects);
}

let ret = await fetch(
url, {method: "GET", credentials: "same-origin"},
);
return ret.json();
}

export async function updateObjectMeta (
container,
objectMeta,
){
let url = new URL(
"/api/bucket/object/meta?container=".concat(encodeURI(container)),
document.location.origin,
);

let ret = await fetch(
url,
{
method: "POST",
credentials: "same-origin",
body: JSON.stringify([objectMeta]),
},
);
return ret;
}

export async function getSharedObjects (
project,
container,
url,
Expand Down
36 changes: 29 additions & 7 deletions swift_browser_ui_frontend/src/common/conv.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

import {
getBucketMeta,
getAccessControlMeta,
getObjectsMeta,
} from "./api";

export default function getLangCookie() {
Expand Down Expand Up @@ -153,12 +155,32 @@ export function getHumanReadableSize(val) {
return ret;
}

function extractTags(meta) {
if ("usertags" in meta[1]) {
return meta[1]["usertags"].split(";");
}
return [];
}

export async function getTagsForContainer(containerName) {
let tags = [];
await getBucketMeta(containerName).then(meta => {
if ("usertags" in meta[1]) {
tags = meta[1]["usertags"].split(";");
}
});
return tags;
let meta = await getBucketMeta(containerName);
return extractTags(meta);
}

export async function getTagsForObjects(containerName, objectList, url) {
let meta = await getObjectsMeta(containerName, objectList, url);
meta.map(item => item[1] = extractTags(item));
return meta;
}

export function makeGetObjectsMetaURL(container, objects) {
return new URL(
"/api/bucket/object/meta?container="
.concat(encodeURI(container))
.concat("&object=")
.concat(encodeURI(objects.join(","))),
document.location.origin,
);
}

export const taginputConfirmKeys = [",", ";", ":", ".", " ", "Tab", "Enter"];
12 changes: 8 additions & 4 deletions swift_browser_ui_frontend/src/common/lang.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ let default_translations = {
copysuccess: "Started copying the bucket in the background",
copyfail: "Failed to copy the bucket",
renderFolders: "Render as Folders",
tagName: "Tags",
tagMessage: "Press enter to add.",
container_ops: {
addContainer: "Add a new bucket",
editContainer: "Editing bucket: ",
Expand All @@ -203,10 +205,10 @@ let default_translations = {
containerMessage: "The name of the new bucket",
fullDelete: "Deleting a bucket with contents requires deleting " +
"all objects inside it first.",
tagName: "Tags",
tagMessage: "Press enter to add.",
},
objects: {
objectName: "Object",
editObject: "Editing object: ",
deleteConfirm: "Delete Objects",
deleteObjects: "Delete Object / Objects",
deleteSuccess: "Objects deleted",
Expand Down Expand Up @@ -422,6 +424,8 @@ let default_translations = {
copysuccess: "Aloitettiin säiliön kopiointi taustalla",
copyfail: "Säiliön kopiointi epäonnistui",
renderFolders: "Näytä kansioina",
tagName: "Tägit",
tagMessage: "Paina 'enter' lisätäksesi.",
container_ops: {
addContainer: "Luo uusi säiliö",
editContainer: "Muokataan säiliötä: ",
Expand All @@ -431,10 +435,10 @@ let default_translations = {
containerName: "Säiliö",
containerMessage: "Uuden säiliön nimi",
fullDelete: "Säiliön sisältö on poistettava ennen säiliön postamista.",
tagName: "Tägit",
tagMessage: "Paina 'enter' lisätäksesi.",
},
objects: {
objectName: "Objekti",
editObject: "Muokataan objekti: ",
deleteConfirm: "Poista objektit",
deleteObjects: "Poista objekti / objektit",
deleteSuccess: "Objektit poistettu",
Expand Down
6 changes: 6 additions & 0 deletions swift_browser_ui_frontend/src/common/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Router from "vue-router";
import DashboardView from "@/views/Dashboard.vue";
import ContainersView from "@/views/Containers.vue";
import ObjectsView from "@/views/Objects.vue";
import EditObjectView from "@/views/EditObject.vue";
import SharedObjects from "@/views/SharedObjects";
import ShareRequests from "@/views/ShareRequests";
import SharedTo from "@/views/SharedTo";
Expand Down Expand Up @@ -89,5 +90,10 @@ export default new Router({
name: "ObjectsView",
component: ObjectsView,
},
{
path: "/browse/:user/:project/:container/:object/edit",
name: "EditObjectView",
component: EditObjectView,
},
],
});
Loading

0 comments on commit 30dd43a

Please sign in to comment.