diff --git a/package-lock.json b/package-lock.json
index 118df32..dc93aab 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -89,6 +89,11 @@
"integrity": "sha512-xRas+PW/fM/MoonB+Pawg48bGTjCqJsFUFwZpH/q2oW80AMHGDAUTUqySBnqeQ18e/SNi7NOCf3ZkYOzKm3Pqw==",
"dev": true
},
+ "@types/xml-zero-lexer": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/xml-zero-lexer/-/xml-zero-lexer-2.1.0.tgz",
+ "integrity": "sha512-UP1ZV82+PbGEvy1+vFZa55Q3FWc7wX64iFsbFlp+D3ULFPw8F8J5WGgRsQ575DXhFBeW9dQkV/KJsLkHp+xqqw=="
+ },
"@types/xml2js": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.4.tgz",
@@ -5078,6 +5083,11 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
+ "xml-zero-lexer": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/xml-zero-lexer/-/xml-zero-lexer-2.1.0.tgz",
+ "integrity": "sha512-tr5RJrQOo4LBoSiknbeRJpedAiOKLdsgq1o5pV6aMa3prZbc2Gd2RnrxmcOELP15zkxJCqN2msC+mRYvVvEtkQ=="
+ },
"xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
diff --git a/package.json b/package.json
index bfd701d..542805c 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
"activationEvents": [
"onCommand:spring.initializr.maven-project",
"onCommand:spring.initializr.gradle-project",
- "onCommand:spring.initializr.editStarters",
+ "onCommand:spring.initializr.addStarters",
"onCommand:spring.initializr.createProject"
],
"main": "./dist/extension",
@@ -28,22 +28,22 @@
"commands": [
{
"command": "spring.initializr.createProject",
- "title": "Create a Spring Boot Project",
+ "title": "Create a Spring Boot Project...",
"category": "Spring Initializr"
},
{
"command": "spring.initializr.maven-project",
- "title": "Generate a Maven Project",
+ "title": "Create a Maven Project...",
"category": "Spring Initializr"
},
{
"command": "spring.initializr.gradle-project",
- "title": "Generate a Gradle Project",
+ "title": "Create a Gradle Project...",
"category": "Spring Initializr"
},
{
- "command": "spring.initializr.editStarters",
- "title": "Edit starters",
+ "command": "spring.initializr.addStarters",
+ "title": "Add Starters...",
"category": "Spring Initializr"
}
],
@@ -57,14 +57,14 @@
"editor/context": [
{
"when": "resourceFilename == pom.xml",
- "command": "spring.initializr.editStarters",
+ "command": "spring.initializr.addStarters",
"group": "SpringInitializr"
}
],
"explorer/context": [
{
"when": "resourceFilename == pom.xml",
- "command": "spring.initializr.editStarters",
+ "command": "spring.initializr.addStarters",
"group": "SpringInitializr"
}
]
@@ -149,6 +149,7 @@
"@types/mocha": "^5.2.7",
"@types/node": "^10.14.13",
"@types/vscode": "1.30.0",
+ "@types/xml-zero-lexer": "^2.1.0",
"@types/xml2js": "^0.4.4",
"glob": "^7.1.4",
"mocha": "^7.1.1",
@@ -165,6 +166,7 @@
"lodash": "^4.17.19",
"md5": "^2.2.1",
"vscode-extension-telemetry-wrapper": "^0.8.0",
+ "xml-zero-lexer": "^2.1.0",
"xml2js": "^0.4.19"
}
}
diff --git a/src/Utils/error.ts b/src/Utils/error.ts
new file mode 100644
index 0000000..9367173
--- /dev/null
+++ b/src/Utils/error.ts
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import { setUserError } from "vscode-extension-telemetry-wrapper";
+
+export class UserError extends Error {
+ constructor(msg?: string) {
+ super(msg);
+ setUserError(this);
+ }
+}
diff --git a/src/Utils/xml/index.ts b/src/Utils/xml/index.ts
new file mode 100644
index 0000000..0391996
--- /dev/null
+++ b/src/Utils/xml/index.ts
@@ -0,0 +1,169 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import * as vscode from "vscode";
+import { UserError } from "../error";
+import { ElementNode, getNodesByTag, XmlTagName } from "./lexer";
+
+export async function updatePom(pomPath: string, deps: IArtifact[], boms: IBom[]) {
+ const edit = new vscode.WorkspaceEdit();
+
+ const projectNode: ElementNode = await getActiveProjectNode();
+ const dependenciesNode: ElementNode | undefined = projectNode.children && projectNode.children.find(node => node.tag === XmlTagName.Dependencies);
+ if (dependenciesNode !== undefined) {
+ await updateWorkspaceEdit(edit, pomPath, dependenciesNode, new DependencyNodes(deps));
+ } else {
+ await updateWorkspaceEdit(edit, pomPath, projectNode, new DependencyNodes(deps, {initParent: true}));
+ }
+
+ if (boms && boms.length > 0) {
+ const depMgmtNode: ElementNode | undefined = projectNode.children && projectNode.children.find(node => node.tag === XmlTagName.DependencyManagement);
+ if (depMgmtNode !== undefined) {
+ const depsNodes: ElementNode | undefined = depMgmtNode.children && depMgmtNode.children.find(node => node.tag === XmlTagName.Dependencies);
+ if (depsNodes !== undefined) {
+ await updateWorkspaceEdit(edit, pomPath, depsNodes, new BOMNodes(boms));
+ } else {
+ await updateWorkspaceEdit(edit, pomPath, depMgmtNode, new BOMNodes(boms, {parents: ["dependencies"]}));
+ }
+ } else {
+ await updateWorkspaceEdit(edit, pomPath, projectNode, new BOMNodes(boms, {parents: ["dependencies", "dependencyManagement"]}));
+ }
+ }
+
+ vscode.workspace.applyEdit(edit);
+}
+
+async function getActiveProjectNode() {
+ if (!vscode.window.activeTextEditor) {
+ throw new UserError("No POM file is open.");
+ }
+
+ // Find out node and insert content.
+ const content = vscode.window.activeTextEditor.document.getText();
+ const projectNodes: ElementNode[] = getNodesByTag(content, XmlTagName.Project);
+ if (projectNodes === undefined || projectNodes.length !== 1) {
+ throw new UserError("Only support POM file with single node.");
+ }
+
+ return projectNodes[0];
+}
+
+function constructNodeText(nodeToInsert: PomNode, baseIndent: string, indent: string, eol: string): string {
+ const lines: string[] = nodeToInsert.getTextLines(indent);
+ return ["", ...lines].join(`${eol}${baseIndent}${indent}`) + eol;
+}
+
+async function updateWorkspaceEdit(edit: vscode.WorkspaceEdit, pomPath: string, parentNode: ElementNode, nodeToInsert: PomNode): Promise {
+ if (parentNode.contentStart === undefined || parentNode.contentEnd === undefined) {
+ throw new UserError("Invalid target XML node to insert dependency.");
+ }
+ const currentDocument: vscode.TextDocument = await vscode.workspace.openTextDocument(pomPath);
+ const textEditor: vscode.TextEditor = await vscode.window.showTextDocument(currentDocument);
+ const baseIndent: string = getIndentation(currentDocument, parentNode.contentEnd);
+ const options: vscode.TextEditorOptions = textEditor.options;
+ const indent: string = options.insertSpaces ? " ".repeat(options.tabSize as number) : "\t";
+ const eol: string = currentDocument.eol === vscode.EndOfLine.LF ? "\n" : "\r\n";
+
+ let insertPos: vscode.Position = currentDocument.positionAt(parentNode.contentEnd);
+ // Not to mess up indentation, move cursor to line start:
+ // | => |
+ const insPosLineStart: vscode.Position = new vscode.Position(insertPos.line, 0);
+ const contentBefore: string = currentDocument.getText(new vscode.Range(insPosLineStart, insertPos));
+ if (contentBefore.trim() === "") {
+ insertPos = insPosLineStart;
+ }
+
+ const targetText: string = constructNodeText(nodeToInsert, baseIndent, indent, eol);
+
+ edit.insert(currentDocument.uri, insertPos, targetText);
+ return edit;
+}
+
+function getIndentation(document: vscode.TextDocument, offset: number): string {
+ const closingTagPosition: vscode.Position = document.positionAt(offset);
+ return document.getText(new vscode.Range(
+ new vscode.Position(closingTagPosition.line, 0),
+ closingTagPosition
+ ));
+}
+
+interface IArtifact {
+ groupId: string;
+ artifactId: string;
+ version?: string;
+ scope?: string;
+}
+
+interface IBom {
+ groupId: string;
+ artifactId: string;
+ version: string;
+ scope?: string;
+ type?: string;
+}
+
+abstract class PomNode {
+ protected static wrapWithParentNode(lines: string[], indent: string, parent: string) {
+ return [
+ `<${parent}>`,
+ ...lines.map(line => `${indent}${line}`),
+ `${parent}>`,
+ ];
+ }
+
+ public abstract getTextLines(indent: string): string[];
+}
+
+class DependencyNodes extends PomNode {
+ constructor(private artifacts: IArtifact[], private options?: { initParent?: boolean }) {
+ super();
+ }
+
+ public getTextLines(indent: string): string[] {
+ const listOfLines: string[] = [].concat(...this.artifacts.map(artifact => this.toTextLine(artifact, indent)));
+ if (this.options && this.options.initParent) {
+ return PomNode.wrapWithParentNode(listOfLines, indent, "dependencies");
+ } else {
+ return listOfLines;
+ }
+ }
+
+ private toTextLine(artifact: IArtifact, indent: string): string[] {
+ const { groupId, artifactId, version, scope } = artifact;
+ const lines: string[] = [
+ `${groupId}`,
+ `${artifactId}`,
+ version && `${version}`,
+ scope && scope !== "compile" && `${scope}`,
+ ].filter(Boolean);
+ return PomNode.wrapWithParentNode(lines, indent, "dependency");
+ }
+}
+
+class BOMNodes extends PomNode {
+ constructor(private boms: IBom[], private options?: { parents?: string[] }) { super(); }
+
+ public getTextLines(indent: string): string[] {
+ const listOfLines: string[][] = this.boms.map(bom => this.bomToTextLine(bom, indent));
+ let lines: string[] = [].concat(...listOfLines);
+ if (this.options && this.options.parents && this.options.parents.length > 0) {
+ for (const parent of this.options.parents) {
+ lines = PomNode.wrapWithParentNode(lines, indent, parent);
+ }
+ }
+ return lines;
+ }
+
+ private bomToTextLine(bom: IBom, indent: string): string[] {
+ const { groupId, artifactId, version } = bom;
+ const lines: string[] = [
+ `${groupId}`,
+ `${artifactId}`,
+ `${version}`,
+ `pom`,
+ `import`,
+ ];
+ return PomNode.wrapWithParentNode(lines, indent, "dependency");
+ }
+
+}
diff --git a/src/Utils/xml/lexer.ts b/src/Utils/xml/lexer.ts
new file mode 100644
index 0000000..f3a2be0
--- /dev/null
+++ b/src/Utils/xml/lexer.ts
@@ -0,0 +1,146 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import Lexx, { NodeTypes } from "xml-zero-lexer";
+
+export enum XmlTagName {
+ GroupId = "groupId",
+ ArtifactId = "artifactId",
+ Version = "version",
+ Dependencies = "dependencies",
+ Plugins = "plugins",
+ Project = "project",
+ DependencyManagement = "dependencyManagement"
+}
+
+export class ElementNode {
+ public parent: ElementNode;
+ public tag: string;
+ public text?: string;
+ public contentStart?: number;
+ public contentEnd?: number;
+ public children?: ElementNode[];
+
+ constructor(parent: ElementNode, tag: string) {
+ this.parent = parent;
+ this.tag = tag;
+ this.text = "";
+ }
+
+ /**
+ * Add a child for current node.
+ * NOTE: an ElementNode can have **either** a value or a list of children.
+ * E.g.
+ * ```xml
+ *
+ * test-gid
+ * test-aid
+ *
+ * ```
+ * For node `dependency`, it has two children.
+ * For node `groupId`, it has value of `test-gid`.
+ *
+ * @param child the child element node.
+ */
+ public addChild(child: ElementNode): void {
+ if (this.text !== undefined) {
+ this.text = undefined; // value cannot exist with children.
+ }
+ if (this.children === undefined) {
+ this.children = [];
+ }
+ this.children.push(child);
+ }
+
+}
+
+export function getNodesByTag(text: string, tag: string): ElementNode[] {
+ const tokens: number[][] = Lexx(text);
+ return getElementHierarchy(text, tokens, tag);
+}
+
+export function getCurrentNode(text: string, offset: number): ElementNode | undefined {
+ const tokens: number[][] = Lexx(text);
+ return getElementHierarchy(text, tokens, offset);
+}
+
+function getElementHierarchy(text: string, tokens: number[][], targetTag: string): ElementNode[];
+function getElementHierarchy(text: string, tokens: number[][], cursorOffset: number): ElementNode;
+// tslint:disable-next-line:cyclomatic-complexity
+function getElementHierarchy(text: string, tokens: number[][], tagOrOffset: number | string): ElementNode | ElementNode[] | undefined {
+ let targetTag: string | undefined;
+ let cursorOffset: number | undefined;
+ if (typeof tagOrOffset === "string") {
+ targetTag = tagOrOffset;
+ } else if (typeof tagOrOffset === "number") {
+ cursorOffset = tagOrOffset;
+ }
+ const n: number = tokens.length;
+ const elementNodes: ElementNode[] = [];
+ const tagNodes: ElementNode[] = [];
+ let cursorNode: ElementNode | undefined;
+ let pointer: number = 0;
+ let i: number = 0;
+
+ while (i < n) {
+ const token: number[] = tokens[i];
+ const currentNode: ElementNode = elementNodes[elementNodes.length - 1];
+ switch (token[0]) {
+ case NodeTypes.XML_DECLARATION:
+ case NodeTypes.ELEMENT_NODE: {
+ // [_type, start, end] = token;
+ const [start, end] = token.slice(1, 3);
+ const newElement: ElementNode = new ElementNode(currentNode, text.substring(start, end));
+ if (currentNode !== undefined) {
+ currentNode.addChild(newElement);
+ }
+
+ pointer = end + 1; // pass ">" mark.
+ elementNodes.push(newElement);
+ newElement.contentStart = pointer;
+ break;
+ }
+ case NodeTypes.ATTRIBUTE_NODE: {
+ // [_type, _keyStart, _keyEnd, _valueStart, valueEnd] = token;
+ const valueEnd: number = token[4];
+ // Attributes not handled yet.
+ pointer = valueEnd + 1; // pass ">" mark.
+ currentNode.contentStart = pointer;
+ break;
+ }
+ case NodeTypes.TEXT_NODE: {
+ // [_type, start, end] = token;
+ const [start, end] = token.slice(1, 3);
+ if (currentNode !== undefined) {
+ currentNode.text = text.substring(start, end);
+ }
+ pointer = end;
+ break;
+ }
+ case NodeTypes.CLOSE_ELEMENT: {
+ currentNode.contentEnd = pointer;
+ elementNodes.pop();
+ break;
+ }
+ default:
+ break;
+ }
+ if (targetTag !== undefined && currentNode !== undefined && targetTag === currentNode.tag && tagNodes.indexOf(currentNode) < 0) {
+ tagNodes.push(currentNode);
+ }
+ if (cursorOffset !== undefined
+ && cursorNode === undefined
+ && currentNode !== undefined
+ && currentNode.contentStart !== undefined && currentNode.contentStart <= cursorOffset
+ && currentNode.contentEnd !== undefined && cursorOffset <= currentNode.contentEnd) {
+ cursorNode = currentNode;
+ }
+ i += 1;
+ }
+ if (targetTag !== undefined) {
+ return tagNodes;
+ } else if (cursorOffset !== undefined) {
+ return cursorNode !== undefined ? cursorNode : elementNodes[elementNodes.length - 1];
+ }
+ return undefined;
+}
diff --git a/src/extension.ts b/src/extension.ts
index 7fcecb7..5ceb6b2 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -8,7 +8,7 @@ import {
initializeFromJsonFile,
instrumentOperation
} from "vscode-extension-telemetry-wrapper";
-import { GenerateProjectHandler } from "./handler";
+import { AddStartersHandler, GenerateProjectHandler } from "./handler";
import { getTargetPomXml, loadPackageInfo } from "./Utils";
export async function activate(context: vscode.ExtensionContext): Promise {
@@ -38,11 +38,11 @@ async function initializeExtension(_operationId: string, context: vscode.Extensi
}
}));
- context.subscriptions.push(instrumentAndRegisterCommand("spring.initializr.editStarters", async (_oid: string, entry?: vscode.Uri) => {
- throw new Error("Not implemented");
+ context.subscriptions.push(instrumentAndRegisterCommand("spring.initializr.addStarters", async (_oid: string, entry?: vscode.Uri) => {
const targetFile: vscode.Uri = entry || await getTargetPomXml();
if (targetFile) {
- // await vscode.window.showTextDocument(targetFile);
+ await vscode.window.showTextDocument(targetFile);
+ await new AddStartersHandler().run(_oid, targetFile);
} else {
vscode.window.showInformationMessage("No pom.xml found in the workspace.");
}
diff --git a/src/handler/AddStartersHandler.ts b/src/handler/AddStartersHandler.ts
new file mode 100644
index 0000000..30a633e
--- /dev/null
+++ b/src/handler/AddStartersHandler.ts
@@ -0,0 +1,135 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+import * as fse from "fs-extra";
+import * as path from "path";
+import * as vscode from "vscode";
+import { setUserError } from "vscode-extension-telemetry-wrapper";
+import { DependencyManager, IDependenciesItem } from "../DependencyManager";
+import {
+ getBootVersion,
+ getDependencyNodes,
+ getParentReletivePath,
+ IMavenId,
+ IStarters,
+ serviceManager,
+ XmlNode
+} from "../model";
+import { readXmlContent } from "../Utils";
+import { updatePom } from "../Utils/xml";
+import { BaseHandler } from "./BaseHandler";
+import { specifyServiceUrl } from "./utils";
+
+export class AddStartersHandler extends BaseHandler {
+ private serviceUrl: string;
+
+ protected get failureMessage(): string {
+ return "Fail to edit starters.";
+ }
+
+ public async runSteps(_operationId: string, entry: vscode.Uri): Promise {
+ const bootVersion: string = await searchForBootVersion(entry.fsPath);
+ if (!bootVersion) {
+ const ex = new Error("Not a valid Spring Boot project.");
+ setUserError(ex);
+ throw ex;
+ }
+
+ const deps: string[] = []; // gid:aid
+ // Read pom.xml for $dependencies(gid, aid)
+ const content: string = vscode.window.activeTextEditor.document.getText();
+ const xml: { project: XmlNode } = await readXmlContent(content);
+
+ getDependencyNodes(xml.project).forEach(elem => {
+ deps.push(`${elem.groupId[0]}:${elem.artifactId[0]}`);
+ });
+
+ this.serviceUrl = await specifyServiceUrl();
+ if (this.serviceUrl === undefined) {
+ return;
+ }
+ // [interaction] Step: Dependencies, with pre-selected deps
+ const starters: IStarters = await vscode.window.withProgress(
+ { location: vscode.ProgressLocation.Window },
+ async (p) => {
+ p.report({ message: `Fetching metadata for version ${bootVersion} ...` });
+ return await serviceManager.getStarters(this.serviceUrl, bootVersion);
+ },
+ );
+
+ const oldStarterIds: string[] = [];
+ if (!starters.dependencies) {
+ await vscode.window.showErrorMessage("Unable to retrieve information of available starters.");
+ return;
+ }
+
+ Object.keys(starters.dependencies).forEach(key => {
+ const elem: IMavenId = starters.dependencies[key];
+ if (deps.indexOf(`${elem.groupId}:${elem.artifactId}`) >= 0) {
+ oldStarterIds.push(key);
+ }
+ });
+ const dependencyManager = new DependencyManager();
+ dependencyManager.selectedIds = [].concat(oldStarterIds);
+ let current: IDependenciesItem = null;
+ do {
+ current = await vscode.window.showQuickPick(
+ dependencyManager.getQuickPickItems(this.serviceUrl, bootVersion),
+ {
+ ignoreFocusOut: true,
+ matchOnDescription: true,
+ matchOnDetail: true,
+ placeHolder: "Select dependencies to add.",
+ },
+ );
+ if (current && current.itemType === "dependency" && oldStarterIds.indexOf(current.id) === -1) {
+ dependencyManager.toggleDependency(current.id);
+ }
+ } while (current && current.itemType === "dependency");
+ if (!current) { return; }
+
+ // Diff deps for adding
+ const toAdd: string[] = dependencyManager.selectedIds.filter(elem => oldStarterIds.indexOf(elem) < 0);
+ if (toAdd.length === 0) {
+ vscode.window.showInformationMessage("No changes.");
+ return;
+ }
+
+ const msgAdd: string = (toAdd && toAdd.length) ? `Adding: [${toAdd.map(d => dependencyManager.dict[d] && dependencyManager.dict[d].name).filter(Boolean).join(", ")}].` : "";
+ const choice: string = await vscode.window.showWarningMessage(`${msgAdd} Proceed?`, "Proceed", "Cancel");
+ if (choice !== "Proceed") {
+ return;
+ }
+
+ const artifacts = toAdd.map(id => starters.dependencies[id]);
+ const bomIds = toAdd.map(id => starters.dependencies[id].bom).filter(Boolean);
+ const boms = bomIds.map(id => starters.boms[id]);
+
+ updatePom(entry.fsPath, artifacts, boms);
+ vscode.window.showInformationMessage("Pom file successfully updated.");
+ return;
+ }
+
+}
+
+async function searchForBootVersion(pomPath: string): Promise {
+ const content: Buffer = await fse.readFile(pomPath);
+ const { project: projectNode } = await readXmlContent(content.toString());
+ const bootVersion: string = getBootVersion(projectNode);
+ if (bootVersion) {
+ return bootVersion;
+ }
+
+ // search recursively in parent pom
+ const relativePath = getParentReletivePath(projectNode);
+ if (relativePath) {
+ let absolutePath = path.join(path.dirname(pomPath), relativePath);
+ if ((await fse.stat(absolutePath)).isDirectory()) {
+ absolutePath = path.join(absolutePath, "pom.xml");
+ }
+ if (await fse.pathExists(absolutePath)) {
+ return await searchForBootVersion(absolutePath);
+ }
+ }
+ return undefined;
+}
diff --git a/src/handler/index.ts b/src/handler/index.ts
index bf8959b..57e79d2 100644
--- a/src/handler/index.ts
+++ b/src/handler/index.ts
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
+import { AddStartersHandler } from "./AddStartersHandler";
import { GenerateProjectHandler } from "./GenerateProjectHandler";
-export { GenerateProjectHandler };
+export { GenerateProjectHandler, AddStartersHandler };
diff --git a/src/model/ServiceManager.ts b/src/model/ServiceManager.ts
index 85e5bac..d7af0a6 100644
--- a/src/model/ServiceManager.ts
+++ b/src/model/ServiceManager.ts
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
-import { IDependency } from ".";
+import { IDependency, IStarters } from ".";
import { downloadFile } from "../Utils";
import { matchRange } from "../Utils/VersionHelper";
import { DependencyGroup, Metadata } from "./Metadata";
@@ -46,6 +46,22 @@ class ServiceManager {
return ret;
}
+ /**
+ * @deprecated `dependencies` endpoint will be removed from metadata v3
+ * This function returns information needed for current implementation of "add starters", e.g. gid/aid/repository/bom etc.
+ * Should be removed in future refactoring.
+ */
+ public async getStarters(serviceUrl: string, bootVersion: string): Promise {
+ const url = `${serviceUrl}dependencies?bootVersion=${bootVersion}`;
+ const rawJSONString: string = await downloadFile(url, true, METADATA_HEADERS);
+ try {
+ const ret = JSON.parse(rawJSONString);
+ return ret;
+ } catch (error) {
+ throw new Error(`failed to parse response from ${url}`);
+ }
+ }
+
private async fetch(serviceUrl: string): Promise {
try {
const rawJSONString: string = await downloadFile(serviceUrl, true, METADATA_HEADERS);
diff --git a/tslint.json b/tslint.json
index 11fa1be..b15d4c6 100644
--- a/tslint.json
+++ b/tslint.json
@@ -11,6 +11,7 @@
"interface-name": false,
"object-literal-sort-keys": false,
"trailing-comma": false,
+ "max-classes-per-file": false,
"no-console": [
true,
"log"