mirror of
https://github.com/home-assistant/frontend.git
synced 2026-06-11 02:43:28 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66235a4c99 |
+29
-15
@@ -77,15 +77,19 @@ export const clearBrandsTokenRefresh = (): void => {
|
||||
};
|
||||
|
||||
export const brandsUrl = (options: BrandsOptions, hassUrl?: string): string => {
|
||||
// The brands API requires a token; without one the request 401s. Return an
|
||||
// empty src so no request fires until the token is available. Components
|
||||
// re-render once the token arrives (see connection-mixin) and recompute this.
|
||||
if (!_brandsAccessToken) {
|
||||
return "";
|
||||
}
|
||||
hassUrl = hassUrl ?? location.origin;
|
||||
const base = `/api/brands/integration/${options.domain}/${
|
||||
options.darkOptimized ? "dark_" : ""
|
||||
}${options.type}.png`;
|
||||
|
||||
const url = new URL(base, hassUrl);
|
||||
if (_brandsAccessToken) {
|
||||
url.searchParams.set("token", _brandsAccessToken);
|
||||
}
|
||||
url.searchParams.set("token", _brandsAccessToken);
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
@@ -93,34 +97,44 @@ export const hardwareBrandsUrl = (
|
||||
options: HardwareBrandsOptions,
|
||||
hassUrl?: string
|
||||
): string => {
|
||||
// See brandsUrl: wait for the token before producing a loadable URL.
|
||||
if (!_brandsAccessToken) {
|
||||
return "";
|
||||
}
|
||||
hassUrl = hassUrl ?? location.origin;
|
||||
const base = `/api/brands/hardware/${options.category}/${
|
||||
options.darkOptimized ? "dark_" : ""
|
||||
}${options.manufacturer}${options.model ? `_${options.model}` : ""}.png`;
|
||||
|
||||
const url = new URL(base, hassUrl);
|
||||
if (_brandsAccessToken) {
|
||||
url.searchParams.set("token", _brandsAccessToken);
|
||||
}
|
||||
url.searchParams.set("token", _brandsAccessToken);
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
export const addBrandsAuth = (url: string, hassUrl?: string): string => {
|
||||
hassUrl = hassUrl ?? location.origin;
|
||||
if (!_brandsAccessToken) {
|
||||
return url;
|
||||
}
|
||||
|
||||
let parsedUrl: URL;
|
||||
try {
|
||||
const parsedUrl = new URL(url, hassUrl);
|
||||
if (!parsedUrl.pathname.startsWith("/api/brands/")) {
|
||||
return url;
|
||||
}
|
||||
parsedUrl.searchParams.set("token", _brandsAccessToken);
|
||||
return parsedUrl.toString();
|
||||
parsedUrl = new URL(url, hassUrl);
|
||||
} catch {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Non-brands URLs (e.g. CDN brands.home-assistant.io or camera proxies) are
|
||||
// returned unchanged; they don't use the brands token.
|
||||
if (!parsedUrl.pathname.startsWith("/api/brands/")) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Brands API request without a token would 401; return an empty src so it
|
||||
// doesn't fire until the token is available.
|
||||
if (!_brandsAccessToken) {
|
||||
return "";
|
||||
}
|
||||
|
||||
parsedUrl.searchParams.set("token", _brandsAccessToken);
|
||||
return parsedUrl.toString();
|
||||
};
|
||||
|
||||
export const extractDomainFromBrandUrl = (url: string): string => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { assert, describe, it, vi, afterEach } from "vitest";
|
||||
import { assert, describe, it, vi, afterEach, beforeEach } from "vitest";
|
||||
import type { HomeAssistant } from "../../src/types";
|
||||
import {
|
||||
addBrandsAuth,
|
||||
@@ -6,17 +6,74 @@ import {
|
||||
clearBrandsTokenRefresh,
|
||||
fetchAndScheduleBrandsAccessToken,
|
||||
fetchBrandsAccessToken,
|
||||
hardwareBrandsUrl,
|
||||
scheduleBrandsTokenRefresh,
|
||||
} from "../../src/util/brands-url";
|
||||
|
||||
// NOTE: the cached brands token is module-level state that persists across
|
||||
// tests. The "without a token" assertions below must run before any test
|
||||
// fetches a token, so this block is intentionally declared first.
|
||||
describe("Brands URLs without a token", () => {
|
||||
// The brands API requires a token; until one is fetched the URL builders
|
||||
// return an empty src so no token-less request (which 401s) fires. Components
|
||||
// re-render once the token arrives and recompute the URL.
|
||||
it("brandsUrl returns an empty src", () => {
|
||||
assert.strictEqual(
|
||||
brandsUrl(
|
||||
{ domain: "cloud", type: "logo" },
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
it("hardwareBrandsUrl returns an empty src", () => {
|
||||
assert.strictEqual(
|
||||
hardwareBrandsUrl(
|
||||
{ category: "boards", manufacturer: "raspberry_pi" },
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
it("addBrandsAuth returns an empty src for brands URLs", () => {
|
||||
assert.strictEqual(
|
||||
addBrandsAuth(
|
||||
"/api/brands/integration/demo/icon.png",
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
it("addBrandsAuth returns non-brands URLs unchanged", () => {
|
||||
assert.strictEqual(
|
||||
addBrandsAuth(
|
||||
"/api/camera_proxy/camera.foo?token=abc",
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"/api/camera_proxy/camera.foo?token=abc"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Generate brands Url", () => {
|
||||
// Fetch a token before these run so the URL builders produce loadable URLs.
|
||||
beforeEach(async () => {
|
||||
const mockHass = {
|
||||
callWS: async () => ({ token: "test-token-123" }),
|
||||
} as unknown as HomeAssistant;
|
||||
await fetchBrandsAccessToken(mockHass);
|
||||
});
|
||||
|
||||
it("Generate logo brands url for cloud component", () => {
|
||||
assert.strictEqual(
|
||||
brandsUrl(
|
||||
{ domain: "cloud", type: "logo" },
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"http://homeassistant.local:8123/api/brands/integration/cloud/logo.png"
|
||||
"http://homeassistant.local:8123/api/brands/integration/cloud/logo.png?token=test-token-123"
|
||||
);
|
||||
});
|
||||
it("Generate icon brands url for cloud component", () => {
|
||||
@@ -25,7 +82,7 @@ describe("Generate brands Url", () => {
|
||||
{ domain: "cloud", type: "icon" },
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"http://homeassistant.local:8123/api/brands/integration/cloud/icon.png"
|
||||
"http://homeassistant.local:8123/api/brands/integration/cloud/icon.png?token=test-token-123"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -35,7 +92,7 @@ describe("Generate brands Url", () => {
|
||||
{ domain: "cloud", type: "logo", darkOptimized: true },
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"http://homeassistant.local:8123/api/brands/integration/cloud/dark_logo.png"
|
||||
"http://homeassistant.local:8123/api/brands/integration/cloud/dark_logo.png?token=test-token-123"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -51,16 +108,6 @@ describe("addBrandsAuth", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("Returns brands URL unchanged when no token is available", () => {
|
||||
assert.strictEqual(
|
||||
addBrandsAuth(
|
||||
"/api/brands/integration/demo/icon.png",
|
||||
"http://homeassistant.local:8123"
|
||||
),
|
||||
"/api/brands/integration/demo/icon.png"
|
||||
);
|
||||
});
|
||||
|
||||
it("Appends token to brands URL when token is available", async () => {
|
||||
const mockHass = {
|
||||
callWS: async () => ({ token: "test-token-123" }),
|
||||
|
||||
Reference in New Issue
Block a user