I am using breezejs v.1.5.4 with OData Web Api controllers (and AngularJS v.1.4.0 if it makes a difference).
I have the following models (simplified):
public partial class Job
{
...
[Required]
[StringLength(128)]
[Index("IX_WorkDoneById")]
[Display(Name = "Work Done By Id")]
public string WorkDoneById { get; set; }
[ForeignKey("WorkDoneById")]
public virtual User WorkDoneBy { get; set; }
}
[DataContract]
public partial class User : IdentityUser
{
...
[Key]
[StringLength(128)]
[Display(Name = "Id")]
[DataMember]
public override string Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
[InverseProperty("WorkDoneBy")]
[DataMember]
public virtual ICollection<Job> Jobs { get; set; }
}
When trying to get a Job information and expanding the WorkDoneBy, it works and I get the user information (i.e. the user is binded to the job). While when I try to get the Jobs associated with the user, I get an empty array. I inspected the network and the Jobs are transmitted with the server response but not attached to the user instance.
My JS query is like this:
var query = new breeze.EntityQuery()
.from("Users")
.expand("Jobs")
.where(new breeze.Predicate("Id", "eq", "Some long Guid"));
Any suggestions ??
Update 1
Also I am using datajs v.1.1.3 and odata service adapter.
Below is the metadata:
{
"metadataVersion": "1.0.5",
"namingConvention": "noChange",
"localQueryComparisonOptions": "caseInsensitiveSQL",
"dataServices": [
{
"serviceName": "odata/",
"adapterName": "odata",
"uriBuilderName": "odata",
"hasServerMetadata": true,
"jsonResultsAdapter": "OData_default",
"useJsonp": false
}
],
"structuralTypes": [
{
"shortName": "Job",
"namespace": "MyApp.Models",
"autoGeneratedKeyType": "None",
"defaultResourceName": "Jobs",
"dataProperties": [
{
"name": "JobId",
"dataType": "Guid",
"isNullable": false,
"defaultValue": "00000000-0000-0000-0000-000000000000",
"isPartOfKey": true,
"validators": [{ "name": "required" }, { "name": "guid" }]
},
{
"name": "WorkDoneById",
"dataType": "String",
"isNullable": false,
"defaultValue": "",
"validators": [{ "name": "required" }, { "name": "string" }]
}
],
"navigationProperties": [
{
"name": "WorkDoneBy",
"entityTypeName": "User:#MyApp.Models",
"isScalar": true,
"associationName": "MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartner"
}
]
},
{
"shortName": "User",
"namespace": "MyApp.Models",
"autoGeneratedKeyType": "None",
"defaultResourceName": "Users",
"dataProperties": [
{
"name": "Id",
"dataType": "String",
"isNullable": false,
"defaultValue": "",
"isPartOfKey": true,
"validators": [{ "name": "required" }, { "name": "string" }]
}
],
"navigationProperties": [
{
"name": "Jobs",
"entityTypeName": "Job:#MyApp.Models",
"isScalar": false,
"associationName": "MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartner"
}
]
}
],
"resourceEntityTypeMap":
{
"Jobs": "Job:#MyApp.Models",
"Users": "User:#MyApp.Models"
}
}
and this is breeze configuration:
var dataService = new breeze.DataService({
adapterName: "odata",
hasServerMetadata: false, // don't ask the server for metadata
serviceName: "odata",
uriBuilderName: "odata",
});
// create the metadataStore
var metadataStore = new breeze.MetadataStore();
// initialize the store from the application's metadata variable
metadataStore.importMetadata(Models.metaData);
// Apply additional functions and properties to the models
metadataStore.registerEntityTypeCtor("Job", Models.Job);
metadataStore.registerEntityTypeCtor("User", Models.User);
// Initializes entity manager.
this.entityManager = new breeze.EntityManager(
{ dataService: dataService, metadataStore: metadataStore }
);
Update 2
Metadata generated from the server odata/$metadata:
<edmx:Edmx Version="1.0">
<edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0">
<Schema Namespace="MyApp.Models">
<EntityType Name="Job">
<Key>
<PropertyRef Name="JobId"/>
</Key>
<Property Name="JobId" Type="Edm.Guid" Nullable="false"/>
<Property Name="WorkDoneById" Type="Edm.String" Nullable="false"/>
<NavigationProperty Name="WorkDoneBy" Relationship="MyApp.Models.MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartner" ToRole="WorkDoneBy" FromRole="WorkDoneByPartner"/>
</EntityType>
<EntityType Name="User">
<Key>
<PropertyRef Name="Id"/>
</Key>
<Property Name="Id" Type="Edm.String" Nullable="false"/>
<NavigationProperty Name="Jobs" Relationship="MyApp.Models.MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartner" ToRole="Jobs" FromRole="JobsPartner"/>
</EntityType>
<Association Name="MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartner">
<End Type="MyApp.Models.User" Role="WorkDoneBy" Multiplicity="0..1"/>
<End Type="MyApp.Models.Job" Role="WorkDoneByPartner" Multiplicity="0..1"/>
</Association>
<Association Name="MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartner">
<End Type="MyApp.Models.Job" Role="Jobs" Multiplicity="*"/>
<End Type="MyApp.Models.User" Role="JobsPartner" Multiplicity="0..1"/>
</Association>
</Schema>
<Schema Namespace="Default">
<EntityContainer Name="Container" m:IsDefaultEntityContainer="true">
<EntitySet Name="Jobs" EntityType="MyApp.Models.Job"/>
<EntitySet Name="Users" EntityType="MyApp.Models.User"/>
<AssociationSet Name="MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartnerSet" Association="MyApp.Models.MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartner">
<End Role="WorkDoneByPartner" EntitySet="Jobs"/>
<End Role="WorkDoneBy" EntitySet="Users"/>
</AssociationSet>
<AssociationSet Name="MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartnerSet" Association="MyApp.Models.MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartner">
<End Role="JobsPartner" EntitySet="Users"/>
<End Role="Jobs" EntitySet="Jobs"/>
</AssociationSet>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
According to your configuration code, you appear to be using locally defined metadata
although it's a bit confusing because the metadata object shown earlier in your question is attached to a dataservice that says "hasServerMetadata": true. I don't know that this inner dataservice actually matters.
I'm not positive but I suspect that the problem is your association names; they are different for the two navigation properties:
// Job
...
"navigationProperties": [
{
"name": "WorkDoneBy",
...
"associationName": "MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartner"
}
]
// User
...
"navigationProperties": [
{
"name": "Jobs",
...
"associationName": "MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartner"
}
]
If you want Breeze to pair up these properties, the associationName must be the same string value. The value itself doesn't matter; just the fact that both property ends of the relationship have the same associationName. That's how Breeze learns that these properties are mated.
Try something nice and short that spells out the underlying relationship ... something like "User_Jobs" or "Job.WorkDoneBy_User.Jobs"
Update #1
It is a mystery how you got those different associationName values.
In a comment I asked that you look at the raw metadata coming from your OData source.
Here is an example from the "ODataBreezejsSample" that gets metadata from an OData v.3 source.
The OData nuget package is "Microsoft.AspNet.WebApi.OData" version="5.2.2". The sample uses the EdmBuilder class as explained in the documentation
There are two types - TodoList and TodoItem - that have a one-to-many relationship. The pertinent raw metadata XML is:
// TodoList
<NavigationProperty Name="TodoItems" Relationship="ODataBreezejsSample.Models.TodoList_TodoItems" ... />
// TodoItem
<NavigationProperty Name="TodoList" Relationship="ODataBreezejsSample.Models.TodoList_TodoItems" ... />
Note they have the same Relationship name: "ODataBreezejsSample.Models.TodoList_TodoItems"
Then I inspect the corresponding navigation properties in the client-side metadata that Breeze produces from this XML. Both properties share the associationName of "TodoList_TodoItems" ... equal to the Relationship name stripped of the namespace.
What kind of OData source (and what OData version) are you querying? Are you using the EdmBuilder class to generate the metadata?
Update #2
So you're using the "Microsoft ASP.NET Web API 2.2 for OData v4.0" v.5.6 nuget package! That means you're using OData v.4.
What a PITA!
This is a huge breaking change from v.5.5.x, the last of the OData v.3 nuget packages.
How could they make that big a leap and re-use the version numbers, especialy the major version digit? It boggles the mind.
To really confuse things, there are now two nuget package names that are just slightly different:
v3: https://www.nuget.org/packages/Microsoft.AspNet.WebApi.OData/
v4: https://www.nuget.org/packages/Microsoft.AspNet.OData/
Did you happen to notice the "WebApi" in the middle of the v.3 package name? I didn't at first.
The bad news is that their implementation of v.4 broke everything ... again ... including metadata. And ... again ... they failed to follow the OData spec, especially w/r/t navigation properties in metadata.
Consequently, Breeze does not yet work with Web API OData v.4 metadata ... and there are other problems as well.
We are in the process of working through the issues with the Microsoft OData team. Until then, you're options are to wait for us or go back to OData v.3.
Also critical: the datajs 3rd party client-side JavaScript library that we have all used for OData v.1-3 does not work with OData v.4. Like everyone else, you'll have to switch to the olingo library. It may be that the olingo library plays a constructive role in rationalizing navigation property metadata. I don't know and our expert on the subject is not available at the moment.
Yes ... this is a mess.
Rather then querying you can use fetchEntityByKey on manager. Which will fetch entity by key.
function getAJobDetail(){
return manager.fetchEntityByKey(
"Jobs", "Some long Guid", true)
.then(fetchSucceeded)
.fail(queryFailed);
function fetchSucceeded(data) {
var s = data.entity;}
function queryFailed(error) {
var msg = '[datacontext.js] Error retrieving data. ' + error.message;
//logError(msg, error);
throw error;
}
}
Note : this method will work only if that your key which is some Guid must be a primary key, else you have to use predicates and compare the fields then query the breeze.
Related
I have dropdown list, this consume a xsodata service in my app SAP UI5. This service working in local mode in SAP Web IDE Rest Full, in the deploy proccess I haven't error but when I test my app deployed the dropdown list not working and I get a error in console.
List Binding is not bound against a list for /MyEntity
When I clic in the dropdown list I get this message:
cdm.js:68 Assertion failed: Type for filter property could not be found in metadata!
It's as if the service xsodata doesn't exist.
PS: This app was deployed in sap cloud foundry.
UPDATE:
My_controller.js
onInit: function () {
oModelHana = new sap.ui.model.odata.ODataModel("xsodata/ServiceHana.xsodata", true);
this.oSelEntity = this.byId("inMyDropdown");
this.oSelEntity.setModel(oModelHana);
}
And my_view.xml
...
<Label text="Entity"/>
<Select id="inMyDropdown" maxWidth="300rem" items="{/Entity}">
<c:Item key="{EntityID}" text="{description}"/>
</Select>
...
Please, can you help me?
What kind of Odata-Service are you using?
sap.ui.model.odata.ODataModel is deprecated since UI5 1.48
So you have to decide between
sap.ui.model.odata.v2.ODataModel
and
sap.ui.model.odata.v4.ODataModel
And why don't you define the Model directly in the manifest.json?
manifest.json
"dataSources": {
"xsodataService": {
"uri": "/xsodata/ServiceHana.xsodata/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
},
"models": {
"": {
"dataSource": "xsodataService",
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"groupId": "$auto",
"autoExpandSelect" : true,
"earlyRequests": true,
"groupProperties": {
"default": {
"submit": "Auto"
}
}
}
},
I'm currently working at a MasterDetail application for my company, which provides expandable categorys represented as nodes.
The binding of the nodes and it's child nodes with a navigation property isn't a problem. However, if I want to search for a certain group node in the search field above, it only filters between the four highest nodes. It can search for the nodes on the first level, but it isn't able to find nodes if they're below the first level.
Binding of the tree:
<Tree
selectionChange="onSelectionChange"
id="list"
noDataText="{masterView>/noDataText}"
busyIndicatorDelay="{masterView>/delay}"
items="{path: '/GroupNodes',
parameters : {
expand: 'ChildGroupNodes',
navigation: {
'GroupNodes': 'ChildGroupNodes'
}
}
}">
<StandardTreeItem
title="{Stext}"
type="Navigation"
press="onSelectionChange"/>
</Tree>
onSearch:
onSearch: function(oEvent) {
if (oEvent.getParameters().refreshButtonPressed) {
this.onRefresh();
return;
}
var sQuery = oEvent.getParameter("query");
if (sQuery) {
this._oListFilterState.aSearch = [new Filter("Stext", FilterOperator.Contains, sQuery)];
} else {
this._oListFilterState.aSearch = [];
}
this._applyFilterSearch();
},
_applyFilterSearch:
_applyFilterSearch: function() {
var aFilters = this._oListFilterState.aSearch.concat(this._oListFilterState.aFilter),
oViewModel = this.getModel();
this._oList.getBinding("items").filter(aFilters, "Application");
if (aFilters.length !== 0) {
oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataWithFilterOrSearchText"));
} else if (this._oListFilterState.aSearch.length > 0) {
oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataText"));
}
},
Filterstate in the onInit() function:
this._oListFilterState = {
aFilter: [],
aSearch: []
};
Metadata:
<EntityType Name="GroupNode" sap:content-version="1">
<Key>
<PropertyRef Name="Grpid"/>
</Key>
<Property Name="Grpid" Type="Edm.String" Nullable="false" MaxLength="8" sap:unicode="false" sap:label="Id Trainingsgruppe" sap:creatable="false" sap:updatable="false" sap:filterable="false"/>
<Property Name="Short" Type="Edm.String" MaxLength="12" sap:unicode="false" sap:label="Kürzel Trainingsgruppe" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
<Property Name="Stext" Type="Edm.String" MaxLength="40" sap:unicode="false" sap:label="Bezeichnung Trainingsgruppe" sap:creatable="false" sap:updatable="false" sap:filterable="false"/>
<Property Name="Begda" Type="Edm.DateTime" Precision="0" sap:unicode="false" sap:label="Beginndatum" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
<Property Name="Endda" Type="Edm.DateTime" Precision="0" sap:unicode="false" sap:label="Endedatum" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
<Property Name="Level" Type="Edm.Int32" sap:unicode="false" sap:label="Level" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
<Property Name="Parentid" Type="Edm.String" Nullable="false" MaxLength="8" sap:unicode="false" sap:label="ParentId" sap:creatable="false" sap:updatable="false" sap:filterable="false"/>
<NavigationProperty Name="ChildGroupNodes" Relationship="Z_HR_LSO_WORKCENTER_SRV.GroupNodeToParent" FromRole="FromRole_GroupNodeToParent" ToRole="ToRole_GroupNodeToParent"/>
<NavigationProperty Name="GroupToTrainingType" Relationship="Z_HR_LSO_WORKCENTER_SRV.GroupToTrainingType" FromRole="FromRole_GroupToTrainingType" ToRole="ToRole_GroupToTrainingType"/>
</EntityType>
We're working with OData V2, so there's no possibility to implement an FilterContains.All filter.
Is it even possible to filter through the child nodes of a sap.m.Tree in the front-end?
First of all, building the tree hierarchy via navigation is deprecated since 1.44. Instead, SAP recommends to leverage metadata annotations:
The use of navigation properties to build up the hierarchy structure is deprecated and it is recommended to use the hierarchy annotations [...].
Once the migration to the annotation approach is done, filter either client-side or server-side.
Client-side
The operationMode of the ODataTreeBinding (or the defaultOperationMode of the v2.ODataModel) needs to be "Client".
When calling .filter, the FilterType as the 2nd argument needs to be "Control".
Here is a working example: https://embed.plnkr.co/moTGOT
Server-side
For server-side filtering, only the FilterType "Application" and the operationMode: "Server" are supported. In that case, the server needs to respond to the $filter request with a ready-made tree structure. The same applies to paging requests for sibling and child nodes.
Limitations
suspended: true in the binding info is not yet supported (Issue #3161).
The above content applies to OData V2 only.
V4 ODataModel doesn't support tree binding at all yet (Issue #2728).
I think this may have something to do with the way you're binding the items, because I am able to filter on child nodes just fine in my example using your JS.
Check that this._oList.getItems() has all the items in it before you do the filter.
I'll post my code, so you can rebuild my project and get a feel for how it works. Let me know if you have more questions.
Page.view.xml
<mvc:View
controllerName="sap.m.sample.Tree.Page"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<SearchField value="{json>/search}" search="onSearch"/>
<Tree
id="Tree"
items="{path: '/'}">
<StandardTreeItem title="{text}"/>
</Tree>
</mvc:View>
Page.controller.js
sap.ui.define(['sap/ui/core/mvc/Controller', 'sap/ui/model/json/JSONModel', 'sap/ui/model/Filter', 'sap/ui/model/FilterOperator'],
function(Controller, JSONModel, Filter, FilterOperator) {
"use strict";
var PageController = Controller.extend("sap.m.sample.Tree.Page", {
onInit: function(evt) {
// set explored app's demo model on this sample
var oModel = new JSONModel(jQuery.sap.getModulePath("sap.m.sample.Tree", "/Tree.json"));
this.getView().setModel(oModel);
var oJSONModel = new JSONModel();
this.getView().setModel("json", oJSONModel);
this._oList = this.byId("Tree");
this._oListFilterState = {
aFilter: [],
aSearch: []
};
},
onSearch: function(oEvent) {
if (oEvent.getParameters().refreshButtonPressed) {
this.onRefresh();
return;
}
var sQuery = oEvent.getParameter("query");
if (sQuery) {
this._oListFilterState.aSearch = [new Filter("text", FilterOperator.Contains, sQuery)];
} else {
this._oListFilterState.aSearch = [];
}
this._applyFilterSearch();
},
_applyFilterSearch: function() {
var aFilters = this._oListFilterState.aSearch.concat(this._oListFilterState.aFilter);
//oViewModel = this.getModel();
this._oList.getBinding("items").filter(aFilters, "Application");
// if (aFilters.length !== 0) {
// oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataWithFilterOrSearchText"));
// } else if (this._oListFilterState.aSearch.length > 0) {
// oViewModel.setProperty("/noDataText", this.getResourceBundle().getText("masterListNoDataText"));
// }
}
});
return PageController;
});
Tree.json
[
{
"text": "Node1",
"ref": "sap-icon://attachment-audio",
"nodes":
[
{
"text": "Node1-1",
"ref": "sap-icon://attachment-e-pub",
"nodes":[
{
"text": "Node1-1-1",
"ref": "sap-icon://attachment-html"
},
{
"text": "Node1-1-2",
"ref": "sap-icon://attachment-photo",
"nodes":[
{
"text": "Node1-1-2-1",
"ref": "sap-icon://attachment-text-file",
"nodes":[
{
"text": "Node1-1-2-1-1",
"ref": "sap-icon://attachment-video"
},
{
"text": "Node1-1-2-1-2",
"ref": "sap-icon://attachment-zip-file"
},
{
"text": "Node1-1-2-1-3",
"ref": "sap-icon://course-program"
}
]
}
]
}
]
},
{
"text": "Node1-2",
"ref": "sap-icon://create"
}
]
},
{
"text": "Node2",
"ref": "sap-icon://customer-financial-fact-sheet"
}
]
That's all you should need, but just incase you need these as well...
index.html
<!DOCTYPE HTML>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<title>Tree - Basic</title>
<script id="sap-ui-bootstrap"
src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m"
data-sap-ui-theme="sap_belize"
data-sap-ui-xx-bindingSyntax="complex"
data-sap-ui-preload="async"
data-sap-ui-compatVersion="edge"
data-sap-ui-resourceroots='{"sap.m.sample.Tree": "./", "sap.ui.demo.mock": "mockdata"}'>
</script>
<!-- Application launch configuration -->
<script>
sap.ui.getCore().attachInit(function() {
new sap.m.App ({
pages: [
new sap.m.Page({
title: "Tree - Basic",
enableScrolling : true,
content: [ new sap.ui.core.ComponentContainer({
name : "sap.m.sample.Tree"
})]
})
]
}).placeAt("content");
});
</script>
</head>
<!-- UI Content -->
<body class="sapUiBody" id="content" role="application">
</body>
Component.js
sap.ui.define(['sap/ui/core/UIComponent'],
function(UIComponent) {
"use strict";
var Component = UIComponent.extend("sap.m.sample.Tree.Component", {
metadata : {
rootView : "sap.m.sample.Tree.Page",
dependencies : {
libs : [
"sap.m",
"sap.ui.layout"
]
},
config : {
sample : {
files : [
"Page.view.xml",
"Page.controller.js",
"Tree.json"
]
}
}
}
});
return Component;
});
Not sure if you managed to solve your problem, but I actually did this exact thing a few months ago.
In your controller, put the following function:
onSearch: function(oEvent) {
var searchVal = oEvent.getParameter("newValue");
var treeFilter = new sap.ui.model.Filter("Text", sap.ui.model.FilterOperator.Contains, searchVal);
var oBinding = this.getView().byId("tree").mBindingInfos.rows.binding;
oBinding.filter(treeFilter, FilterType.Application);
oBinding.expandToLevel(3);
},
And this should work straight away. Whatever you enter in the search box, it will populate the tree (up to 3 levels, but this can be changed) with your filter.
The onSearch function is of course executed on the livechange of the search field.
I have 2 weeks looking for an example to understand how the OData works.
I have defined in the Manifest.json my url with the OData service
{
"_version" : "1.7.0",
"sap.app" : {
"id" : "test",
"type" : "application",
"i18n" : "i18n/i18n.properties",
"applicationVersion": {
"version" : "1.0.0"
},
"title" : "{{appTitle}}",
"description" : "{{appDescription}}",
"sourceTemplate": {
"id" : "servicecatalog.connectivityComponent",
"version" : "0.0.0"
},
"dataSources" : {
"Test" : {
"uri" : "/sap/opu/odata/sap/ZMY_SERVICE_SRV/",
"type" : "OData",
"settings" : {
"odataVersion" : "2.0",
"localUri" : "localService/metadata.xml"
}
}
}
}..
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "test.i18n.i18n"
}
},
"Test": {
"type": "sap.ui.model.odata.v2.ODataModel",
"settings": {
"defaultOperationMode": "Server",
"defaultBindingMode": "TwoWay",
"defaultCountMode": "None"
},
"dataSource": "Test"
},
and in my Component.js within the Init method:
init: function() {
// call the base component's init function
UIComponent.prototype.init.apply(this, arguments);
// create the views based on the url/hash
this.getRouter().initialize();
// set the device model
this.setModel(models.createDeviceModel(), "device");
var sServiceUrl = this.getMetadata().getManifestEntry("sap.app").dataSources["Test"].uri;
var oModel = new sap.ui.model.odata.v2.ODataModel(sServiceUrl);
this.setModel(sabModel, "/Test");
sabModel.read(sServiceUrl, "Test");
}
I don´t want to use a Table or a List to load the OData from Backend. I want to load the information "manually" and based on what I got from the Backend, I want to navigate to one or another View.
Debugging the result in the navigator I see the following error: Console
Checking the Error Log I can see: Error Log
If I test the service in Backend works fine: OData Service
I got:
<?xml version="1.0"?><feed xml:base="http://Myserver:8000/sap/opu/odata/sap/ZMY_SERVICE_SRV/"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
<id>http://Myserver:8000/sap/opu/odata/sap/ZMY_SERVICE_SRV/TestSet</id>
<title type="text">TestSet</title>
<updated>2017-10-23T20:37:55Z</updated>
-<author>
<name/>
</author>
<link title="TestSet" rel="self" href="TestSet"/>
-<entry>
<id>http://Myserver:8000/sap/opu/odata/sap/ZMY_SERVICE_SRV/TestSet('1')</id>
<title type="text">TestSet('1')</title>
<updated>2017-10-23T20:37:55Z</updated>
<category scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" term="ZMY_SERVICE_SRV.Test"/>
<link title="Test" rel="self" href="TestSet('1')"/>
-<content type="application/xml">
-<m:properties xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<d:Pernr>1</d:Pernr>
<d:Nachn>TestUser</d:Nachn>
<d:Vorna>UserTest</d:Vorna>
<d:SavingDate m:null="true"/>
<d:Option/>
<d:MsgType/>
<d:MsgNumb/>
</m:properties>
</content>
</entry>
Thank you for your help!!! Any input is more than welcome!!
try this
var oModel = this.getModel("Test");//get the model
oModel.read("/TestSet", {
method: "GET",
success: function(data) {
alert(JSON.stringify(data));
},
error: function() {
}
});
Read the API documentation about "read" method:
https://sapui5.netweaver.ondemand.com/#/api/sap.ui.model.odata.v2.ODataModel
You should not specify the service URL in read method, because it automatically gets concatenated with "sPath", which you pass as a first argument. You should use the name of entityset (is defined in $metadata) you want to read starting with slash "/".
Recently I wrote restful APIs with SpringMvc and swagger-ui(v2). I noticed the Import function in Postman:
So my question is how to create the file which Postman needed?
I am not familiar with Swagger.
I work on PHP and have used Swagger 2.0 to document the APIs.
The Swagger Document is created on the fly (at least that is what I use in PHP). The document is generated in the JSON format.
Sample document
{
"swagger": "2.0",
"info": {
"title": "Company Admin Panel",
"description": "Converting the Magento code into core PHP and RESTful APIs for increasing the performance of the website.",
"contact": {
"email": "jaydeep1012#gmail.com"
},
"version": "1.0.0"
},
"host": "localhost/cv_admin/api",
"schemes": [
"http"
],
"paths": {
"/getCustomerByEmail.php": {
"post": {
"summary": "List the details of customer by the email.",
"consumes": [
"string",
"application/json",
"application/x-www-form-urlencoded"
],
"produces": [
"application/json"
],
"parameters": [
{
"name": "email",
"in": "body",
"description": "Customer email to ge the data",
"required": true,
"schema": {
"properties": {
"id": {
"properties": {
"abc": {
"properties": {
"inner_abc": {
"type": "number",
"default": 1,
"example": 123
}
},
"type": "object"
},
"xyz": {
"type": "string",
"default": "xyz default value",
"example": "xyz example value"
}
},
"type": "object"
}
}
}
}
],
"responses": {
"200": {
"description": "Details of the customer"
},
"400": {
"description": "Email required"
},
"404": {
"description": "Customer does not exist"
},
"default": {
"description": "an \"unexpected\" error"
}
}
}
},
"/getCustomerById.php": {
"get": {
"summary": "List the details of customer by the ID",
"parameters": [
{
"name": "id",
"in": "query",
"description": "Customer ID to get the data",
"required": true,
"type": "integer"
}
],
"responses": {
"200": {
"description": "Details of the customer"
},
"400": {
"description": "ID required"
},
"404": {
"description": "Customer does not exist"
},
"default": {
"description": "an \"unexpected\" error"
}
}
}
},
"/getShipmentById.php": {
"get": {
"summary": "List the details of shipment by the ID",
"parameters": [
{
"name": "id",
"in": "query",
"description": "Shipment ID to get the data",
"required": true,
"type": "integer"
}
],
"responses": {
"200": {
"description": "Details of the shipment"
},
"404": {
"description": "Shipment does not exist"
},
"400": {
"description": "ID required"
},
"default": {
"description": "an \"unexpected\" error"
}
}
}
}
},
"definitions": {
}
}
This can be imported into Postman as follow.
Click on the 'Import' button in the top left corner of Postman UI.
You will see multiple options to import the API doc. Click on the 'Paste Raw Text'.
Paste the JSON format in the text area and click import.
You will see all your APIs as 'Postman Collection' and can use it from the Postman.
You can also use 'Import From Link'. Here paste the URL which generates the JSON format of the APIs from the Swagger or any other API Document tool.
This is my Document (JSON) generation file. It's in PHP. I have no idea of JAVA along with Swagger.
<?php
require("vendor/autoload.php");
$swagger = \Swagger\scan('path_of_the_directory_to_scan');
header('Content-Type: application/json');
echo $swagger;
With .Net Core it is now very easy:
You go and find JSON URL on your swagger page:
Click that link and copy the URL
Now go to Postman and click Import:
Select what you need and you end up with a nice collection of endpoints:
The accepted answer is correct but I will rewrite complete steps for java.
I am currently using Swagger V2 with Spring Boot 2 and it's straightforward 3 step process.
Step 1: Add required dependencies in pom.xml file. The second dependency is optional use it only if you need Swagger UI.
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
Step 2: Add configuration class
#Configuration
#EnableSwagger2
public class SwaggerConfig {
public static final Contact DEFAULT_CONTACT = new Contact("Usama Amjad", "https://stackoverflow.com/users/4704510/usamaamjad", "hello#email.com");
public static final ApiInfo DEFAULT_API_INFO = new ApiInfo("Article API", "Article API documentation sample", "1.0", "urn:tos",
DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<VendorExtension>());
#Bean
public Docket api() {
Set<String> producesAndConsumes = new HashSet<>();
producesAndConsumes.add("application/json");
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(DEFAULT_API_INFO)
.produces(producesAndConsumes)
.consumes(producesAndConsumes);
}
}
Step 3: Setup complete and now you need to document APIs in controllers
#ApiOperation(value = "Returns a list Articles for a given Author", response = Article.class, responseContainer = "List")
#ApiResponses(value = { #ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "The resource you were trying to reach is not found") })
#GetMapping(path = "/articles/users/{userId}")
public List<Article> getArticlesByUser() {
// Do your code
}
Usage:
You can access your Documentation from http://localhost:8080/v2/api-docs just copy it and paste in Postman to import collection.
Optional Swagger UI: You can also use standalone UI without any other rest client via http://localhost:8080/swagger-ui.html and it's pretty good, you can host your documentation without any hassle.
Recommend you to read this answer
https://stackoverflow.com/a/51072071/4712855
Refer to the https://stackoverflow.com/posts/39072519 answer, and then partially delete the returned content. Finally, it is found that swagger lacks some configuration and postmat cannot be imported.
You need to add the following configuration in swagger
#Bean
public Docket api(SwaggerProperties swaggerProperties) {
swaggerProperties.setTitle("my-project");
swaggerProperties.setDescription("my project");
swaggerProperties.setVersion("v1");
swaggerProperties.getContact().setUrl("http");
//I overlooked other configurations. Note that my swagger configuration lacks these.
}
In short, the attributes in the ApiInfoBuilder class are assigned values as much as possible
spring-boot version:2.3.10.RELEASE
springfox-swagger version: 2.9.2
You can do that: Postman -> Import -> Link -> {root_url}/v2/api-docs
This is what worked for me from the Swagger editor interface:
Method 1
Copy the YAML file contents into the Raw Text area:
Method 2 (more steps)
Step 1: Export the file as JSON
Step 2: Import the JSON file with Postman "Import"
Click on the orange button ("choose files")
Browse to the Swagger doc (swagger.yaml)
After selecting the file, a new collection gets created in POSTMAN. It will contain folders based on your endpoints.
You can also get some sample swagger files online to verify this(if you have errors in your swagger doc).
This issue, with scalar complex properties, was reported earlier and resolved in breeze 1.3.5.
I am still seeing it, with non-scalar complex properties, in breeze 1.4.5. After creating an entity using this metadata, the exportEntities() method on the entity manager fails with an exception in JSON.stringify, complaining about a circular reference.
Here's some code to replicate the problem:
var jsonMetadata = {
"metadataVersion": "1.0.5",
"namingConvention": "camelCase",
"localQueryComparisonOptions": "caseInsensitiveSQL",
"dataServices": [{"serviceName": "breeze/myservice/" } ],
"structuralTypes": [
{
"shortName": "Address",
"namespace": "mynamespace",
"isComplexType": true,
"dataProperties": [
{"name": "street"},
{"name": "city"},
]
},
{
"shortName": "Person",
"namespace": "mynamespace",
"autoGeneratedKeyType": "Identity",
"defaultResourceName": "Person",
"dataProperties": [
{"name": "_id", "dataType": "MongoObjectId", "isNullable": false, "defaultValue": "",
"isPartOfKey": true },
{"name": "displayName", "dataType": "String"},
{ "name": "addresses",
"complexTypeName": "Address:#mynamespace",
"isScalar": false
}
]
}
],
"resourceEntityTypeMap": {
"Person": "Person:#mynamespace"
}};
var manager = new breeze.EntityManager();
manager.metadataStore.importMetadata(jsonMetadata);
var person = manager.createEntity('Person', {displayName: "Joe Bob"});
var myAddresses = person.getProperty('addresses');
var myAddressProp = manager.metadataStore.getEntityType("Address").createInstance(
{street: "Main", city:"Pleasantville"});
myAddresses.push(myAddressProp);
console.log("Complex property is a circular datatype, cannot convert to JSON - that's fine")
//JSON.stringify(person.addresses); // fails with error
console.log("... except that manager.exportEntities() doesn't handle that case!");
var entities = manager.exportEntities(); // also fails
The circular reference that JSON.stringify is complaining about seems to be in the 'parent' property of the ComplexAspect of the Address property.
Also, if there's a simpler way to populate the addresses array, I'd appreciate some help.
Ok, this should be fixed as of Breeze v 1.4.6 ( or later) available now
------------- Original Post ------------------
This is a bug. It will be fixed in the next release, out later this week or early next week. and... thanks for the repro. I will post back when it gets in.