mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge remote-tracking branch 'origin/master' into lens_restructure
# Conflicts: # src/renderer/api/kube-api.ts
This commit is contained in:
commit
4d6bb47bcd
@ -45,10 +45,62 @@ const tests: ParseAPITest[] = [
|
|||||||
namespace: undefined,
|
namespace: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
url: "/api/v1/namespaces",
|
||||||
|
expected: {
|
||||||
|
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');
|
jest.mock('../kube-watch-api.ts', () => 'KubeWatchApi');
|
||||||
describe("parseAPI unit tests", () => {
|
describe("parseApi unit tests", () => {
|
||||||
for (const i in tests) {
|
for (const i in tests) {
|
||||||
const { url: tUrl, expected:tExpect} = tests[i];
|
const { url: tUrl, expected:tExpect} = tests[i];
|
||||||
test(`test #${parseInt(i)+1}`, () => {
|
test(`test #${parseInt(i)+1}`, () => {
|
||||||
|
|||||||
@ -19,23 +19,17 @@ export class ApiManager {
|
|||||||
private views = observable.map<KubeApi, ApiComponents>();
|
private views = observable.map<KubeApi, ApiComponents>();
|
||||||
|
|
||||||
getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) {
|
getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) {
|
||||||
const apis = this.apis;
|
|
||||||
if (typeof pathOrCallback === "string") {
|
if (typeof pathOrCallback === "string") {
|
||||||
let api = apis.get(pathOrCallback);
|
return this.apis.get(pathOrCallback) || this.apis.get(KubeApi.parseApi(pathOrCallback).apiBase);
|
||||||
if (!api) {
|
|
||||||
const { apiBase } = KubeApi.parseApi(pathOrCallback);
|
|
||||||
api = apis.get(apiBase);
|
|
||||||
}
|
|
||||||
return api;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Array.from(apis.values()).find(pathOrCallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Array.from(this.apis.values()).find(pathOrCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerApi(apiBase: string, api: KubeApi) {
|
registerApi(apiBase: string, api: KubeApi) {
|
||||||
if (this.apis.has(apiBase)) return;
|
if (!this.apis.has(apiBase)) {
|
||||||
this.apis.set(apiBase, api);
|
this.apis.set(apiBase, api);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected resolveApi(api: string | KubeApi): KubeApi {
|
protected resolveApi(api: string | KubeApi): KubeApi {
|
||||||
|
|||||||
@ -42,21 +42,65 @@ export interface IKubeApiLinkBase extends IKubeApiLinkRef {
|
|||||||
export class KubeApi<T extends KubeObject = any> {
|
export class KubeApi<T extends KubeObject = any> {
|
||||||
static parseApi(apiPath = ""): IKubeApiLinkBase {
|
static parseApi(apiPath = ""): IKubeApiLinkBase {
|
||||||
apiPath = new URL(apiPath, location.origin).pathname;
|
apiPath = new URL(apiPath, location.origin).pathname;
|
||||||
|
|
||||||
const [, prefix, ...parts] = apiPath.split("/");
|
const [, prefix, ...parts] = apiPath.split("/");
|
||||||
const apiPrefix = `/${prefix}`;
|
const apiPrefix = `/${prefix}`;
|
||||||
|
|
||||||
const [left, right, found] = splitArray(parts, "namespaces");
|
const [left, right, namespaced] = splitArray(parts, "namespaces");
|
||||||
let apiGroup, apiVersion, namespace, resource, name;
|
let apiGroup, apiVersion, namespace, resource, name;
|
||||||
if (found) {
|
|
||||||
if (left.length == 0) {
|
if (namespaced) {
|
||||||
throw new Error(`invalid apiPath: ${apiPath}`)
|
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();
|
apiVersion = left.pop();
|
||||||
apiGroup = left.join("/");
|
apiGroup = left.join("/");
|
||||||
[namespace, resource = "namespaces", name] = right; // fix: "resource" is empty when "/api/v1/namespaces"
|
|
||||||
} else {
|
} else {
|
||||||
[apiGroup, apiVersion, resource] = left;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiVersionWithGroup = [apiGroup, apiVersion].filter(v => v).join("/");
|
const apiVersionWithGroup = [apiGroup, apiVersion].filter(v => v).join("/");
|
||||||
const apiBase = [apiPrefix, apiGroup, apiVersion, resource].filter(v => v).join("/");
|
const apiBase = [apiPrefix, apiGroup, apiVersion, resource].filter(v => v).join("/");
|
||||||
|
|
||||||
@ -154,7 +198,7 @@ export class KubeApi<T extends KubeObject = any> {
|
|||||||
if (KubeObject.isJsonApiData(data)) {
|
if (KubeObject.isJsonApiData(data)) {
|
||||||
return new KubeObjectConstructor(data);
|
return new KubeObjectConstructor(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// process items list response
|
// process items list response
|
||||||
if (KubeObject.isJsonApiDataList(data)) {
|
if (KubeObject.isJsonApiDataList(data)) {
|
||||||
const { apiVersion, items, metadata } = data;
|
const { apiVersion, items, metadata } = data;
|
||||||
@ -166,12 +210,12 @@ export class KubeApi<T extends KubeObject = any> {
|
|||||||
...item,
|
...item,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom apis might return array for list response, e.g. users, groups, etc.
|
// custom apis might return array for list response, e.g. users, groups, etc.
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
return data.map(data => new KubeObjectConstructor(data));
|
return data.map(data => new KubeObjectConstructor(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +233,7 @@ export class KubeApi<T extends KubeObject = any> {
|
|||||||
|
|
||||||
async create({ name = "", namespace = "default" } = {}, data?: Partial<T>): Promise<T> {
|
async create({ name = "", namespace = "default" } = {}, data?: Partial<T>): Promise<T> {
|
||||||
const apiUrl = this.getUrl({ namespace });
|
const apiUrl = this.getUrl({ namespace });
|
||||||
|
|
||||||
return this.request
|
return this.request
|
||||||
.post(apiUrl, {
|
.post(apiUrl, {
|
||||||
data: merge({
|
data: merge({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user