Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dependency on /user and /profile API #657

Merged
merged 4 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/common/src/dto/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,8 @@ export interface IServerConfig {
};
cheNamespace: string;
}

export interface IUserProfile {
email: string;
username: string;
}
13 changes: 8 additions & 5 deletions packages/dashboard-backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { registerDockerConfigRoutes } from './routes/api/dockerConfig';
import { registerKubeConfigRoute } from './routes/api/kubeConfig';
import { registerNamespacesRoute } from './routes/api/namespaces';
import { registerServerConfigRoute } from './routes/api/serverConfig';
import { registerUserProfileRoute } from './routes/api/userProfile';
import { registerYamlResolverRoute } from './routes/api/yamlResolver';
import { registerFactoryAcceptanceRedirect } from './routes/factoryAcceptanceRedirect';
import { registerWorkspaceRedirect } from './routes/workspaceRedirect';
Expand Down Expand Up @@ -79,19 +80,21 @@ export default async function buildApp(server: FastifyInstance): Promise<void> {
registerNamespacesRoute(server);
}

registerDevworkspacesRoutes(server);
registerClusterConfigRoute(server);

registerClusterInfoRoute(server);

registerDevWorkspaceTemplates(server);

registerDockerConfigRoutes(server);
registerDevworkspacesRoutes(server);

registerServerConfigRoute(server);
registerDockerConfigRoutes(server);

registerKubeConfigRoute(server);

registerClusterInfoRoute(server);
registerServerConfigRoute(server);

registerClusterConfigRoute(server);
registerUserProfileRoute(server);

registerYamlResolverRoute(server);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import {
IDevWorkspaceApi,
IDevWorkspaceTemplateApi,
IDockerConfigApi,
IServerConfigApi,
IKubeConfigApi,
INamespaceApi,
IServerConfigApi,
IUserProfileApi,
} from '../types';

export class DevWorkspaceClient implements IDevWorkspaceClient {
Expand All @@ -39,4 +40,7 @@ export class DevWorkspaceClient implements IDevWorkspaceClient {
get namespaceApi(): INamespaceApi {
throw new Error('Method not implemented.');
}
get userProfileApi(): IUserProfileApi {
throw new Error('Method not implemented.');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { DockerConfigApiService } from '../services/dockerConfigApi';
import { KubeConfigApiService } from '../services/kubeConfigApi';
import { NamespaceApiService } from '../services/namespaceApi';
import { ServerConfigApiService } from '../services/serverConfigApi';
import { UserProfileApiService } from '../services/userProfileApi';

jest.mock('../services/devWorkspaceApi.ts');

Expand All @@ -43,5 +44,6 @@ describe('DevWorkspace client', () => {
expect(client.namespaceApi).toBeInstanceOf(NamespaceApiService);
expect(client.serverConfigApi).toBeInstanceOf(ServerConfigApiService);
expect(client.devWorkspaceTemplateApi).toBeInstanceOf(DevWorkspaceTemplateApiService);
expect(client.userProfileApi).toBeInstanceOf(UserProfileApiService);
});
});
50 changes: 21 additions & 29 deletions packages/dashboard-backend/src/devworkspaceClient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,66 +11,58 @@
*/

import * as k8s from '@kubernetes/client-node';
import { DevWorkspaceApiService } from './services/devWorkspaceApi';
import { DevWorkspaceTemplateApiService } from './services/devWorkspaceTemplateApi';
import { DockerConfigApiService } from './services/dockerConfigApi';
import { KubeConfigApiService } from './services/kubeConfigApi';
import { NamespaceApiService } from './services/namespaceApi';
import { ServerConfigApiService } from './services/serverConfigApi';
import { UserProfileApiService } from './services/userProfileApi';
import {
IServerConfigApi,
IDevWorkspaceApi,
IDevWorkspaceClient,
IDevWorkspaceTemplateApi,
IDockerConfigApi,
IKubeConfigApi,
INamespaceApi,
IServerConfigApi,
IUserProfileApi,
} from './types';
import { DevWorkspaceTemplateApiService } from './services/devWorkspaceTemplateApi';
import { DevWorkspaceApiService } from './services/devWorkspaceApi';
import { DockerConfigApiService } from './services/dockerConfigApi';
import { ServerConfigApiService } from './services/serverConfigApi';
import { KubeConfigApiService } from './services/kubeConfigApi';
import { NamespaceApiService } from './services/namespaceApi';

export * from './types';

export class DevWorkspaceClient implements IDevWorkspaceClient {
private apiEnabled: boolean | undefined;

private readonly _apisApi: k8s.ApisApi;
private readonly _devWorkspaceTemplateApi: IDevWorkspaceTemplateApi;
private readonly _devworkspaceApi: IDevWorkspaceApi;
private readonly _dockerConfigApi: IDockerConfigApi;
private readonly _serverConfigApi: IServerConfigApi;
private readonly _kubeConfigApi: IKubeConfigApi;
private readonly _namespaceApi: INamespaceApi;
private readonly kubeConfig: k8s.KubeConfig;

constructor(kc: k8s.KubeConfig) {
this._devWorkspaceTemplateApi = new DevWorkspaceTemplateApiService(kc);
this._devworkspaceApi = new DevWorkspaceApiService(kc);
this._dockerConfigApi = new DockerConfigApiService(kc);
this._serverConfigApi = new ServerConfigApiService(kc);
this._kubeConfigApi = new KubeConfigApiService(kc);
this._namespaceApi = new NamespaceApiService(kc);
this._apisApi = kc.makeApiClient(k8s.ApisApi);
this.kubeConfig = kc;
}

get devWorkspaceTemplateApi(): IDevWorkspaceTemplateApi {
return this._devWorkspaceTemplateApi;
return new DevWorkspaceTemplateApiService(this.kubeConfig);
}

get devworkspaceApi(): IDevWorkspaceApi {
return this._devworkspaceApi;
return new DevWorkspaceApiService(this.kubeConfig);
}

get dockerConfigApi(): IDockerConfigApi {
return this._dockerConfigApi;
return new DockerConfigApiService(this.kubeConfig);
}

get serverConfigApi(): IServerConfigApi {
return this._serverConfigApi;
return new ServerConfigApiService(this.kubeConfig);
}

get kubeConfigApi(): IKubeConfigApi {
return this._kubeConfigApi;
return new KubeConfigApiService(this.kubeConfig);
}

get namespaceApi(): INamespaceApi {
return this._namespaceApi;
return new NamespaceApiService(this.kubeConfig);
}

get userProfileApi(): IUserProfileApi {
return new UserProfileApiService(this.kubeConfig);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2021 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

/* eslint-disable @typescript-eslint/no-unused-vars */

import * as mockClient from '@kubernetes/client-node';
import { CoreV1Api, V1Secret } from '@kubernetes/client-node';
import { UserProfileApiService } from '../userProfileApi';

jest.mock('../../../helpers/getUserName.ts');

const userNamespace = 'user1-che';

describe('UserProfile API Service', () => {
let userProfileService: UserProfileApiService;

beforeEach(() => {
const { KubeConfig } = mockClient;
const kubeConfig = new KubeConfig();

kubeConfig.makeApiClient = jest.fn().mockImplementation(_api => {
return {
readNamespacedSecret: (_name, _namespace) => {
return Promise.resolve(buildSecret());
},
} as CoreV1Api;
});

userProfileService = new UserProfileApiService(kubeConfig);
});

afterEach(() => {
jest.clearAllMocks();
});

test('user profile object', async () => {
const res = await userProfileService.getUserProfile(userNamespace);
expect(res).toEqual({ email: 'user1@che', username: 'user1' });
});
});

function buildSecret(): { body: V1Secret } {
return {
body: {
apiVersion: 'v1',
data: {
email: 'dXNlcjFAY2hl',
id: 'Q2dFeEVnVnNiMk5oYkE=',
name: 'dXNlcjE=',
},
kind: 'Secret',
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2021 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import { api } from '@eclipse-che/common';
import * as k8s from '@kubernetes/client-node';
import { IUserProfileApi } from '../types';
import { createError } from './helpers/createError';

const ERROR_LABEL = 'CORE_V1_API_ERROR';
const USER_PROFILE_SECRET_NAME = 'user-profile';

export class UserProfileApiService implements IUserProfileApi {
private readonly coreV1API: k8s.CoreV1Api;

constructor(kc: k8s.KubeConfig) {
this.coreV1API = kc.makeApiClient(k8s.CoreV1Api);
}

async getUserProfile(namespace: string): Promise<api.IUserProfile | undefined> {
try {
const result = await this.coreV1API.readNamespacedSecret(USER_PROFILE_SECRET_NAME, namespace);
const data = result.body.data;
if (data === undefined) {
throw new Error('Data is empty');
}
return {
username: Buffer.from(data.name, 'base64').toString(),
email: Buffer.from(data.email, 'base64').toString(),
};
} catch (e) {
console.error('Unable to get user profile data:', e);
throw createError(e, ERROR_LABEL, 'Unable to get user profile data');
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,13 @@ export interface IKubeConfigApi {
injectKubeConfig(namespace: string, devworkspaceId: string): Promise<void>;
}

export interface IUserProfileApi {
/**
* Returns user profile object that contains username and email.
*/
getUserProfile(namespace: string): Promise<api.IUserProfile | undefined>;
}

export type IDevWorkspaceCallbacks = {
onModified: (workspace: V1alpha2DevWorkspace) => void;
onDeleted: (workspaceId: string) => void;
Expand All @@ -215,6 +222,7 @@ export interface IDevWorkspaceClient {
serverConfigApi: IServerConfigApi;
kubeConfigApi: IKubeConfigApi;
namespaceApi: INamespaceApi;
userProfileApi: IUserProfileApi;
}

export interface IDevWorkspaceList {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2021 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import { FastifyInstance } from 'fastify';
import { baseApiPath } from '../../../constants/config';
import { setup, teardown } from '../../../helpers/tests/appBuilder';
import { stubUserProfile } from '../helpers/__mocks__/getDevWorkspaceClient';

jest.mock('../helpers/getDevWorkspaceClient.ts');
jest.mock('../helpers/getToken.ts');
jest.mock('../helpers/getServiceAccountToken.ts');

describe('UserProfile Route', () => {
let app: FastifyInstance;
const namespace = 'user-che';

beforeEach(async () => {
app = await setup();
});

afterEach(() => {
teardown(app);
});

test('GET ${baseApiPath}/namespaces', async () => {
const res = await app.inject().get(`${baseApiPath}/userprofile/${namespace}`);

expect(res.statusCode).toEqual(200);
expect(res.json()).toEqual(stubUserProfile);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ export const stubDockerConfig = {};

export const stubNamespaces = ['user-che'];

export const stubUserProfile: api.IUserProfile = {
email: 'user1@che',
username: 'user1',
};

export function getDevWorkspaceClient(_args: Parameters<typeof helper>): ReturnType<typeof helper> {
return {
serverConfigApi: {
Expand Down Expand Up @@ -108,5 +113,8 @@ export function getDevWorkspaceClient(_args: Parameters<typeof helper>): ReturnT
listInNamespace: _namespace => Promise.resolve(stubDevWorkspaceTemplatesList),
patch: (_namespace, _name, _patches) => Promise.resolve(stubDevWorkspaceTemplate),
} as IDevWorkspaceTemplateApi,
userProfileApi: {
getUserProfile: _namespace => Promise.resolve(stubUserProfile),
},
} as DevWorkspaceClient;
}
34 changes: 34 additions & 0 deletions packages/dashboard-backend/src/routes/api/userProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2018-2021 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import { FastifyInstance, FastifyRequest } from 'fastify';
import { baseApiPath } from '../../constants/config';
import { namespacedSchema } from '../../constants/schemas';
import { getSchema } from '../../services/helpers';
import { restParams } from '../../typings/models';
import { getDevWorkspaceClient } from './helpers/getDevWorkspaceClient';
import { getToken } from './helpers/getToken';

const tags = ['UserProfile'];

export function registerUserProfileRoute(server: FastifyInstance) {
server.get(
`${baseApiPath}/userprofile/:namespace`,
getSchema({ tags, params: namespacedSchema }),
async function (request: FastifyRequest) {
const { namespace } = request.params as restParams.INamespacedParam;
const token = getToken(request);
const { userProfileApi } = getDevWorkspaceClient(token);
return userProfileApi.getUserProfile(namespace);
},
);
}
Loading