From f68b96efe34f7e83f749af14e722272f05408962 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 18 Jun 2020 14:41:30 -0400 Subject: [PATCH] fix parseApi to be more consitent with the older version - Add many more unit tests (these are exact copies of the old behaviour so that any changes do not change behaviour) - Make explicit, with documentation, why certain actions are being taken with the parsing of the api URL parts Signed-off-by: Sebastian Malton --- .../client/api/__test__/parseAPI.test.ts | 53 ++++++++++++++--- dashboard/client/api/kube-api.ts | 57 +++++++++++++++++-- 2 files changed, 98 insertions(+), 12 deletions(-) diff --git a/dashboard/client/api/__test__/parseAPI.test.ts b/dashboard/client/api/__test__/parseAPI.test.ts index b3a1320082..b748098622 100644 --- a/dashboard/client/api/__test__/parseAPI.test.ts +++ b/dashboard/client/api/__test__/parseAPI.test.ts @@ -46,18 +46,57 @@ const tests: ParseAPITest[] = [ }, }, { - url: "/apis/namespaces/default/es/gke-svc-vodka-1-app-1-w-l-1-9eea2bfe-4jnl", + url: "/api/v1/namespaces", expected: { - apiBase: "/apis", - apiPrefix: "/apis", - apiGroup: undefined, - apiVersion: undefined, - apiVersionWithGroup: "", - resource: undefined, + apiBase: "/api/v1/namespaces", + apiPrefix: "/api", + apiGroup: "", + apiVersion: "v1", + apiVersionWithGroup: "v1", + resource: "namespaces", name: undefined, namespace: undefined, }, }, + { + url: "/api/v1/secrets", + expected: { + apiBase: "/api/v1/secrets", + apiPrefix: "/api", + apiGroup: "", + apiVersion: "v1", + apiVersionWithGroup: "v1", + resource: "secrets", + name: undefined, + namespace: undefined, + }, + }, + { + url: "/api/v1/nodes/minikube", + expected: { + apiBase: "/api/v1/nodes", + apiPrefix: "/api", + apiGroup: "", + apiVersion: "v1", + apiVersionWithGroup: "v1", + resource: "nodes", + name: "minikube", + namespace: undefined, + }, + }, + { + url: "/api/foo-bar/nodes/minikube", + expected: { + apiBase: "/api/foo-bar/nodes", + apiPrefix: "/api", + apiGroup: "", + apiVersion: "foo-bar", + apiVersionWithGroup: "foo-bar", + resource: "nodes", + name: "minikube", + namespace: undefined, + }, + }, ]; jest.mock('../kube-watch-api.ts', () => 'KubeWatchApi'); diff --git a/dashboard/client/api/kube-api.ts b/dashboard/client/api/kube-api.ts index f495e1eb5f..ca2cd574f9 100644 --- a/dashboard/client/api/kube-api.ts +++ b/dashboard/client/api/kube-api.ts @@ -8,6 +8,7 @@ import { apiKube } from "./index"; import { kubeWatchApi } from "./kube-watch-api"; import { apiManager } from "./api-manager"; import { split } from "../utils/arrays"; +import isEqual from "lodash/isEqual"; export interface IKubeApiOptions { kind: string; // resource type within api-group, e.g. "Namespace" @@ -40,21 +41,67 @@ export interface IKubeApiLinkBase extends IKubeApiLinkRef { } export class KubeApi { - static parseApi(apiPath = ""): IKubeApiLinkBase { + static parseApi(apiPath = "") { apiPath = new URL(apiPath, location.origin).pathname; + const [, prefix, ...parts] = apiPath.split("/"); const apiPrefix = `/${prefix}`; const [left, right, found] = split(parts, "namespaces"); let apiGroup, apiVersion, namespace, resource, name; - if (found && left.length > 0) { + if (!found) { + switch (left.length) { + case 2: + resource = left.pop(); + case 1: + apiVersion = left.pop(); + apiGroup = ""; + break; + default: + /** + * Given that + * - `apiVersion` is `GROUP/VERSION` and + * - `VERSION` is `DNS_LABEL` which is /^[a-z0-9]((-[a-z0-9])|[a-z0-9])*$/i + * where length <= 63 + * - `GROUP` is /^D(\.D)*$/ where D is `DNS_LABEL` and length <= 253 + * + * There is no well defined selection from an array of items that were + * seperated by '/' + * + * Solution is to create a huristic. Namely: + * 1. if '.' in left[0] then apiGroup <- left[0] + * 2. if left[1] matches /^v[0-9]/ then apiGroup, apiVersion <- left[0], left[1] + * 3. otherwise assume apiVersion <- left[0] + * 4. always resource, name <- left[(0 or 1)+1..] + */ + if (left[0].includes('.') || left[1].match(/^v[0-9]/)) { + [apiGroup, apiVersion] = left; + resource = left.slice(2).join("/") + } else { + apiGroup = ""; + apiVersion = left[0]; + [resource, name] = left.slice(1) + } + break; + } + } else { + switch (right.length) { + case 0: + resource = "namespaces"; // special case this due to `split` removing namespaces + break; + case 1: + resource = right[0]; + break; + default: + [namespace, resource, name] = right; + break; + } + apiVersion = left.pop(); apiGroup = left.join("/"); - [namespace, resource, name] = right; - } else { - [apiGroup, apiVersion, resource] = left; } + const apiVersionWithGroup = [apiGroup, apiVersion].filter(v => v).join("/"); const apiBase = [apiPrefix, apiGroup, apiVersion, resource].filter(v => v).join("/");