diff --git a/src/main.rs b/src/main.rs index 9499ce7..4d82d42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -280,6 +280,7 @@ fn main() -> Result<()> { .service(routes::put_object) .service(routes::put_object_attribute) .service(routes::delete_object) + .service(routes::get_address) .service(routes::get_all_attributes) .service(routes::api_refresh) .service(routes::list_hier) diff --git a/src/routes.rs b/src/routes.rs index a6c653b..2ddf57f 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -619,6 +619,33 @@ pub async fn delete_object( // Ok(HttpResponse::Ok().finish()) // } +#[derive(Deserialize)] +pub struct GetAddressRequest { + attribute: Option, +} + +#[get("/api/address")] +pub async fn get_address( + web::Query(query): web::Query, +) -> Result { + let address = match query { + GetAddressRequest { + attribute: Some(attribute), + } => Address::Attribute(attribute), + _ => Err(ErrorBadRequest("Specify one of: `attribute`"))?, + }; + + Ok(HttpResponse::Ok() + .set_header( + http::header::CACHE_CONTROL, + CacheControl(vec![ + CacheDirective::MaxAge(2678400), + CacheDirective::Extension("immutable".into(), None), + ]), + ) + .json(format!("{}", address))) +} + #[get("/api/all/attributes")] pub async fn get_all_attributes(state: web::Data) -> Result { let connection = state.upend.connection().map_err(ErrorInternalServerError)?; diff --git a/webui/src/components/Inspect.svelte b/webui/src/components/Inspect.svelte index be219f2..233db15 100644 --- a/webui/src/components/Inspect.svelte +++ b/webui/src/components/Inspect.svelte @@ -15,10 +15,16 @@ import type { BrowseContext } from "../util/browse"; import { useParams } from "svelte-navigator"; import { GROUP_TYPE_ADDR } from "upend/constants"; - import { deleteEntry, putEntityAttribute, putEntry } from "../lib/api"; + import { + deleteEntry, + putEntityAttribute, + putEntry, + queryOnce, + } from "../lib/api"; import Icon from "./utils/Icon.svelte"; import BlobViewer from "./display/BlobViewer.svelte"; import { i18n } from "../i18n"; + import EntryList from "./widgets/EntryList.svelte"; const dispatch = createEventDispatcher(); const params = useParams(); @@ -40,7 +46,7 @@ addresses: addressesStore, } as BrowseContext); - $: ({ entity, error, revalidate } = useEntity(address)); + $: ({ entity, entityInfo, error, revalidate } = useEntity(address)); $: allTypeAddresses = ($entity?.attr["IS"] || []).map((attr) => attr.value.c); @@ -127,6 +133,15 @@ .map((e) => [e.address, e.entity]) .sort(); // TODO + let attributesUsed: UpEntry[] = []; + $: { + if ($entityInfo?.t === "Attribute") { + queryOnce(`(matches ? "${$entityInfo.c}" ?)`).then( + (result) => (attributesUsed = result.entries) + ); + } + } + async function onChange(ev: CustomEvent) { const change = ev.detail; switch (change.type) { @@ -300,6 +315,15 @@ on:change={onChange} /> {/if} + + {#if $entityInfo?.t === "Attribute"} + + {/if} {#if editable} diff --git a/webui/src/components/display/UpLink.svelte b/webui/src/components/display/UpLink.svelte index 7650b9c..632c2e7 100644 --- a/webui/src/components/display/UpLink.svelte +++ b/webui/src/components/display/UpLink.svelte @@ -1,6 +1,6 @@ @@ -43,7 +51,8 @@ @@ -56,4 +65,7 @@ :global(.uplink.passthrough) { display: contents; } + :global(.uplink.unresolved) { + pointer-events: none; + } diff --git a/webui/src/components/display/UpObject.svelte b/webui/src/components/display/UpObject.svelte index 22a710a..3e0af91 100644 --- a/webui/src/components/display/UpObject.svelte +++ b/webui/src/components/display/UpObject.svelte @@ -4,13 +4,15 @@ import HashBadge from "./HashBadge.svelte"; import Ellipsis from "../utils/Ellipsis.svelte"; import UpLink from "./UpLink.svelte"; - import { useEntity } from "../../lib/entity"; + import { useEntity, type EntityInfo } from "../../lib/entity"; import { API_URL, nativeOpen as nativeOpenApi } from "../../lib/api"; - import { readable } from "svelte/store"; + import { readable, type Readable } from "svelte/store"; import { notify, UpNotification } from "../../notifications"; import IconButton from "../utils/IconButton.svelte"; import { vaultInfo } from "../../util/info"; import type { BrowseContext } from "../../util/browse"; + import type { UpObject } from "upend"; + import { i18n } from "../../i18n"; const dispatch = createEventDispatcher(); export let address: string; @@ -18,8 +20,9 @@ export let link = false; export let banner = false; - let entity = readable(undefined); - $: if (labels === undefined) ({ entity } = useEntity(address)); + let entity: Readable = readable(undefined); + let entityInfo: Readable = readable(undefined); + $: if (labels === undefined) ({ entity, entityInfo } = useEntity(address)); // isFile $: isFile = $entity?.get("IS") === BLOB_TYPE_ADDR; @@ -29,9 +32,16 @@ $: inferredIds = $entity?.identify() || []; $: resolving = inferredIds.concat(labels || []).length == 0 && !$entity; - $: displayLabel = - Array.from(new Set(inferredIds.concat(labels || []))).join(" | ") || - address; + let displayLabel = address; + $: { + displayLabel = Array.from(new Set(inferredIds.concat(labels || []))).join( + " | " + ); + if (!displayLabel && $entityInfo?.t === "Attribute") { + displayLabel = `${$i18n.t("Attribute")}: ${$entityInfo.c}`; + } + displayLabel = displayLabel || address; + } $: dispatch("resolved", inferredIds); @@ -84,7 +94,11 @@ class:left-active={address == $addresses[$index - 1]} class:right-active={address == $addresses[$index + 1]} > -
+
diff --git a/webui/src/components/widgets/EntryList.svelte b/webui/src/components/widgets/EntryList.svelte index 2c03231..b82953b 100644 --- a/webui/src/components/widgets/EntryList.svelte +++ b/webui/src/components/widgets/EntryList.svelte @@ -16,6 +16,7 @@ import { attributeLabels } from "../../util/labels"; import { formatDuration } from "../../util/fragments/time"; import { i18n } from "../../i18n"; + import UpLink from "../display/UpLink.svelte"; const dispatch = createEventDispatcher(); export let columns: string | undefined = undefined; @@ -229,10 +230,12 @@ Object.keys($attributeLabels).includes(entry.attribute) )} > - + + + {:else if column == VALUE_COL} diff --git a/webui/src/lib/api.ts b/webui/src/lib/api.ts index a2fe5d7..1520b40 100644 --- a/webui/src/lib/api.ts +++ b/webui/src/lib/api.ts @@ -138,3 +138,10 @@ export async function fetchStoreInfo(): Promise<{ [key: string]: StoreInfo }> { const response = await fetch(`${API_URL}/store`); return await response.json(); } + +export async function getAddress(input: { attribute: string }): Promise { + const response = await fetch( + `${API_URL}/address?attribute=${input.attribute}` + ); + return await response.json(); +} diff --git a/webui/yarn.lock b/webui/yarn.lock index 8a17427..b69891c 100644 --- a/webui/yarn.lock +++ b/webui/yarn.lock @@ -4068,8 +4068,8 @@ __metadata: "upend@file:../tools/upend_js::locator=upend-kestrel%40workspace%3A.": version: 0.0.1 - resolution: "upend@file:../tools/upend_js#../tools/upend_js::hash=4972b4&locator=upend-kestrel%40workspace%3A." - checksum: 9449fce51433df8ce7a4d4e597fbd8811611f15ed3d8c499a6e14fd29bbba40adaf7f441e5cd4f4f3439e9fb46adf269783cef574904975569ba99c9933e711d + resolution: "upend@file:../tools/upend_js#../tools/upend_js::hash=b32491&locator=upend-kestrel%40workspace%3A." + checksum: 4e4e5c0a6498ad2ea8369a91e3c67c160186a96d47073469d61794c3717824198bd2de7851f3e0786833fa5f8c0eb85c65f7d7dc9c0114b9328b2a1060f645b2 languageName: node linkType: hard