Skip to content

Commit

Permalink
[FIX] ApplicationFormatter: detect the namespace for Maven placeholders
Browse files Browse the repository at this point in the history
When the manifest.json contains a Maven placeholder the namespace cannot
be detected properly. With this extension the namespace can be resolved
from the pom.xml from the pom entries or Maven properties.
  • Loading branch information
petermuessig committed Apr 24, 2019
1 parent 27e86bd commit d73bb11
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 10 deletions.
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;
}
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")
.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
2 changes: 1 addition & 1 deletion test/expected/build/application.h/dest/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "application.g",
"id": "${project.artifactId}",
"type": "application",
"applicationVersion": {
"version": "1.2.2"
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}}"
}
}
2 changes: 1 addition & 1 deletion test/fixtures/application.h/webapp/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "application.g",
"id": "${project.artifactId}",
"type": "application",
"applicationVersion": {
"version": "1.2.2"
Expand Down
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");
});

0 comments on commit d73bb11

Please sign in to comment.