UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

245 lines (224 loc) 9.29 kB
/*! * OpenUI5 * (c) Copyright 2009-2023 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ /** * Defines support rules related to the model. */ sap.ui.define([ "sap/ui/support/library", "sap/ui/support/supportRules/util/StringAnalyzer", "sap/ui/model/CompositeBinding", "sap/ui/model/ListBinding", "sap/ui/model/json/JSONModel", "sap/ui/model/odata/ODataMetadata" ], function( SupportLib, StringAnalyzer, CompositeBinding, ListBinding, JSONModel, ODataMetadata ) { "use strict"; /*eslint max-nested-callbacks: 0 */ // shortcuts var Categories = SupportLib.Categories; // Accessibility, Performance, Memory, ... var Severity = SupportLib.Severity; // Hint, Warning, Error var Audiences = SupportLib.Audiences; // Control, Internal, Application function _fnFindBestMatch(aValues, sBindingPath) { var iJsonModelMin = -1; var sJsonModelBestMatch = false; aValues.forEach(function(sKey) { var iCurrDest = StringAnalyzer.calculateLevenshteinDistance(sBindingPath, sKey); if (iJsonModelMin === -1 || iCurrDest < iJsonModelMin) { iJsonModelMin = iCurrDest; sJsonModelBestMatch = sKey; } }); return sJsonModelBestMatch; } //********************************************************** // Check Functions //********************************************************** var fnCheckSelect = function (oIssueManager, oCoreFacade, oScope) { oScope.getElements().forEach(function (oElement) { var mBindingInfos = {}; Object.assign(mBindingInfos, oElement.mBindingInfos, oElement.mObjectBindingInfos); Object.keys(mBindingInfos).forEach(function (sName) { var oBinding = mBindingInfos[sName].binding, sDetails; if (!oBinding || oBinding.getModel().bAutoExpandSelect) { return; } if (oBinding.isA("sap.ui.model.odata.v2.ODataListBinding") && (!oBinding.mParameters || !oBinding.mParameters.select)) { sDetails = "The aggregation '" + sName + "' of element " + oElement.getId() + " with binding path '" + oBinding.getPath() + "' is bound against a " + "collection, yet no binding parameter 'select' is used. Using 'select' " + "may improve performance."; } else if (oBinding.isA("sap.ui.model.odata.v4.ODataListBinding") && (!oBinding.mParameters || !oBinding.mParameters.$select)) { sDetails = "The aggregation '" + sName + "' of element " + oElement.getId() + " with binding path '" + oBinding.getPath() + "' is " + "bound against a collection, yet no OData query option '$select' is used." + " Using '$select' may improve performance. Alternatively, enable the " + "automatic generation of '$select' and '$expand' in the model using the " + "'autoExpandSelect' parameter."; } else if (oBinding.isA("sap.ui.model.odata.v2.ODataContextBinding") && (!oBinding.mParameters || !oBinding.mParameters.select)) { sDetails = "The element " + oElement.getId() + " with binding path '" + oBinding.getPath() + "' is bound against an entity, yet no binding " + "parameter 'select' is used. Using 'select' may improve performance."; } else if (oBinding.isA("sap.ui.model.odata.v4.ODataContextBinding") && (!oBinding.mParameters || !oBinding.mParameters.$select)) { sDetails = "The element " + oElement.getId() + " with binding path '" + oBinding.getPath() + "' is bound against an entity, yet no OData query" + " option '$select' is used. Using '$select' may improve performance. " + "Alternatively, enable the automatic generation of '$select' and " + "'$expand' in the model using the 'autoExpandSelect' parameter."; } if (sDetails) { oIssueManager.addIssue({ context : { id : oElement.getId() }, details : sDetails, severity : Severity.Low }); } }); }); }; //********************************************************** // Rule Definitions //********************************************************** /** * Checks whether there are bindings for models where the model is available but a binding has no result. * It checks the path structure and checks for typos. */ var oBindingPathSyntaxValidation = { id: "bindingPathSyntaxValidation", audiences: [Audiences.Control, Audiences.Application], categories: [Categories.Bindings], enabled: true, minversion: "1.32", title: "Model: Unresolved binding path", description: "The binding path used in the model could not be resolved", resolution: "Check the binding path for typos", resolutionurls: [ { href: "https://sdk.openui5.org/api/sap.ui.model.Context", text: "API Reference: Context" }, { href: "https://sdk.openui5.org/topic/e5310932a71f42daa41f3a6143efca9c", text: "Documentation: Data Binding Tutorial" }, { href: "https://sdk.openui5.org/topic/97830de2d7314e93b5c1ee3878a17be9", text: "Documentation: Data Binding Tutorial - Step 12: Aggregation Binding Using Templates" }, { href: "https://sdk.openui5.org/topic/6c7c5c266b534e7ea9a28f861dc515f5", text: "Documentation: Data Binding Tutorial - Step 13: Element Binding" } ], check: function(oIssueManager, oCoreFacade, oScope) { var mElements = oScope.getElements(); Object.keys(mElements).forEach(function(sElement) { var oElement = mElements[sElement], mBindingInfos = oElement.mBindingInfos; Object.keys(mBindingInfos).forEach(function(sBindingInfo) { var oBinding = mBindingInfos[sBindingInfo].binding; if (oBinding && !(oBinding instanceof CompositeBinding) && oBinding.getModel && oBinding.getModel()) { var oModel = oBinding.getModel(); //find elements with unresolved PropertyBindings if ((oBinding.getValue && oBinding.getValue() === undefined) || (oBinding instanceof ListBinding && oBinding.getLength() === 0)) { var sJsonModelBestMatch = false; if (oModel instanceof JSONModel) { var oJsonModelResult = oModel.getObject(oBinding.getPath()); if (!oJsonModelResult) { var oData = oModel.getData(); sJsonModelBestMatch = _fnFindBestMatch(Object.keys(oData), oBinding.getPath()); } } else if (oModel.oMetadata && oModel.oMetadata instanceof ODataMetadata) { //try to look it up var result = oModel.oMetadata._getEntityTypeByPath(oBinding.getPath()); if (!result) { var aValues = []; oModel.oMetadata.getServiceMetadata().dataServices.schema.forEach(function(mShema) { if (mShema.entityContainer) { mShema.entityContainer.forEach(function(mContainer) { if (mContainer.entitySet) { mContainer.entitySet.forEach(function(mEntitySet) { if (mEntitySet.name) { aValues.push(mEntitySet.name); } }); } }); } }); sJsonModelBestMatch = _fnFindBestMatch(aValues, oBinding.getPath()); } } if (sJsonModelBestMatch) { oIssueManager.addIssue({ severity: Severity.Medium, details: "Element " + oElement.getId() + " with binding path '" + oBinding.getPath() + "' has unresolved bindings." + " You could try '" + sJsonModelBestMatch + "' instead", context: { id: oElement.getId() } }); } } else if (oBinding.getValue && oBinding.getValue() === oBinding.getPath()) { oIssueManager.addIssue({ severity: Severity.Low, details: "Element " + oElement.getId() + " with binding path '" + oBinding.getPath() + "' has the same value as the path. Potential Error.", context: { id: oElement.getId() } }); } } }); }); } }; /** * Checks whether the select(v2)/$select(v4) parameter is used when binding against an * aggregation. */ var oSelectUsedInAggregation = { audiences : [Audiences.Application], categories : [Categories.Bindings, Categories.Performance], description : "Using $select allows the back end to send only necessary properties", enabled : true, id : "selectUsedInBoundAggregation", minversion : "1.38", resolution : "Use the '$select' binding parameter when binding an aggregation against " + "an OData V4 model, or 'select' in case of an OData V2 model", resolutionurls : [{ href : "https://sdk.openui5.org/topic/408b40efed3c416681e1bd8cdd8910d4#section_useSelectQuery", text : "Documentation: Performance: Speed Up Your App" }, { href : "https://sdk.openui5.org/topic/10ca58b701414f7f93cd97156f898f80", text : "OData V4 only: Automatic determination of $expand and $select" }, { href : "https://sdk.openui5.org/api/sap.ui.model.odata.v4.ODataModel/methods/bindList", text : "Documentation: v4.ODataModel#bindList" }, { href : "https://sdk.openui5.org/api/sap.ui.model.odata.v2.ODataModel/methods/bindList", text : "Documentation: v2.ODataModel#bindList" }], title : "Model: Use the $select/select binding parameter when binding aggregations to " + "improve performance", check : fnCheckSelect }; return [oBindingPathSyntaxValidation, oSelectUsedInAggregation]; }, true);