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

[FIX] ApplicationFormatter: detect the namespace from component controller #243

Merged
merged 2 commits into from
Apr 24, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
68 changes: 60 additions & 8 deletions lib/types/application/ApplicationFormatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ const fs = require("graceful-fs");
const {promisify} = require("util");
const readFile = promisify(fs.readFile);
const AbstractFormatter = require("../AbstractFormatter");
let readXML; // lazy definition of the readXML function (see readPOM)

class ApplicationFormatter extends AbstractFormatter {
/**
* Validates the project and retrieves its manifest
*
*
* @param {Object} project
* @returns {Promise} when validated and manifest has been read
*/
Expand All @@ -19,34 +20,85 @@ class ApplicationFormatter extends AbstractFormatter {
"/": project.resources.configuration.paths.webapp
};

return this.readManifest(project).then(function(manifest) {
if (!manifest["sap.app"] || !manifest["sap.app"].id) {
return this.readManifest(project).then((manifest) => {
// check for a proper sap.app/id in manifest.json to determine namespace
const appId = manifest["sap.app"] && manifest["sap.app"].id;
if (!appId) {
log.warn(`No "sap.app" ID configuration found in manifest of project ${project.metadata.name}`);
return;
}
project.metadata.namespace = manifest["sap.app"].id.replace(/\./g, "/");
}).catch((err) => {
return appId;
}, (err) => {
log.verbose(`No manifest found for project ${project.metadata.name}.`);
}).then((appId) => {
// check app id for being a Maven placeholder and try to read the value from the pom.xml
const parts = appId && appId.match(/^\$\{(.*)\}$/);
if (parts) {
log.verbose(`"sap.app" ID configuration contains Maven placeholder "${parts[1]}". Resolving from pom.xml...`);
return this.readPOM(project).then((pom) => {
let mvnAppId;
if (pom.project && pom.project.properties && pom.project.properties[parts[1]]) {
mvnAppId = pom.project.properties[parts[1]];
} else {
let obj = pom;
parts[1].split(".").forEach((part) => {
obj = obj && obj[part];
});
mvnAppId = obj;
}
petermuessig marked this conversation as resolved.
Show resolved Hide resolved
if (!mvnAppId) {
log.warn(`"sap.app" ID configuration couldn't be resolved from Maven property "${parts[1]}" of pom.xml of project ${project.metadata.name}`);
return;
}
return mvnAppId;
}, (err) => {
log.verbose(`No or invalid pom.xml found for project ${project.metadata.name}.`);
});
}
return appId;
}).then((appId) => {
if (appId) {
project.metadata.namespace = appId.replace(/\./g, "/");
log.verbose(`"sap.app" ID configuration found and set as namespace ${project.metadata.namespace} for project ${project.metadata.name}.`);
}
});
});
}

/**
* Reads the manifest
*
*
* @param {Object} project
* @returns {Promise<Object>} resolves with the json object
*/
readManifest(project) {
return readFile(path.join(project.path, project.resources.pathMappings["/"], "manifest.json"))
return readFile(path.join(project.path, project.resources.pathMappings["/"], "manifest.json"), "utf-8")
RandomByte marked this conversation as resolved.
Show resolved Hide resolved
.then((file) => {
return JSON.parse(file);
});
}

/**
* Reads the pom.xml file
*
* @param {Object} project
* @returns {Promise<Object>} resolves with the XML document from the pom.xml
*/
readPOM(project) {
if (!readXML) {
const xml2js = require("xml2js");
const parser = new xml2js.Parser({
explicitArray: false,
ignoreAttrs: true
});
readXML = promisify(parser.parseString);
}
return readFile(path.join(project.path, "pom.xml"), "utf-8").then(readXML);
}

/**
* Validates a project
*
*
* @param {Object} project
* @returns {Promise} resolves if successfully validated
* @throws {Error} if validation fails
Expand Down
41 changes: 41 additions & 0 deletions test/fixtures/application.h/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">


<!--**************************************************************************
* POM SECTION: Maven Model Version Settings
***************************************************************************-->

<modelVersion>4.0.0</modelVersion>


<!--**************************************************************************
* POM SECTION: Maven Settings
***************************************************************************-->

<groupId>com.sap.test</groupId>
<artifactId>application.h</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>


<!--**************************************************************************
* POM SECTION: Project Settings
***************************************************************************-->

<name>application.h</name>
<description>Simple SAPUI5 based application</description>


<!--**************************************************************************
* POM SECTION: Properties Settings
***************************************************************************-->

<properties>

<componentName>application.h</componentName>

</properties>


</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "${project.artifactId}",
"type": "application",
"applicationVersion": {
"version": "1.2.2"
},
"embeds": ["embedded"],
"title": "{{title}}"
}
}
13 changes: 13 additions & 0 deletions test/fixtures/application.h/webapp-properties.appId/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "${appId}",
"type": "application",
"applicationVersion": {
"version": "1.2.2"
},
"embeds": ["embedded"],
"title": "{{title}}"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "${componentName}",
"type": "application",
"applicationVersion": {
"version": "1.2.2"
},
"embeds": ["embedded"],
"title": "{{title}}"
}
}
51 changes: 51 additions & 0 deletions test/lib/types/application/ApplicationFormatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,54 @@ test("format: set namespace to id", async (t) => {
t.deepEqual(project.metadata.namespace, "my/id",
"namespace was successfully set since readManifest provides the correct object structure");
});

const applicationHPath = path.join(__dirname, "..", "..", "..", "fixtures", "application.h");
const applicationHTree = {
id: "application.h",
version: "1.0.0",
path: applicationHPath,
dependencies: [],
_level: 0,
specVersion: "0.1",
type: "application",
metadata: {
name: "application.h"
},
resources: {
configuration: {
paths: {
webapp: "webapp"
}
}
}
};

test("namespace: detect namespace from pom.xml via ${project.artifactId}", async (t) => {
const myProject = clone(applicationHTree);
myProject.resources.configuration.paths.webapp = "webapp-project.artifactId";
const applicationFormatter = new ApplicationFormatter();

await applicationFormatter.format(myProject);
t.deepEqual(myProject.metadata.namespace, "application/h",
"namespace was successfully set since readManifest provides the correct object structure");
});

test("namespace: detect namespace from pom.xml via ${componentName} from properties", async (t) => {
const myProject = clone(applicationHTree);
myProject.resources.configuration.paths.webapp = "webapp-properties.componentName";
const applicationFormatter = new ApplicationFormatter();

await applicationFormatter.format(myProject);
t.deepEqual(myProject.metadata.namespace, "application/h",
"namespace was successfully set since readManifest provides the correct object structure");
});

test("namespace: detect namespace from pom.xml via ${appId} from properties", async (t) => {
const myProject = clone(applicationHTree);
myProject.resources.configuration.paths.webapp = "webapp-properties.appId";
const applicationFormatter = new ApplicationFormatter();

await applicationFormatter.format(myProject);
t.falsy(myProject.metadata.namespace,
"namespace is falsy since readManifest resolves with an empty object");
});