Skip to content

Commit 843dca5

Browse files
Implement manage repositories workflow in serverless view (#3126)
* manage repo initial commit Signed-off-by: msivasubramaniaan <msivasub@redhat.com> * add,edit and delete functionalites were added Signed-off-by: msivasubramaniaan <msivasub@redhat.com> * added telemetery Signed-off-by: msivasubramaniaan <msivasub@redhat.com> * added telemetery Signed-off-by: msivasubramaniaan <msivasub@redhat.com> * refactor the method Signed-off-by: msivasubramaniaan <msivasub@redhat.com> * Update src/webview/manage-repository/app/addRepository.tsx Co-authored-by: Mohit Suman <mohit.skn@gmail.com> * changed the entire layout Signed-off-by: msivasubramaniaan <msivasub@redhat.com> * addressed review comments Signed-off-by: msivasubramaniaan <msivasub@redhat.com> * removed loading button Signed-off-by: msivasubramaniaan <msivasub@redhat.com> * addressed review comments Signed-off-by: msivasubramaniaan <msivasub@redhat.com> * desc changed Signed-off-by: msivasubramaniaan <msivasub@redhat.com> --------- Signed-off-by: msivasubramaniaan <msivasub@redhat.com> Co-authored-by: Mohit Suman <mohit.skn@gmail.com>
1 parent fbbe4f6 commit 843dca5

File tree

22 files changed

+1281
-94
lines changed

22 files changed

+1281
-94
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ src/webview/welcome
1818
src/webview/helm-chart
1919
src/webview/feedback
2020
src/webview/serverless-function
21+
src/webview/serverless-manage-repository
2122
# vendored from https://github.com/IonicaBizau/git-url-parse, see https://github.com/IonicaBizau/git-url-parse/pull/159
2223
src/util/gitParse.ts
2324
test/sandbox-registration

build/esbuild.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const webviews = [
1919
'welcome',
2020
'feedback',
2121
'serverless-function',
22+
'serverless-manage-repository',
2223
'add-service-binding',
2324
];
2425

package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,12 @@
784784
"category": "OpenShift",
785785
"icon": "$(plus)"
786786
},
787+
{
788+
"command": "openshift.Serverless.manageRepository",
789+
"title": "Manage Knative Function Template Repositories",
790+
"category": "OpenShift",
791+
"icon": "$(gist)"
792+
},
787793
{
788794
"command": "openshift.Serverless.refresh",
789795
"title": "Refresh Serverless Function View",
@@ -1299,6 +1305,11 @@
12991305
"when": "view == openshiftServerlessFunctionsView",
13001306
"group": "navigation@1"
13011307
},
1308+
{
1309+
"command": "openshift.Serverless.manageRepository",
1310+
"when": "view == openshiftServerlessFunctionsView",
1311+
"group": "navigation@2"
1312+
},
13021313
{
13031314
"command": "openshift.component.openCreateComponent",
13041315
"when": "view == openshiftComponentsView",

src/serverlessFunction/commands.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,19 +117,41 @@ export class ServerlessCommand {
117117
}
118118

119119
static config(functionPath: string, mode: string, isAdd: boolean): CommandText {
120+
const option = isAdd ? mode === 'git' ? 'set' : 'add' : 'remove';
120121
const commandText = new CommandText('func', 'config', [
121122
new CommandOption(mode),
123+
new CommandOption(option),
122124
new CommandOption('-p', functionPath)
123125
]);
124-
if (isAdd) {
125-
if (mode === 'git') {
126-
commandText.addOption(new CommandOption('set'));
127-
} else {
128-
commandText.addOption(new CommandOption('add'));
129-
}
130-
} else {
131-
commandText.addOption(new CommandOption('remove'));
132-
}
126+
return commandText;
127+
}
128+
129+
static addRepo(name: string, gitURL: string): CommandText {
130+
const commandText = new CommandText('func', 'repository');
131+
commandText.addOption(new CommandOption('add'));
132+
commandText.addOption(new CommandOption(name));
133+
commandText.addOption(new CommandOption(gitURL));
134+
return commandText;
135+
}
136+
137+
static deleteRepo(name: string): CommandText {
138+
const commandText = new CommandText('func', 'repository');
139+
commandText.addOption(new CommandOption('remove'));
140+
commandText.addOption(new CommandOption(name));
141+
return commandText;
142+
}
143+
144+
static list(): CommandText {
145+
const commandText = new CommandText('func', 'repository');
146+
commandText.addOption(new CommandOption('list'));
147+
return commandText;
148+
}
149+
150+
static renameRepo(oldName: string, newName: string): CommandText {
151+
const commandText = new CommandText('func', 'repository');
152+
commandText.addOption(new CommandOption('rename'));
153+
commandText.addOption(new CommandOption(oldName));
154+
commandText.addOption(new CommandOption(newName));
133155
return commandText;
134156
}
135157
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*-----------------------------------------------------------------------------------------------
2+
* Copyright (c) Red Hat, Inc. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE file in the project root for license information.
4+
*-----------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
import { OdoImpl } from '../odo';
8+
import sendTelemetry from '../telemetry';
9+
import { ServerlessCommand } from './commands';
10+
11+
export class ManageRepository {
12+
13+
private static instance: ManageRepository;
14+
15+
static getInstance(): ManageRepository {
16+
if (!ManageRepository.instance) {
17+
ManageRepository.instance = new ManageRepository();
18+
}
19+
return ManageRepository.instance;
20+
}
21+
22+
public async deleteRepo(name: string): Promise<boolean> {
23+
await sendTelemetry('openshift.managerepo.delete', {
24+
name
25+
});
26+
const result = await OdoImpl.Instance.execute(ServerlessCommand.deleteRepo(name), '', false);
27+
if (result.error) {
28+
await sendTelemetry('openshift.managerepo.delete.error', {
29+
error: result.error.message
30+
});
31+
void vscode.window.showErrorMessage(result.error.message);
32+
return false;
33+
}
34+
return true;
35+
}
36+
37+
public async renameRepo(oldName: string, newName: string): Promise<boolean> {
38+
await sendTelemetry('openshift.managerepo.rename', {
39+
oldName,
40+
newName
41+
});
42+
const result = await OdoImpl.Instance.execute(ServerlessCommand.renameRepo(oldName, newName), '', false);
43+
if (result.error) {
44+
await sendTelemetry('openshift.managerepo.rename.error', {
45+
error: result.error.message
46+
});
47+
void vscode.window.showErrorMessage(result.error.message);
48+
return false;
49+
}
50+
await sendTelemetry('openshift.managerepo.rename.success', {
51+
message: `Repo ${newName} renamed successfully`
52+
});
53+
return true;
54+
}
55+
56+
public async addRepo(name: string, url: string): Promise<boolean> {
57+
await sendTelemetry('openshift.managerepo.add', {
58+
name, url
59+
});
60+
const result = await OdoImpl.Instance.execute(ServerlessCommand.addRepo(name, url), '', false);
61+
if (result.error) {
62+
await sendTelemetry('openshift.managerepo.add.error', {
63+
error: result.error.message
64+
});
65+
void vscode.window.showErrorMessage(result.error.message);
66+
return false;
67+
} else if (result.stdout.length === 0 && result.stderr.length === 0) {
68+
await sendTelemetry('openshift.managerepo.add.success', {
69+
name,
70+
message: 'Repo added successfully'
71+
});
72+
void vscode.window.showInformationMessage(`Repository ${name} added successfully`);
73+
return true;
74+
}
75+
await sendTelemetry('openshift.managerepo.add.error', {
76+
error: result.stderr
77+
});
78+
return false;
79+
}
80+
81+
public async list(): Promise<string[]> {
82+
await sendTelemetry('openshift.managerepo.list');
83+
const result = await OdoImpl.Instance.execute(ServerlessCommand.list(), '', false);
84+
if (result.error) {
85+
await sendTelemetry('openshift.managerepo.list.error', {
86+
error: result.error.message
87+
});
88+
void vscode.window.showErrorMessage(result.error.message);
89+
return [];
90+
}
91+
await sendTelemetry('openshift.managerepo.list.success', {
92+
repos: result.stdout.split('\n')
93+
});
94+
return result.stdout.split('\n');
95+
}
96+
}

src/serverlessFunction/view.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { FunctionContextType, FunctionObject, FunctionStatus } from './types';
2929
import ServerlessFunctionViewLoader from '../webview/serverless-function/serverlessFunctionLoader';
3030
import { Functions } from './functions';
3131
import { vsCommand } from '../vscommand';
32+
import ManageRepositoryViewLoader from '../webview/serverless-manage-repository/manageRepositoryLoader';
3233

3334
const kubeConfigFolder: string = path.join(Platform.getUserHomePath(), '.kube');
3435

@@ -203,6 +204,11 @@ export class ServerlessFunctionView implements TreeDataProvider<ExplorerItem>, D
203204
await ServerlessFunctionViewLoader.loadView('Serverless Function - Create');
204205
}
205206

207+
@vsCommand('openshift.Serverless.manageRepository')
208+
static async openManageRepository(): Promise<void> {
209+
await ManageRepositoryViewLoader.loadView('Manage Repository');
210+
}
211+
206212
@vsCommand('openshift.Serverless.refresh')
207213
static refresh(target?: ExplorerItem) {
208214
ServerlessFunctionView.getInstance().refresh(target);

src/webview/common-ext/utils.ts

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@
66
import * as fs from 'fs/promises';
77
import * as path from 'path';
88
import { Uri, WebviewPanel, extensions } from 'vscode';
9+
import OpenShiftItem from '../../openshift/openshiftItem';
910
import { ExtensionID } from '../../util/constants';
11+
import { gitUrlParse } from '../../util/gitParse';
12+
import { validateGitURLProps } from '../common/propertyTypes';
13+
14+
export type Message = {
15+
action: string;
16+
data: any;
17+
};
1018

1119
export async function loadWebviewHtml(webviewName: string, webviewPanel: WebviewPanel, additionalInjections?: Map<string, string>): Promise<string> {
1220

@@ -28,7 +36,7 @@ export async function loadWebviewHtml(webviewName: string, webviewPanel: Webview
2836
style-src 'self' vscode-resource: 'unsafe-inline';">`;
2937
const htmlWithDefaultInjections = `${htmlString}`
3038
.replace('%PLATFORM%', process.platform)
31-
.replace('%SCRIPT%',`${reactJavascriptUri}`)
39+
.replace('%SCRIPT%', `${reactJavascriptUri}`)
3240
.replace('%BASE_URL%', `${reactJavascriptUri}`)
3341
.replace('%STYLE%', `${reactStylesheetUri}`)
3442
.replace('<!-- meta http-equiv="Content-Security-Policy" -->', meta);
@@ -41,3 +49,62 @@ export async function loadWebviewHtml(webviewName: string, webviewPanel: Webview
4149
}
4250
return htmlWithAdditionalInjections;
4351
}
52+
53+
function isGitURL(host: string): boolean {
54+
return [
55+
'github.com',
56+
'bitbucket.org',
57+
'gitlab.com',
58+
'git.sr.ht',
59+
'codeberg.org',
60+
'gitea.com',
61+
].includes(host);
62+
}
63+
64+
export function validateGitURL(event: Message): validateGitURLProps {
65+
if (typeof event.data === 'string' && (event.data).trim().length === 0) {
66+
return {
67+
url: event.data,
68+
error: true,
69+
helpText: 'Please enter a Git URL.'
70+
} as validateGitURLProps
71+
}
72+
try {
73+
const parse = gitUrlParse(event.data);
74+
const isGitRepo = isGitURL(parse.host);
75+
if (!isGitRepo) {
76+
throw 'Invalid Git URL';
77+
}
78+
if (parse.organization !== '' && parse.name !== '') {
79+
return {
80+
url: event.data,
81+
error: false,
82+
helpText: 'The git repo URL is valid.'
83+
} as validateGitURLProps
84+
}
85+
return {
86+
url: event.data,
87+
error: true,
88+
helpText: 'URL is missing organization or repo name.'
89+
} as validateGitURLProps
90+
91+
} catch (e) {
92+
return {
93+
url: event.data,
94+
error: true,
95+
helpText: 'Invalid Git URL.'
96+
} as validateGitURLProps
97+
}
98+
}
99+
100+
export function validateName(value: string): string | null {
101+
let validationMessage = OpenShiftItem.emptyName('Required', value.trim());
102+
if (!validationMessage) {
103+
validationMessage = OpenShiftItem.validateMatches(
104+
'Only lower case alphabets and numeric characters or \'-\', start and ends with only alphabets',
105+
value,
106+
);
107+
}
108+
if (!validationMessage) { validationMessage = OpenShiftItem.lengthName('Should be between 2-63 characters', value, 0); }
109+
return validationMessage;
110+
}

src/webview/common/propertyTypes.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
* Copyright (c) Red Hat, Inc. All rights reserved.
33
* Licensed under the MIT License. See LICENSE file in the project root for license information.
44
*-----------------------------------------------------------------------------------------------*/
5-
6-
import { ChangeEvent } from 'react';
75
import { ComponentTypeDescription, Registry } from '../../odo/componentType';
86
import { StarterProject } from '../../odo/componentTypeDescription';
97
import { ChartResponse } from '../helm-chart/helmChartType';
@@ -84,3 +82,9 @@ export interface RunFunctionPageProps extends DefaultProps {
8482
skip: (stepCount: number) => void;
8583
onRunSubmit: (folderPath: Uri, build: boolean) => void;
8684
}
85+
86+
export interface validateGitURLProps {
87+
url: string;
88+
error: boolean;
89+
helpText: string;
90+
}

src/webview/common/utils.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)