From 8abcbafb135a74aa9ac350191354a4d9f742f519 Mon Sep 17 00:00:00 2001 From: Maxime Dufour Date: Mon, 10 Jul 2023 16:56:39 +0000 Subject: [PATCH 1/6] Add VMGroup and VmTemplate --- src/cloud/vmgroup.ts | 101 ++++++++++++++++++ src/cloud/vmtemplate.ts | 64 +++++++++++ .../folders/simple/node.folder.vmgroup.ts | 45 ++++++++ .../folders/simple/node.folder.vmtemplate.ts | 45 ++++++++ src/flat/node.profile.ts | 4 + src/flat/node.ts | 4 +- src/ui-test/treeview.ts | 4 +- src/virtual_filesystem/oscvirtualfs.ts | 6 +- 8 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 src/cloud/vmgroup.ts create mode 100644 src/cloud/vmtemplate.ts create mode 100644 src/flat/folders/simple/node.folder.vmgroup.ts create mode 100644 src/flat/folders/simple/node.folder.vmtemplate.ts diff --git a/src/cloud/vmgroup.ts b/src/cloud/vmgroup.ts new file mode 100644 index 0000000..29323f2 --- /dev/null +++ b/src/cloud/vmgroup.ts @@ -0,0 +1,101 @@ + +import * as osc from "outscale-api"; +import { getConfig, handleRejection } from '../cloud/cloud'; +import { Profile } from "../flat/node"; + + +export function getVmGroups(profile: Profile, filters?: osc.FiltersVmGroup): Promise | string> { + const config = getConfig(profile); + const readParameters: osc.ReadVmGroupsOperationRequest = { + readVmGroupsRequest: { + filters: filters + } + }; + + const api = new osc.VmGroupApi(config); + return api.readVmGroups(readParameters) + .then((res: osc.ReadVmGroupsResponse) => { + if (res.vmGroups === undefined || res.vmGroups.length === 0) { + return []; + } + return res.vmGroups; + }, (err_: any) => { + return handleRejection(err_); + }); +} + +export function getVmGroup(profile: Profile, vmGroupId: string): Promise { + const config = getConfig(profile); + const readParameters: osc.ReadVmGroupsOperationRequest = { + readVmGroupsRequest: { + filters: { + vmGroupIds: [vmGroupId] + } + } + }; + + const api = new osc.VmGroupApi(config); + return api.readVmGroups(readParameters) + .then((res: osc.ReadVmGroupsResponse) => { + if (res.vmGroups === undefined || res.vmGroups.length === 0) { + return undefined; + } + return res.vmGroups[0]; + }, (err_: any) => { + return handleRejection(err_); + }); +} + +export function deleteVmGroup(profile: Profile, resourceId: string): Promise { + const config = getConfig(profile); + const deleteParameters: osc.DeleteVmGroupOperationRequest = { + deleteVmGroupRequest: { + vmGroupId: resourceId + } + }; + + const api = new osc.VmGroupApi(config); + return api.deleteVmGroup(deleteParameters) + .then(() => { + return undefined; + }, (err_: any) => { + return handleRejection(err_); + }); +} + + +export function scaleUp(profile: Profile, resourceId: string, addition: number): Promise { + const config = getConfig(profile); + const parameters: osc.ScaleUpVmGroupOperationRequest = { + scaleUpVmGroupRequest: { + vmGroupId: resourceId, + vmAddition: addition + } + }; + + const api = new osc.VmGroupApi(config); + return api.scaleUpVmGroup(parameters) + .then(() => { + return undefined; + }, (err_: any) => { + return handleRejection(err_); + }); +} + +export function scaleDown(profile: Profile, resourceId: string, substraction: number): Promise { + const config = getConfig(profile); + const parameters: osc.ScaleDownVmGroupOperationRequest = { + scaleDownVmGroupRequest: { + vmGroupId: resourceId, + vmSubtraction: substraction + } + }; + + const api = new osc.VmGroupApi(config); + return api.scaleDownVmGroup(parameters) + .then(() => { + return undefined; + }, (err_: any) => { + return handleRejection(err_); + }); +} \ No newline at end of file diff --git a/src/cloud/vmtemplate.ts b/src/cloud/vmtemplate.ts new file mode 100644 index 0000000..73a9444 --- /dev/null +++ b/src/cloud/vmtemplate.ts @@ -0,0 +1,64 @@ + +import * as osc from "outscale-api"; +import { getConfig, handleRejection } from './cloud'; +import { Profile } from "../flat/node"; + + +export function getVmTemplates(profile: Profile, filters?: osc.FiltersVmTemplate): Promise | string> { + const config = getConfig(profile); + const readParameters: osc.ReadVmTemplatesOperationRequest = { + readVmTemplatesRequest: { + filters: filters + } + }; + + const api = new osc.VmTemplateApi(config); + return api.readVmTemplates(readParameters) + .then((res: osc.ReadVmTemplatesResponse) => { + if (res.vmTemplates === undefined || res.vmTemplates.length === 0) { + return []; + } + return res.vmTemplates; + }, (err_: any) => { + return handleRejection(err_); + }); +} + +export function getVmTemplate(profile: Profile, resourceId: string): Promise { + const config = getConfig(profile); + const readParameters: osc.ReadVmTemplatesOperationRequest = { + readVmTemplatesRequest: { + filters: { + vmTemplateIds: [resourceId] + } + } + }; + + const api = new osc.VmTemplateApi(config); + return api.readVmTemplates(readParameters) + .then((res: osc.ReadVmTemplatesResponse) => { + if (res.vmTemplates === undefined || res.vmTemplates.length === 0) { + return undefined; + } + return res.vmTemplates[0]; + }, (err_: any) => { + return handleRejection(err_); + }); +} + +export function deleteVmTemplate(profile: Profile, resourceId: string): Promise { + const config = getConfig(profile); + const deleteParameters: osc.DeleteVmTemplateOperationRequest = { + deleteVmTemplateRequest: { + vmTemplateId: resourceId + } + }; + + const api = new osc.VmTemplateApi(config); + return api.deleteVmTemplate(deleteParameters) + .then(() => { + return undefined; + }, (err_: any) => { + return handleRejection(err_); + }); +} \ No newline at end of file diff --git a/src/flat/folders/simple/node.folder.vmgroup.ts b/src/flat/folders/simple/node.folder.vmgroup.ts new file mode 100644 index 0000000..5eb6131 --- /dev/null +++ b/src/flat/folders/simple/node.folder.vmgroup.ts @@ -0,0 +1,45 @@ +import * as vscode from 'vscode'; +import { ExplorerNode, ExplorerFolderNode, Profile, resourceNodeCompare } from '../../node'; +import { FiltersFolderNode } from '../node.filterfolder'; +import { ResourceNode } from '../../resources/node.resources'; +import { FiltersVmGroup, FiltersVmGroupFromJSON } from 'outscale-api'; +import { deleteVmGroup, getVmGroup, getVmGroups } from '../../../cloud/vmgroup'; + +export const VMGROUPS_FOLDER_NAME = "Vm Groups"; +export class VmGroupsFolderNode extends FiltersFolderNode implements ExplorerFolderNode { + constructor(readonly profile: Profile) { + super(profile, VMGROUPS_FOLDER_NAME); + } + + getChildren(): Thenable { + this.updateFilters(); + return getVmGroups(this.profile, this.filters).then(results => { + if (typeof results === "string") { + vscode.window.showErrorMessage(vscode.l10n.t(`Error while reading {0}: {1}`, this.folderName, results)); + return Promise.resolve([]); + } + const resources = []; + for (const item of results) { + + if (typeof item.vmGroupId === 'undefined') { + + continue; + } + + if (typeof item.vmGroupName === 'undefined') { + + continue; + } + + resources.push(new ResourceNode(this.profile, item.vmGroupName, item.vmGroupId, "VmGroup", deleteVmGroup, getVmGroup)); + + } + return Promise.resolve(resources.sort(resourceNodeCompare)); + }); + + } + + filtersFromJson(json: string): FiltersVmGroup { + return FiltersVmGroupFromJSON(json); + } +} \ No newline at end of file diff --git a/src/flat/folders/simple/node.folder.vmtemplate.ts b/src/flat/folders/simple/node.folder.vmtemplate.ts new file mode 100644 index 0000000..09122d3 --- /dev/null +++ b/src/flat/folders/simple/node.folder.vmtemplate.ts @@ -0,0 +1,45 @@ +import * as vscode from 'vscode'; +import { ExplorerNode, ExplorerFolderNode, Profile, resourceNodeCompare } from '../../node'; +import { FiltersFolderNode } from '../node.filterfolder'; +import { ResourceNode } from '../../resources/node.resources'; +import { FiltersVmTemplate, FiltersVmTemplateFromJSON } from 'outscale-api'; +import { deleteVmTemplate, getVmTemplate, getVmTemplates } from '../../../cloud/vmtemplate'; + +export const VMTEMPLATES_FOLDER_NAME = "Vm Templates"; +export class VmTemplatesFolderNode extends FiltersFolderNode implements ExplorerFolderNode { + constructor(readonly profile: Profile) { + super(profile, VMTEMPLATES_FOLDER_NAME); + } + + getChildren(): Thenable { + this.updateFilters(); + return getVmTemplates(this.profile, this.filters).then(results => { + if (typeof results === "string") { + vscode.window.showErrorMessage(vscode.l10n.t(`Error while reading {0}: {1}`, this.folderName, results)); + return Promise.resolve([]); + } + const resources = []; + for (const item of results) { + + if (typeof item.vmTemplateId === 'undefined') { + + continue; + } + + if (typeof item.vmTemplateName === 'undefined') { + + continue; + } + + resources.push(new ResourceNode(this.profile, item.vmTemplateName, item.vmTemplateId, "VmTemplate", deleteVmTemplate, getVmTemplate)); + + } + return Promise.resolve(resources.sort(resourceNodeCompare)); + }); + + } + + filtersFromJson(json: string): FiltersVmTemplate { + return FiltersVmTemplateFromJSON(json); + } +} \ No newline at end of file diff --git a/src/flat/node.profile.ts b/src/flat/node.profile.ts index f391dc3..825e05e 100644 --- a/src/flat/node.profile.ts +++ b/src/flat/node.profile.ts @@ -28,6 +28,8 @@ import { SubnetsFolderNode, SUBNETS_FOLDER_NAME } from './folders/simple/node.fo import { VirtualGatewaysFolderNode, VIRTUALGATEWAYS_FOLDER_NAME } from './folders/specific/node.folder.virtualgateway'; import { VpnConnectionsFolderNode, VPNCONNECTIONS_FOLDER_NAME } from './folders/simple/node.folder.vpnconnection'; import { DISABLE_FOLDER_PARAMETER, getConfigurationParameter } from '../configuration/utils'; +import { VMGROUPS_FOLDER_NAME, VmGroupsFolderNode } from './folders/simple/node.folder.vmgroup'; +import { VMTEMPLATES_FOLDER_NAME, VmTemplatesFolderNode } from './folders/simple/node.folder.vmtemplate'; export class ProfileNode implements ExplorerProfileNode { @@ -70,6 +72,8 @@ export class ProfileNode implements ExplorerProfileNode { [VM_FOLDER_NAME, new VmsFolderNode(this.profile)], [VPNCONNECTIONS_FOLDER_NAME, new VpnConnectionsFolderNode(this.profile)], [VOLUME_FOLDER_NAME, new VolumeFolderNode(this.profile)], + [VMGROUPS_FOLDER_NAME, new VmGroupsFolderNode(this.profile)], + [VMTEMPLATES_FOLDER_NAME, new VmTemplatesFolderNode(this.profile)], ]; let disableFolder = getConfigurationParameter>(DISABLE_FOLDER_PARAMETER); diff --git a/src/flat/node.ts b/src/flat/node.ts index bd53bee..0ddd7f2 100644 --- a/src/flat/node.ts +++ b/src/flat/node.ts @@ -55,7 +55,9 @@ export type ResourceNodeType = "eips" | "omis" | "snapshots" | - "routetables"; + "routetables" | + "VmTemplate" | + "VmGroup"; export class NodeImpl { } diff --git a/src/ui-test/treeview.ts b/src/ui-test/treeview.ts index 0304c63..e41aae2 100644 --- a/src/ui-test/treeview.ts +++ b/src/ui-test/treeview.ts @@ -37,7 +37,9 @@ const resourceTypes = [ "Subnets", "Virtual Gateways", "Vms", - "Vpn Connections" + "Vpn Connections", + "Vm Groups", + "Vm Templates" ]; const profile: any = { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/src/virtual_filesystem/oscvirtualfs.ts b/src/virtual_filesystem/oscvirtualfs.ts index 7227cc8..63b0be5 100644 --- a/src/virtual_filesystem/oscvirtualfs.ts +++ b/src/virtual_filesystem/oscvirtualfs.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; -import { AccessKeyToJSON, AccountToJSON, ApiAccessRuleToJSON, CaToJSON, ClientGatewayToJSON, DhcpOptionsSetToJSON, DirectLinkInterfaceToJSON, DirectLinkToJSON, FlexibleGpuToJSON, ImageToJSON, InternetServiceToJSON, KeypairToJSON, LoadBalancerToJSON, NatServiceToJSON, NetAccessPointToJSON, NetPeeringToJSON, NetToJSON, NicToJSON, PublicIpToJSON, RouteTableToJSON, SecurityGroupToJSON, SnapshotToJSON, SubnetToJSON, VirtualGatewayToJSON, VmToJSON, VolumeToJSON, VpnConnectionToJSON } from "outscale-api"; +import { AccessKeyToJSON, AccountToJSON, ApiAccessRuleToJSON, CaToJSON, ClientGatewayToJSON, DhcpOptionsSetToJSON, DirectLinkInterfaceToJSON, DirectLinkToJSON, FlexibleGpuToJSON, ImageToJSON, InternetServiceToJSON, KeypairToJSON, LoadBalancerToJSON, NatServiceToJSON, NetAccessPointToJSON, NetPeeringToJSON, NetToJSON, NicToJSON, PublicIpToJSON, RouteTableToJSON, SecurityGroupToJSON, SnapshotToJSON, SubnetToJSON, VirtualGatewayToJSON, VmGroupFromJSON, VmGroupToJSON, VmTemplateToJSON, VmToJSON, VolumeToJSON, VpnConnectionToJSON } from "outscale-api"; import { getExternalIP } from "../cloud/publicips"; import { getKeypair } from "../cloud/keypairs"; import { getLoadBalancer } from "../cloud/loadbalancers"; @@ -30,6 +30,8 @@ import { getNic } from '../cloud/nics'; import { getSubnet } from '../cloud/subnets'; import { getVirtualGateway } from '../cloud/virtualgateways'; import { getVpnConnection } from '../cloud/vpnconnections'; +import { getVmGroup } from '../cloud/vmgroup'; +import { getVmTemplate } from '../cloud/vmtemplate'; class ResourceEncoding { @@ -67,6 +69,8 @@ const resourceMap = new Map([ ["Subnet", new ResourceEncoding(getSubnet, SubnetToJSON)], ["VirtualGateway", new ResourceEncoding(getVirtualGateway, VirtualGatewayToJSON)], ["VpnConnection", new ResourceEncoding(getVpnConnection, VpnConnectionToJSON)], + ["VmGroup", new ResourceEncoding(getVmGroup, VmGroupToJSON)], + ["VmTemplate", new ResourceEncoding(getVmTemplate, VmTemplateToJSON)], ]); export class OscVirtualContentProvider implements vscode.TextDocumentContentProvider { From 6cacd65ea0a849060f7a66465a616e953dfb3a5e Mon Sep 17 00:00:00 2001 From: Maxime Dufour Date: Mon, 16 Oct 2023 10:53:34 +0000 Subject: [PATCH 2/6] Implement link on Resource description Add link when reading resources --- src/extension.ts | 4 ++ src/virtual_filesystem/osclinkprovider.ts | 76 +++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/virtual_filesystem/osclinkprovider.ts diff --git a/src/extension.ts b/src/extension.ts index f5f78a0..2674914 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -18,6 +18,7 @@ import { LinkResourceNode } from './flat/resources/types/node.resources.link'; import { SubResourceNode } from './flat/resources/types/node.resources.subresource'; import { NetResourceNode } from './flat/resources/node.resources.nets'; import { init } from './network/networkview'; +import { OscLinkProvider } from './virtual_filesystem/osclinkprovider'; function getMultipleSelection(mainSelectedItem: T, allSelectedItems?: any[]): T[] { if (typeof allSelectedItems === 'undefined') { @@ -69,6 +70,9 @@ export function activate(context: vscode.ExtensionContext) { const oscProvider = new OscVirtualContentProvider(); context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(myScheme, oscProvider)); + const oscLinkProvider = new OscLinkProvider(); + context.subscriptions.push(vscode.languages.registerDocumentLinkProvider({ scheme: myScheme }, oscLinkProvider)); + vscode.commands.registerCommand('osc.refreshResourceData', async (arg: any) => { oscProvider.onDidChangeEmitter.fire(arg); vscode.window.showInformationMessage(vscode.l10n.t(`Refreshed`)); diff --git a/src/virtual_filesystem/osclinkprovider.ts b/src/virtual_filesystem/osclinkprovider.ts new file mode 100644 index 0000000..15a45a0 --- /dev/null +++ b/src/virtual_filesystem/osclinkprovider.ts @@ -0,0 +1,76 @@ +import * as vscode from 'vscode'; +import { ResourceNodeType } from '../flat/node'; + +const resourceToRegexp: Map = new Map([ + //["AccessKey", "[A-Z0-9]{20}"], + ["ApiAccessRule", ["aar-", "aar-[a-f0-9]{32}"]], + ["Ca", ["ca-", "ca-[a-f0-9]{32}"]], + ["ClientGateway", ["cgw-", "cgw-[a-f0-9]{8}"]], + ["DhcpOption", ["dopt-", "dopt-[a-f0-9]{8}"]], + ["DirectLink", ["dxcon-", "dxcon-[a-f0-9]{8}"]], + ["DirectLinkInterface", ["dxvif-", "dxvif-[a-f0-9]{8}"]], + ["FlexibleGpu", ["fgpu-", "fgpu-[a-f0-9]{8}"]], + ["InternetService", ["igw-", "igw-[a-f0-9]{8}"]], + ["NatService", ["nat-", "nat-[a-f0-9]{8}"]], + ["NetAccessPoint", ["vpce-", "vpce-[a-f0-9]{8}"]], + ["NetPeering", ["pcx-", "pcx-[a-f0-9]{8}"]], + ["Nic", ["eni-", "eni-[a-f0-9]{8}"]], + ["Subnet", ["subnet-", "subnet-[a-f0-9]{8}"]], + ["VirtualGateway", ["vgw-", "vgw-[a-f0-9]{8}"]], + ["VpnConnection", ["vpn-", "vpn-[a-f0-9]{8}"]], + ["vms", ["i-", "i-[a-f0-9]{8}"]], + ["vpc", ["vpc-", "vpc-[a-f0-9]{8}"]], + ["securitygroups", ["sg-", "sg-[a-f0-9]{8}"]], + //["keypairs", ""], + ["volumes", ["vol-", "vol-[a-f0-9]{8}"]], + //["loadbalancers", ""], // Currently, the Id of Loadbalancer is names which is ... everything up to 32 chars, too many false negatives therefore we skip this + ["eips", ["eipalloc-", "eipalloc-[a-f0-9]{8}"]], + ["omis", ["ami-", "ami-[a-f0-9]{8}"]], + ["snapshots", ["snap-", "snap-[a-f0-9]{8}"]], + ["routetables", ["rtb-", "rtb-[a-f0-9]{8}"]], + ["VmTemplate", ["vmtemplate-", "vmtemplate-[a-f0-9]{32}"]], + ["VmGroup", ["vmgroup-", "vmgroup-[a-f0-9]{32}"]], +]); + +const globalResourcePrefix: string = Array.from(resourceToRegexp.values()).map(e => e[0]).join("|"); + +export class OscLinkProvider implements vscode.DocumentLinkProvider { + + + createDocumentLink(line: number, startIndex: number, resource: ResourceNodeType, value: string, uriPrefix: vscode.Uri): vscode.DocumentLink { + const startPosition = new vscode.Position(line, startIndex); // Start of the value index + const endPosition = new vscode.Position(line, startIndex + value.length); // End of value index + const range: vscode.Range = new vscode.Range(startPosition, endPosition); + const targetUri = vscode.Uri.joinPath(uriPrefix, resource + "/" + value + ".json"); // The trick is to change the resourcename and the resourceId + const link = new vscode.DocumentLink(range, targetUri); + return link; + + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + provideDocumentLinks(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.ProviderResult { + const links: Array = []; + const globalResourceRegexp = new RegExp(`"(${globalResourcePrefix}).+"`); + + for (let i = 0; i < document.lineCount; i++) { + const line = document.lineAt(i); + // This test try to see if there is a potential match to reduce multiple tests + if (!globalResourceRegexp.test(line.text)) { + continue; + } + + for (const resource of resourceToRegexp.entries()) { + const regex = new RegExp(`"(${resource[1][1]})"`); + const matches = regex.exec(line.text); + if (matches !== null) { + const matchStartIndex = 1 + matches.index; + const value = matches[1]; + const link = this.createDocumentLink(i, matchStartIndex, resource[0], value, vscode.Uri.joinPath(document.uri, "../../")); + links.push(link); + } + } + + } + return Promise.resolve(links); + } +} \ No newline at end of file From 2fc6a09a5988fcef8520828ea261671cba922663 Mon Sep 17 00:00:00 2001 From: Maxime Dufour Date: Mon, 16 Oct 2023 10:54:48 +0000 Subject: [PATCH 3/6] Add json at the end of resources description This will automatically add solor syntax --- src/extension.ts | 3 +-- src/ui-test/treeview.ts | 4 ++-- src/virtual_filesystem/oscvirtualfs.ts | 14 +++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 2674914..33b6f54 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -28,10 +28,9 @@ function getMultipleSelection(mainSelectedItem: T, allSelectedItems?: any[]): } export async function showResourceDetails(profileName: string, resourceType: ResourceNodeType, resourceId: string) { - const uri = vscode.Uri.parse('osc:/' + profileName + "/" + resourceType + "/" + resourceId); + const uri = vscode.Uri.parse('osc:/' + profileName + "/" + resourceType + "/" + resourceId + ".json"); const doc = await vscode.workspace.openTextDocument(uri); // calls back into the provider await vscode.window.showTextDocument(doc); - await vscode.languages.setTextDocumentLanguage(doc, "json"); } // this method is called when your extension is activated diff --git a/src/ui-test/treeview.ts b/src/ui-test/treeview.ts index e41aae2..914987a 100644 --- a/src/ui-test/treeview.ts +++ b/src/ui-test/treeview.ts @@ -507,14 +507,14 @@ describe('ActivityBar', () => { // New titles in editor const editor = new TextEditor(); - expect(await editor.getTitle()).equals("AK"); + expect(await editor.getTitle()).equals("AK.json"); const data = await editor.getText(); const accessKey = osc.AccessKeyFromJSON(JSON.parse(data)); expect(accessKey.accessKeyId).equals("AK"); expect(accessKey.state).equals("ACTIVE"); const editorView = new EditorView(); - await editorView.closeEditor("AK"); + await editorView.closeEditor("AK.json"); }); it.skip("refresh the state", async () => { diff --git a/src/virtual_filesystem/oscvirtualfs.ts b/src/virtual_filesystem/oscvirtualfs.ts index 63b0be5..85c6737 100644 --- a/src/virtual_filesystem/oscvirtualfs.ts +++ b/src/virtual_filesystem/oscvirtualfs.ts @@ -78,18 +78,22 @@ export class OscVirtualContentProvider implements vscode.TextDocumentContentProv onDidChange = this.onDidChangeEmitter.event; async provideTextDocumentContent(uri: vscode.Uri): Promise { - const pathSplit = uri.path.split("/"); - if (pathSplit.length !== 4) { + const regexp = new RegExp("osc:/([^/]+)/([^/]+)/([^/]+).json"); + const uriString = uri.toString(true); + + const result = regexp.exec(uriString); + if (result === null) { throw new Error("malformed uri"); } + // Retrieve Profile - const uriProfile = pathSplit[1]; + const uriProfile = result[1]; const profile = getProfile(uriProfile); // Retrieve the resource Type - const resourceType = pathSplit[2]; - const resourceId = pathSplit[3]; + const resourceType = result[2]; + const resourceId = result[3]; return this.readFileAsync(profile, resourceType, resourceId); } From 0440f004206cbe17b2c91798246ca80ea4925e78 Mon Sep 17 00:00:00 2001 From: Maxime Dufour Date: Mon, 16 Oct 2023 11:09:41 +0000 Subject: [PATCH 4/6] Release v0.7.0 --- CHANGELOG.md | 6 ++++++ package-lock.json | 4 ++-- package.json | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04963d4..f274128 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## [v0.7.0] +### Features + - Add resources links in resource descriptions + - Add VmGroup and VmTemplate resources +### Changes + - Change virtual resources description filename to add autodetection of file language (add .json) ## [v0.6.1] ### Bugfixes - Add Virtual Private Gateway to the Net visualization graph diff --git a/package-lock.json b/package-lock.json index dfa2e0a..b9b3992 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "osc-viewer", - "version": "0.6.1", + "version": "0.7.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "osc-viewer", - "version": "0.6.1", + "version": "0.7.0", "dependencies": { "@types/cytoscape": "^3.19.9", "@vscode/l10n": "^0.0.13", diff --git a/package.json b/package.json index 9fb1fc2..561749d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "osc-viewer", "displayName": "osc-viewer", "description": "Viewer of the resource in the 3DS Outscale Cloud", - "version": "0.6.1", + "version": "0.7.0", "l10n": "./l10n", "icon": "resources/outscale.png", "publisher": "outscale", @@ -407,4 +407,4 @@ "rxjs": "^7.5.7", "true-myth": "^6.2.0" } -} +} \ No newline at end of file From 55d2ac7dcf084763f12fe67eabb9d1cb0a01ddeb Mon Sep 17 00:00:00 2001 From: Maxime Dufour Date: Wed, 18 Oct 2023 08:30:57 +0000 Subject: [PATCH 5/6] Use latest Outscale SDK to fix compilation Signed-off-by: Maxime Dufour --- package-lock.json | 181 ++++++++++++++++++++++++---------------------- package.json | 4 +- 2 files changed, 97 insertions(+), 88 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9b3992..b98674a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@types/cytoscape": "^3.19.9", "@vscode/l10n": "^0.0.13", "cross-fetch": "^3.1.5", - "outscale-api": "^0.9.0", + "outscale-api": "^0.11.0", "rxjs": "^7.5.7", "true-myth": "^6.2.0" }, @@ -31,7 +31,7 @@ "eslint": "^8.14.0", "glob": "^8.0.1", "mocha": "^9.2.2", - "outscale-api": "^0.9.0", + "outscale-api": "^0.11.0", "ovsx": "^0.8.0", "sinon": "^14.0.1", "ts-mock-imports": "^1.3.8", @@ -603,15 +603,15 @@ } }, "node_modules/@vscode/vsce": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.18.0.tgz", - "integrity": "sha512-tUA3XoKx5xjoi3EDcngk0VUYMhvfXLhS4s7CntpLPh1qtLYtgSCexTIMUHkCy6MqyozRW98bdW3a2yHPEADRnQ==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.21.1.tgz", + "integrity": "sha512-f45/aT+HTubfCU2oC7IaWnH9NjOWp668ML002QiFObFRVUCoLtcwepp9mmql/ArFUy+HCHp54Xrq4koTcOD6TA==", "dev": true, "dependencies": { "azure-devops-node-api": "^11.0.1", "chalk": "^2.4.2", "cheerio": "^1.0.0-rc.9", - "commander": "^6.1.0", + "commander": "^6.2.1", "glob": "^7.0.6", "hosted-git-info": "^4.0.2", "jsonc-parser": "^3.2.0", @@ -621,11 +621,11 @@ "minimatch": "^3.0.3", "parse-semver": "^1.1.1", "read": "^1.0.7", - "semver": "^5.1.0", + "semver": "^7.5.2", "tmp": "^0.2.1", "typed-rest-client": "^1.8.4", "url-join": "^4.0.1", - "xml2js": "^0.4.23", + "xml2js": "^0.5.0", "yauzl": "^2.3.1", "yazl": "^2.2.2" }, @@ -718,15 +718,6 @@ "node": ">=4" } }, - "node_modules/@vscode/vsce/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, "node_modules/@vscode/vsce/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -739,6 +730,19 @@ "node": ">=4" } }, + "node_modules/@vscode/vsce/node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/acorn": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", @@ -879,10 +883,10 @@ } }, "node_modules/aws4fetch": { - "version": "1.0.13", - "resolved": "git+ssh://git@github.com/outscale-dev/aws4fetch.git#dcd7e13ee42facf987272bf17b1cc8efaaf03d1d", - "dev": true, - "license": "MIT" + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.17.tgz", + "integrity": "sha512-4IbOvsxqxeOSxI4oA+8xEO8SzBMVlzbSTgGy/EF83rHnQ/aKtP6Sc6YV/k0oiW0mqrcxuThlbDosnvetGOuO+g==", + "dev": true }, "node_modules/azure-devops-node-api": { "version": "11.2.0", @@ -1673,9 +1677,9 @@ } }, "node_modules/editorconfig/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -2047,9 +2051,9 @@ } }, "node_modules/execa/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -2344,9 +2348,9 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "engines": { "node": "*" @@ -3548,12 +3552,12 @@ } }, "node_modules/outscale-api": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/outscale-api/-/outscale-api-0.9.0.tgz", - "integrity": "sha512-BwawLkCMWDtwsNttgRTjd1GEKFWs9uypLLv1W2SggGDIAIUzBzDLVu3GznPIuUt9NTAh6aEzXAASU5gYrcpSMQ==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/outscale-api/-/outscale-api-0.11.0.tgz", + "integrity": "sha512-dUfah/6ChkeU8feP5SgahaE0KmubyMafWRzMIqWH1JFP9kZV8opRhs+66NmPPENsBX/ymrllioT+PBf0a0sU4g==", "dev": true, "dependencies": { - "aws4fetch": "github:outscale-dev/aws4fetch" + "aws4fetch": "^1.0.13" } }, "node_modules/ovsx": { @@ -3652,9 +3656,9 @@ } }, "node_modules/parse-semver/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -4184,9 +4188,9 @@ } }, "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4917,9 +4921,9 @@ } }, "node_modules/vsce/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -5021,9 +5025,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5616,15 +5620,15 @@ } }, "@vscode/vsce": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.18.0.tgz", - "integrity": "sha512-tUA3XoKx5xjoi3EDcngk0VUYMhvfXLhS4s7CntpLPh1qtLYtgSCexTIMUHkCy6MqyozRW98bdW3a2yHPEADRnQ==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.21.1.tgz", + "integrity": "sha512-f45/aT+HTubfCU2oC7IaWnH9NjOWp668ML002QiFObFRVUCoLtcwepp9mmql/ArFUy+HCHp54Xrq4koTcOD6TA==", "dev": true, "requires": { "azure-devops-node-api": "^11.0.1", "chalk": "^2.4.2", "cheerio": "^1.0.0-rc.9", - "commander": "^6.1.0", + "commander": "^6.2.1", "glob": "^7.0.6", "hosted-git-info": "^4.0.2", "jsonc-parser": "^3.2.0", @@ -5635,11 +5639,11 @@ "minimatch": "^3.0.3", "parse-semver": "^1.1.1", "read": "^1.0.7", - "semver": "^5.1.0", + "semver": "^7.5.2", "tmp": "^0.2.1", "typed-rest-client": "^1.8.4", "url-join": "^4.0.1", - "xml2js": "^0.4.23", + "xml2js": "^0.5.0", "yauzl": "^2.3.1", "yazl": "^2.2.2" }, @@ -5705,12 +5709,6 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -5719,6 +5717,16 @@ "requires": { "has-flag": "^3.0.0" } + }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } } } }, @@ -5812,9 +5820,10 @@ "dev": true }, "aws4fetch": { - "version": "git+ssh://git@github.com/outscale-dev/aws4fetch.git#dcd7e13ee42facf987272bf17b1cc8efaaf03d1d", - "dev": true, - "from": "aws4fetch@github:outscale-dev/aws4fetch" + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.17.tgz", + "integrity": "sha512-4IbOvsxqxeOSxI4oA+8xEO8SzBMVlzbSTgGy/EF83rHnQ/aKtP6Sc6YV/k0oiW0mqrcxuThlbDosnvetGOuO+g==", + "dev": true }, "azure-devops-node-api": { "version": "11.2.0", @@ -6417,9 +6426,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "yallist": { @@ -6698,9 +6707,9 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "shebang-command": { @@ -6930,9 +6939,9 @@ "dev": true }, "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, "get-intrinsic": { @@ -7840,12 +7849,12 @@ } }, "outscale-api": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/outscale-api/-/outscale-api-0.9.0.tgz", - "integrity": "sha512-BwawLkCMWDtwsNttgRTjd1GEKFWs9uypLLv1W2SggGDIAIUzBzDLVu3GznPIuUt9NTAh6aEzXAASU5gYrcpSMQ==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/outscale-api/-/outscale-api-0.11.0.tgz", + "integrity": "sha512-dUfah/6ChkeU8feP5SgahaE0KmubyMafWRzMIqWH1JFP9kZV8opRhs+66NmPPENsBX/ymrllioT+PBf0a0sU4g==", "dev": true, "requires": { - "aws4fetch": "github:outscale-dev/aws4fetch" + "aws4fetch": "^1.0.13" } }, "ovsx": { @@ -7917,9 +7926,9 @@ }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true } } @@ -8307,9 +8316,9 @@ } }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -8885,9 +8894,9 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "supports-color": { @@ -8969,9 +8978,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "workerpool": { diff --git a/package.json b/package.json index 561749d..60e4ef4 100644 --- a/package.json +++ b/package.json @@ -390,7 +390,7 @@ "eslint": "^8.14.0", "glob": "^8.0.1", "mocha": "^9.2.2", - "outscale-api": "^0.9.0", + "outscale-api": "^0.11.0", "ovsx": "^0.8.0", "sinon": "^14.0.1", "ts-mock-imports": "^1.3.8", @@ -403,7 +403,7 @@ "@types/cytoscape": "^3.19.9", "@vscode/l10n": "^0.0.13", "cross-fetch": "^3.1.5", - "outscale-api": "^0.9.0", + "outscale-api": "^0.11.0", "rxjs": "^7.5.7", "true-myth": "^6.2.0" } From 418075e2fe165a722362200f257f179ac08863eb Mon Sep 17 00:00:00 2001 From: Maxime Dufour Date: Fri, 20 Oct 2023 09:25:18 +0000 Subject: [PATCH 6/6] Add documentation regarding the resource management Signed-off-by: Maxime Dufour --- docs/how-to/add-a-resource.md | 262 ++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 docs/how-to/add-a-resource.md diff --git a/docs/how-to/add-a-resource.md b/docs/how-to/add-a-resource.md new file mode 100644 index 0000000..9b5ab08 --- /dev/null +++ b/docs/how-to/add-a-resource.md @@ -0,0 +1,262 @@ +# How to add a resource ? + +- [How to add a resource ?](#how-to-add-a-resource-) + - [Minimum for a resource](#minimum-for-a-resource) + - [Implement API calls for the resource](#implement-api-calls-for-the-resource) + - [Add the resource type into the enum](#add-the-resource-type-into-the-enum) + - [Implement the class to display the resource](#implement-the-class-to-display-the-resource) + - [Load the created class at the startup](#load-the-created-class-at-the-startup) + - [Configure the resource display class to handle the new resource](#configure-the-resource-display-class-to-handle-the-new-resource) + - [Configure the link process to handle resource links](#configure-the-link-process-to-handle-resource-links) + - [Add others existing actions](#add-others-existing-actions) + - [Create a new action](#create-a-new-action) + - [Creating oneshot actions (if not)](#creating-oneshot-actions-if-not) + - [Creating factorized actions (if so)](#creating-factorized-actions-if-so) + - [Register the action (both cases)](#register-the-action-both-cases) + - [Define the command in `package.json`](#define-the-command-in-packagejson) + - [Register the action at start up](#register-the-action-at-start-up) + + +## Minimum for a resource +Let's say you want to add a new Outscale resource to the plugin. Here are the steps to add a new resource. Here we want to add `VmTemplate`. + +### Implement API calls for the resource +Add the functions to call the API using the SDK in `src/cloud/vmtemplate.ts` + +In this file, you need to implement at least 3 functions: + - Getter for all resource of this type +```javascript +export function getVmTemplates(profile: Profile, filters?: osc.FiltersVmTemplate): Promise | string> +``` + - Getter for a specific resource + +```javascript +export function getVmTemplate(profile: Profile, resourceId: string): Promise { +``` + - Deletion of the resource + +```javascript +export function deleteVmTemplate(profile: Profile, resourceId: string): Promise { +``` + +The implementation should be straightforward, take a look at one implemented resource to see the template of these functions. + +### Add the resource type into the enum +Add the type of the resource in the variable `ResourceNodeType` in the file `src/flat/node.ts`. This type is how we deal with Generic in our code especially when we want to display resource description. + +In our case, we will add `VmTemplate`, it is just a convention to remove spaces. +```javascript +export type ResourceNodeType = + "AccessKey" | + ... | + "VmTemplate"; +``` + +### Implement the class to display the resource +Create the resource to display the resource in the plugin + +In this step, we will create a class that implements `ExplorerFolderNode` which will allow us to display the resource in the plugin. To do it, create the file `src/flat/folders/simple/node.folder.vmtemplate.ts`. + +```javascript +export const VMTEMPLATES_FOLDER_NAME = "Vm Templates"; // 1. The display name of the resource + +export class VmTemplatesFolderNode extends FiltersFolderNode implements ExplorerFolderNode { + constructor(readonly profile: Profile) { + super(profile, VMTEMPLATES_FOLDER_NAME); + } + + getChildren(): Thenable { // 2. Retrieval of all resources under the resources + this.updateFilters(); + return getVmTemplates(this.profile, this.filters).then(results => { + if (typeof results === "string") { + vscode.window.showErrorMessage(vscode.l10n.t(`Error while reading {0}: {1}`, this.folderName, results)); + return Promise.resolve([]); + } + const resources = []; + for (const item of results) { + + if (typeof item.vmTemplateId === 'undefined') { + + continue; + } + + if (typeof item.vmTemplateName === 'undefined') { + + continue; + } + + resources.push(new ResourceNode(this.profile, item.vmTemplateName, item.vmTemplateId, "VmTemplate", deleteVmTemplate, getVmTemplate)); + + } + return Promise.resolve(resources.sort(resourceNodeCompare)); + }); + + } + + filtersFromJson(json: string): FiltersVmTemplate { // This function is used to filters the resources directly + return FiltersVmTemplateFromJSON(json); + } +} +``` + +`ResourceNode` is as generic class to display simple resource in the plugin. It takes: +- the profile object to make API calls +- the human name of the resource if it exist, otherwise just put `""` +- the id of the resource +- the type of the resource +- API delete function +- API get function + +### Load the created class at the startup + +To do it, you need to add into the local variable `resources`in `src/flat/node.profile.ts`, the definition of our folder: +```javascript +const resources = [ + [ACCESSKEY_FOLDER_NAME, new AccessKeysFolderNode(this.profile)], + ... + [VMTEMPLATES_FOLDER_NAME, new VmTemplatesFolderNode(this.profile)], +]; +``` + +The first element is the display name in the UI, the second element is how to instantiate the object. + +### Configure the resource display class to handle the new resource +Helps the resources description viewer to display the resource + +To do it, you need to add the serialization method of the resource in the variable `resourceMap` in the file `src/virtual_filesystem/oscvirtualfs.ts` + +In our example: +```javascript +const resourceMap = new Map([ + ["profile", new ResourceEncoding(getAccount, AccountToJSON)], + ... + ["VmTemplate", new ResourceEncoding(getVmTemplate, VmTemplateToJSON)], +]); +``` + +### Configure the link process to handle resource links + +When you display the resource, you can link to the resource but the process needs to detect it. The detection is done by providing the regexp to recognize the ID our resource. You need to add it in `resourceToRegexp`in the file `src/virtual_filesystem/osclinkprovider.ts` + +In our example, the ID of `VmTemplate` is `vmtemplate-<32 char of hexa>" therefore we add: +```javascript +const resourceToRegexp: Map = new Map([ + ["ApiAccessRule", ["aar-", "aar-[a-f0-9]{32}"]], + ... + ["VmTemplate", ["vmtemplate-", "vmtemplate-[a-f0-9]{32}"]]; +``` +The first element is the type of our resource (defined in 2.), the second is composed of the prefix of the ID and the real regexp of the resource. + +With 6 four little steps, you will have the resource in the UI and you will have automatically: +- Resources description +- Deletion +- ID copy +- Link in resource description +- Filters when listing resources + +## Add others existing actions +The previous chapter should covers the average usage but sometimes we need to add more actions. For example: + - Unlink resources (volume, routetable, etc) + - Handle subresources (SecurityGroup, RouteTable) + +If the resource supports one or more actions listed above, you will need to create two more files. + +> Vm Template do not need new actions so the resource will now be RouteTable + +1. Move `src/flat/folders/simple/node.folder.routetables.ts` into `src/flat/folders/specific/node.folder.routetables.ts` + +Why do we need this ? To maintain a list of resources that have additional resources + +2. Create a file `src/flat/resources/node.resources.routetables.ts` + +All interfaces to the additional actions can be found in `src/flat/resources/types` + +Route Table can unlink resource and have subresources therefore it must implement `LinkResourceNode` and `SubResourceNode`. + +The file is now: + +```javascript +export class RouteTableResourceNode extends ResourceNode implements LinkResourceNode, SubResourceNode { + + constructor(readonly profile: Profile, readonly resourceName: string, readonly resourceId: string, readonly resourceState: string) { + super(profile, resourceName, resourceId, "routetables", deleteRouteTable, getRouteTable); + } + + getContextValue(): string { + return "routetableresourcenode;linkresourcenode;subresourcenode"; // This will be improved later but right now this is do enable button actions for the resource `liunkresourcenode` => `Unlink`, `subresourcenode` => `Remove Subresource` + } + + getIconPath(): vscode.ThemeIcon { + switch (this.resourceState) { + case "link": + return new vscode.ThemeIcon("plug"); + default: + return new vscode.ThemeIcon("dash"); + } + + } + ... + +} +``` + +And implements all mandatory methods. + +## Create a new action + +When creating a new action for a resource, you need to think whether or not the action will be used for another resource. + +### Creating oneshot actions (if not) +Add the method in the resource directly + +### Creating factorized actions (if so) +> We will take `UnLink` action as an example + +- Create the interface in `src/flat/resources/types/node.resources.link.ts` (we could have named unlink, you're right) +```javascript +export interface LinkResourceNode extends ExplorerResourceNode { + unlinkResource(): Promise + unlinkAllResource(): Promise +} +``` +You are free to call the method whatever you like as long as it describes the actions. + +- Implements the action in the target resources ([see](#add-others-existing-actions)) + +### Register the action (both cases) +See [resources](https://code.visualstudio.com/api/extension-guides/tree-view#view-actions) for more info +#### Define the command in `package.json` +- Add the command in `commands`, you will need to specify the command name, the title, the category (always `osc-viewer`) and the icon (optional). In our example: +```json +{ + "command": "osc.unlinkResource", + "title": "Unlink", + "category": "osc-viewer" +}, +``` + +- Specify when the action has to be shown by adding to `view/item/context`. In our example: +```json +{ + "command": "osc.unlinkResource", + "when": "view == profile && viewItem =~ /linkresourcenode/", + "group": "oscinteract@5" +}, +``` +> viewItem is related to the getContext method (cf [here](#add-others-existing-actions)) + +#### Register the action at start up +To register the action, you need to add the following code into `src/extension.ts` + +```javascript + vscode.commands.registerCommand('osc.unlinkResource', async (arg: LinkResourceNode) => { + showYesOrNoWindow(vscode.l10n.t(`Do you want to unlink the resource {0} ?`, arg.getResourceName()), async () => { + const res = await arg.unlinkResource(); + if (typeof res === "undefined") { + vscode.window.showInformationMessage(vscode.l10n.t(`The resource {0} has been unlinked`, arg.getResourceName())); + } else { + vscode.window.showErrorMessage(vscode.l10n.t(`Error while unlinking the resource {0}: {1}`, arg.getResourceName(), res)); + } + }); + }); +```