diff --git a/lib/lbt/analyzer/ComponentAnalyzer.js b/lib/lbt/analyzer/ComponentAnalyzer.js index 4c9a1ee82..7cccd873f 100644 --- a/lib/lbt/analyzer/ComponentAnalyzer.js +++ b/lib/lbt/analyzer/ComponentAnalyzer.js @@ -75,6 +75,7 @@ class ComponentAnalyzer { * @private */ _analyzeManifest( manifest, info ) { + const sapApp = (manifest && manifest["sap.app"]) || {}; const ui5 = (manifest && manifest["sap.ui5"]) || {}; if ( ui5.resources && ui5.resources.css ) { @@ -116,12 +117,43 @@ class ComponentAnalyzer { // TODO usages + // See sap/ui/core/Component._createManifestModelConfigurations each( ui5.models, (options, model) => { + let modelType; if ( options.type ) { - const module = ModuleName.fromUI5LegacyName( options.type ); - log.verbose("derived model implementation dependency ", module); - info.addDependency(module); + modelType = options.type; + } else if ( options.dataSource && sapApp.dataSources ) { + const oDataSource = sapApp.dataSources[options.dataSource]; + if (!oDataSource) { + return; + } + // default dataSource type is OData + const dataSourceType = oDataSource.type || "OData"; + switch (dataSourceType) { + case "OData": + if (oDataSource.settings && oDataSource.settings.odataVersion === "4.0") { + modelType = "sap.ui.model.odata.v4.ODataModel"; + } else { + modelType = "sap.ui.model.odata.v2.ODataModel"; + } + break; + case "JSON": + modelType = "sap.ui.model.json.JSONModel"; + break; + case "XML": + modelType = "sap.ui.model.xml.XMLModel"; + break; + default: + // for custom dataSource types, the class should already be specified in the sap.ui5 models config + return; + } + } else { + // no type and no dataSource defined + return; } + const module = ModuleName.fromUI5LegacyName( modelType ); + log.verbose("derived model implementation dependency ", module); + info.addDependency(module); }); const routing = ui5.routing; diff --git a/test/lib/lbt/analyzer/ComponentAnalyzer.js b/test/lib/lbt/analyzer/ComponentAnalyzer.js index c2b495639..b7cad1920 100644 --- a/test/lib/lbt/analyzer/ComponentAnalyzer.js +++ b/test/lib/lbt/analyzer/ComponentAnalyzer.js @@ -594,3 +594,270 @@ test("_analyzeManifest: Manifest with models", async (t) => { t.deepEqual(stubAddDependency.getCall(0).args[0], "sap/ui/model/resource/ResourceModel.js", "addDependency should be called with the dependency name"); }); + +test("_analyzeManifest: Manifest with V2 OData model via dataSources", async (t) => { + const manifest = { + "sap.app": { + "dataSources": { + "mainService": { + "uri": "/uri/to/odata/v2/service", + "type": "OData", + "settings": { + "odataVersion": "2.0" + } + } + } + }, + "sap.ui5": { + "models": { + "": { + "dataSource": "mainService" + } + } + } + }; + + const moduleInfo = { + addDependency: function() {} + }; + const stubAddDependency = sinon.spy(moduleInfo, "addDependency"); + + const analyzer = new ComponentAnalyzer(); + await analyzer._analyzeManifest(manifest, moduleInfo); + + t.true(stubAddDependency.calledOnce, "addDependency was called once"); + t.deepEqual(stubAddDependency.getCall(0).args[0], "sap/ui/model/odata/v2/ODataModel.js", + "addDependency should be called with the dependency name"); +}); + +test("_analyzeManifest: Manifest with V2 OData model via dataSources (default type)", async (t) => { + const manifest = { + "sap.app": { + "dataSources": { + "mainService": { + "uri": "/uri/to/odata/v2/service" + } + } + }, + "sap.ui5": { + "models": { + "": { + "dataSource": "mainService" + } + } + } + }; + + const moduleInfo = { + addDependency: function() {} + }; + const stubAddDependency = sinon.spy(moduleInfo, "addDependency"); + + const analyzer = new ComponentAnalyzer(); + await analyzer._analyzeManifest(manifest, moduleInfo); + + t.true(stubAddDependency.calledOnce, "addDependency was called once"); + t.deepEqual(stubAddDependency.getCall(0).args[0], "sap/ui/model/odata/v2/ODataModel.js", + "addDependency should be called with the dependency name"); +}); + + +test("_analyzeManifest: Manifest with V4 OData model via dataSources", async (t) => { + const manifest = { + "sap.app": { + "dataSources": { + "mainService": { + "uri": "/uri/to/odata/v4/service", + "type": "OData", + "settings": { + "odataVersion": "4.0", + } + } + } + }, + "sap.ui5": { + "models": { + "": { + "dataSource": "mainService" + } + } + } + }; + + const moduleInfo = { + addDependency: function() {} + }; + const stubAddDependency = sinon.spy(moduleInfo, "addDependency"); + + const analyzer = new ComponentAnalyzer(); + await analyzer._analyzeManifest(manifest, moduleInfo); + + t.true(stubAddDependency.calledOnce, "addDependency was called once"); + t.deepEqual(stubAddDependency.getCall(0).args[0], "sap/ui/model/odata/v4/ODataModel.js", + "addDependency should be called with the dependency name"); +}); + +test("_analyzeManifest: Manifest with JSON model via dataSources", async (t) => { + const manifest = { + "sap.app": { + "dataSources": { + "mainService": { + "uri": "/uri/to/json/service", + "type": "JSON" + } + } + }, + "sap.ui5": { + "models": { + "": { + "dataSource": "mainService" + } + } + } + }; + + const moduleInfo = { + addDependency: function() {} + }; + const stubAddDependency = sinon.spy(moduleInfo, "addDependency"); + + const analyzer = new ComponentAnalyzer(); + await analyzer._analyzeManifest(manifest, moduleInfo); + + t.true(stubAddDependency.calledOnce, "addDependency was called once"); + t.deepEqual(stubAddDependency.getCall(0).args[0], "sap/ui/model/json/JSONModel.js", + "addDependency should be called with the dependency name"); +}); + +test("_analyzeManifest: Manifest with V4 OData model via dataSources", async (t) => { + const manifest = { + "sap.app": { + "dataSources": { + "mainService": { + "uri": "/uri/to/xml/service", + "type": "XML" + } + } + }, + "sap.ui5": { + "models": { + "": { + "dataSource": "mainService" + } + } + } + }; + + const moduleInfo = { + addDependency: function() {} + }; + const stubAddDependency = sinon.spy(moduleInfo, "addDependency"); + + const analyzer = new ComponentAnalyzer(); + await analyzer._analyzeManifest(manifest, moduleInfo); + + t.true(stubAddDependency.calledOnce, "addDependency was called once"); + t.deepEqual(stubAddDependency.getCall(0).args[0], "sap/ui/model/xml/XMLModel.js", + "addDependency should be called with the dependency name"); +}); + +test("_analyzeManifest: Manifest with model via dataSources (custom type)", async (t) => { + const manifest = { + "sap.app": { + "dataSources": { + "mainService": { + "uri": "/uri/to/some/service", + "type": "MyType" + } + } + }, + "sap.ui5": { + "models": { + "": { + "dataSource": "mainService" + } + } + } + }; + + const moduleInfo = { + addDependency: function() {} + }; + const stubAddDependency = sinon.spy(moduleInfo, "addDependency"); + + const analyzer = new ComponentAnalyzer(); + await analyzer._analyzeManifest(manifest, moduleInfo); + + t.true(stubAddDependency.notCalled, "addDependency was not called"); +}); + +test("_analyzeManifest: Manifest with model (non existing dataSource)", async (t) => { + const manifest = { + "sap.app": { + "dataSources": { + "mainService": { + "uri": "/uri/to/some/service" + } + } + }, + "sap.ui5": { + "models": { + "": { + "dataSource": "someOtherService" + } + } + } + }; + + const moduleInfo = { + addDependency: function() {} + }; + const stubAddDependency = sinon.spy(moduleInfo, "addDependency"); + + const analyzer = new ComponentAnalyzer(); + await analyzer._analyzeManifest(manifest, moduleInfo); + + t.true(stubAddDependency.notCalled, "addDependency was not called"); +}); + +test("_analyzeManifest: Manifest with model (non existing dataSource)", async (t) => { + const manifest = { + "sap.ui5": { + "models": { + "": { + "dataSource": "mainService" + } + } + } + }; + + const moduleInfo = { + addDependency: function() {} + }; + const stubAddDependency = sinon.spy(moduleInfo, "addDependency"); + + const analyzer = new ComponentAnalyzer(); + await analyzer._analyzeManifest(manifest, moduleInfo); + + t.true(stubAddDependency.notCalled, "addDependency was not called"); +}); + +test("_analyzeManifest: Manifest with model (no type / no dataSource)", async (t) => { + const manifest = { + "sap.ui5": { + "models": { + "": {} + } + } + }; + + const moduleInfo = { + addDependency: function() {} + }; + const stubAddDependency = sinon.spy(moduleInfo, "addDependency"); + + const analyzer = new ComponentAnalyzer(); + await analyzer._analyzeManifest(manifest, moduleInfo); + + t.true(stubAddDependency.notCalled, "addDependency was not called"); +});