I'm trying to write a web service using spring web service library. I'm able to configure my endpoints successfully and it is working fine, but I'm facing some issues with the exception mappings.
I'm able to map exceptions using #SoapFault and SoapFaultAnnotationExceptionResolver but the wsdl definition is as follows
<xsd:schema elementFormDefault="qualified" targetNamespace="http://abc.com/soap/">
<xsd:complexType name="ServiceException">
<xsd:sequence>
<xsd:element name="message" nillable="true" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ValidationException">
<xsd:complexContent>
<xsd:extension base="tns:ServiceException">
<xsd:sequence/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="InternalException">
<xsd:complexContent>
<xsd:extension base="tns:ServiceException">
<xsd:sequence/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="AuthenticationException">
<xsd:complexContent>
<xsd:extension base="tns:ServiceException">
<xsd:sequence/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="LoginInput">
<xsd:sequence>
<xsd:element minOccurs="1" maxOccurs="1" name="UserName" nillable="false" type="xsd:string"/>
<xsd:element minOccurs="1" maxOccurs="1" name="PassWord" nillable="false" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="LoginOutput">
<xsd:sequence>
<xsd:element minOccurs="1" maxOccurs="1" name="ValidTo" nillable="false" type="xsd:dateTime"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="login" type="tns:LoginInput"/>
<xsd:element name="loginResponse" type="tns:LoginOutput"/>
<xsd:element name="ValidationException" type="tns:ValidationException"/>
<xsd:element name="InternalException" type="tns:InternalException"/>
<xsd:element name="AuthenticationException" type="tns:AuthenticationException"/>
</xsd:schema>
<message name="LoginRequest">
<part name="login" element="tns:login"/>
</message>
<message name="LoginResponse">
<part name="loginResponse" element="tns:loginResponse"/>
</message>
<message name="ValidationException">
<part name="ValidationException" element="tns:ValidationException"/>
</message>
<message name="InternalException">
<part name="InternalException" element="tns:InternalException"/>
</message>
<message name="AuthenticationException">
<part name="AuthenticationException" element="tns:AuthenticationException"/>
</message>
<portType name="ServicePortType">
<operation name="Login">
<input message="tns:LoginRequest"/>
<output message="tns:LoginResponse"/>
<fault name="ValidationException" message="tns:ValidationException"/>
<fault name="InternalException" message="tns:InternalException"/>
<fault name="AuthenticationException" message="tns:AuthenticationException"/>
</operation>
</portType>
<binding name="ServiceBinding" type="tns:ServicePortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="Login">
<soap:operation soapAction="urn://Service#Login"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="ValidationException">
<soap:fault name="ValidationException" use="literal"/>
</fault>
<fault name="InternalException">
<soap:fault name="InternalException" use="literal"/>
</fault>
<fault name="AuthenticationException">
<soap:fault name="AuthenticationException" use="literal"/>
</fault>
</operation>
</binding>
How can I write a exception handling for this service definition?
Thank you
After some more search I found this from spring source forum.
SoapMessage response = (SoapMessage) messageContext.getResponse();
SoapBody soapBody = response.getSoapBody();
SoapFault soapFault =
soapBody.addClientOrSenderFault(ex.getMessage(), Locale.ENGLISH);
SoapFaultDetail faultDetail = soapFault.addFaultDetail();
Result result = faultDetail.getResult();
// My detail XML object
InvalidArgumentFault fault = new InvalidArgumentFault();
fault.setErrorCode("Custom Error Code");
fault.setOpsMessage("This is the ops message");
fault.setSystemMessage("This is the system message");
// Marshal the detail. We have to use the ObjectFactory which isn't
// marshaller agnostic because the detail element doesn't have an
// XmlRootElement tag as required by JAXB.
ObjectFactory of = new ObjectFactory();
mMarshaller.marshal(of.createInvalidArgumentFault( fault), result);
UPDATED
This a complete sample implementations I'm using,
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.xml.transform.Result;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.oxm.Marshaller;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.endpoint.AbstractEndpointExceptionResolver;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapFault;
import org.springframework.ws.soap.SoapFaultDetail;
import org.springframework.ws.soap.SoapMessage;
import org.apache.commons.lang.StringUtils;
public class CustomSoapFaultDetailAnnotationExceptionResolver extends
AbstractEndpointExceptionResolver implements ApplicationContextAware {
private static Log log = LogFactory
.getLog(CustomSoapFaultDetailAnnotationExceptionResolver.class);
private Collection<Marshaller> marshallers;
private Map<Class<? extends Object>, Marshaller> marshallerMap = new HashMap<Class<? extends Object>, Marshaller>();
public CustomSoapFaultDetailAnnotationExceptionResolver() {
setWarnLogCategory(getClass().getCanonicalName());
}
#Override
protected boolean resolveExceptionInternal(MessageContext messageContext,
Object endpoint, Exception ex) {
boolean resolved = false;
try {
CustomSoapFaultDetails annotation = ex.getClass().getAnnotation(
CustomSoapFaultDetails.class);
if (annotation != null) {
Method m = ex.getClass().getMethod("getFaultInfo",
new Class[] {});
Object fault = m.invoke(ex, new Object[] {});
SoapMessage response = (SoapMessage) messageContext
.getResponse();
SoapBody soapBody = response.getSoapBody();
SoapFault soapFault = soapBody
.addClientOrSenderFault(
StringUtils.isBlank(ex.getMessage()) ? "server exception"
: ex.getMessage(), Locale.ENGLISH);
SoapFaultDetail faultDetail = soapFault.addFaultDetail();
Result result = faultDetail.getResult();
if (marshallerMap.containsKey(fault.getClass())) {
marshallerMap.get(fault.getClass()).marshal(fault, result);
resolved = true;
} else {
for (Marshaller marshaller : marshallers) {
try {
marshaller.marshal(fault, result);
marshallerMap.put(fault.getClass(), marshaller);
resolved = true;
break;
} catch (Exception e) {
// Ignore error
}
}
}
}
} catch (Exception e) {
log.error(e.toString(), e);
}
return resolved;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.marshallers = applicationContext.getBeansOfType(Marshaller.class)
.values();
}
}
I have successfully used SOAP Spring-WS to create a web service. Now I was doing the fault implementation in this SOAP service and I was able to do it with a class that I created like the following,
public class ServiceSoapFaultMappingExceptionResolver extends SoapFaultMappingExceptionResolver
and inside the following overridden function,
#Override
protected void customizeFault(Object endpoint, Exception ex, SoapFault fault) {
//code for adding fault details
SoapFaultDefinition soapFaultDefinition = new SoapFaultDefinition();
String ENVELOPE_NAMESPACE_URI = "http://schemas.xmlsoap.org/soap/envelope/";
// soapFaultDefinition.setFaultStringOrReason("--" + ex);
// soapFaultDefinition.setLocale(Locale.ENGLISH);
QName CLIENT_FAULT_NAME = new QName(ENVELOPE_NAMESPACE_URI,"5003", "e");
soapFaultDefinition.setFaultCode(CLIENT_FAULT_NAME);
setDefaultFault(soapFaultDefinition);
Result result = fault.addFaultDetail().getResult();
// marshal
try {
JAXBContext.newInstance(ExceptionListType.class).createMarshaller().marshal(exceptionList, result);
}
Please this answer for the original solution.
Related
In serilog, I need to split the information in two files. i.e one file should contain the Information and the other should contain the error. with the below code and configuration, the information and the errors both displays in both files, instead of filtering out.
Note: I am trying this in .Net6 in Serverside Blazor. Please help us.
Thanks in Advance
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Expressions" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"Enrich": [
"FromLogContext",
"WithMachineName"
],
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "Logs/ex_.log",
"outputTemplate": "{Timestamp:o} [{Level:u3}] ({SourceContext}) {Message:lj}{NewLine}{Exception}",
"rollingInterval": "Day",
"retainedFileCountLimit": 7
}
},
{
"Name": "Logger",
"Args": {
"configureLogger": {
"Filter": [
{
"Name": "ByExcluding",
"Args": {
"expression": "(#Level = 'Information' )"
}
}
],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Logs/ERROR_.log",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
"rollingInterval": "Day",
"retainedFileCountLimit": 7
}
}
]
}
}
}
]
}
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();
WebApplicationBuilder builder;
try
{
Log.Information("Application starting .....");
builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((ctx, lc) => lc
.WriteTo.Console()
.ReadFrom.Configuration(ctx.Configuration));
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddTelerikBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSerilogRequestLogging();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
}
catch (Exception ex)
{
Log.Fatal("Application even failed to start....");
}
You need to make sure the following NuGet packages are installed. You can find this working example code on GitHub here
Serilog,
Serilog.AspNetCore
Serilog.Settings.Configuration
Serilog.Sinks.File, Serilog.Sinks.* (whatever sink type you are
using, relevant package)
Serilog.Expressions,
Serilog.Enrichers.*(this package is an optional choice) if you want to use enrichers.
I am using Blazor Serverside with .Net6 example here.
Program.cs
using Serilog;
var builder = WebApplication.CreateBuilder(args);
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.WriteTo.Console()
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(" #l = 'Debug' or #l = 'Information' or #l = 'Warning'")
.WriteTo.File("Logs/Log-Information-{Date}.log", rollingInterval: RollingInterval.Day))
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(" #l = 'Error' or #l = 'Fatal'")
.WriteTo.File("Logs/Log-Error-{Date}.log", rollingInterval: RollingInterval.Day))
.CreateLogger();
Log.Information("Starting up");
Serilog.Log.Information("Starting application");
Serilog.Log.Error("Error");
Serilog.Log.Fatal("Fatal");
Serilog.Log.Debug("Debug");
// Add services to the container.
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
var app = builder.Build();
Appsettings.json file:
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning",
"Microsoft.Extensions.DependencyInjection": "Warning",
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "{Timestamp:HH:mm:ss} [{Level}] {SourceContext}{NewLine}{Message}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"Path": "./logs/log.txt",
"IncludeScopes": true,
"TimestampFormat": "yyyy-MM-dd HH:mm:ss",
"RollingInterval": "Day"
}
},
{
"Name": "Seq",
"Args": { "serverUrl": "http://localhost:5341"}
}
]
}
The above code snippet will generate two different log files under the "./Logs/" path, per the below screenshot.
Inside the files:
Note: I have added "#using Serilog" into _Imports.razor as well.
My project package references:
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Negotiate" Version="6.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.5" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
<PackageReference Include="Serilog.Expressions" Version="3.3.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.1" />
<PackageReference Include="Serilog.Enrichers.AspNetCore.HttpContext" Version="1.0.1" />
<PackageReference Include="Serilog.Enrichers.AssemblyName" Version="1.0.9" />
<PackageReference Include="Serilog.Enrichers.Context" Version="4.2.0" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.2.0" />
<PackageReference Include="Serilog.Enrichers.Memory" Version="1.0.4" />
<PackageReference Include="Serilog.Enrichers.Process" Version="2.0.2" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="5.7.0" />
</ItemGroup>
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.
In my iOS app I'm trying to get streaming events from EWS following this manual. So first I subscribe for notifications, receive a subscription id and then perform GetStreamingEvents request. The subscription process is successful, however getting streaming events is not. I receive a response only by timeout. Yes, it does contain all the notifications, so no problems with that but I expect to receive a response once an event has occurred not when the request time is up.
So I launched Charles and examined the traffic of Mac Mail app. I can see that it receives one response right after making GetStreamingEvents request and then it successfully receives responses every time an event has occurred as expected. So I made my XML look exactly like Mac Mail app's but still no luck.
This is my Subscribe xml:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<soap:Header>
<t:RequestedServerVersion Version="Exchange2010_SP2" />
</soap:Header>
<soap:Body>
<m:Subscribe xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:StreamingSubscriptionRequest SubscribeToAllFolders="true">
<t:EventTypes>
<t:EventType>CopiedEvent</t:EventType>
<t:EventType>CreatedEvent</t:EventType>
<t:EventType>DeletedEvent</t:EventType>
<t:EventType>ModifiedEvent</t:EventType>
<t:EventType>MovedEvent</t:EventType>
<t:EventType>NewMailEvent</t:EventType>
<t:EventType>FreeBusyChangedEvent</t:EventType>
</t:EventTypes>
</m:StreamingSubscriptionRequest>
</m:Subscribe>
</soap:Body>
</soap:Envelope>
and this is my GetStreamingEvents xml:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<soap:Header>
<t:RequestServerVersion Version="Exchange2010_SP2" />
</soap:Header>
<soap:Body>
<m:GetStreamingEvents xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:SubscriptionIds>
<t:SubscriptionId>JwBkYjVwcjA2bWIxNDY0LmV1cnByZDA2LnByb2Qub3V0bG9vay5jb20QAAAAF9r7tBXj+U+UapGUZ4XFytKbA+ad1dQIEAAAAMiCIP/mQqJOjzx9Aog45Fk=</t:SubscriptionId>
</m:SubscriptionIds>
<m:ConnectionTimeout>30</m:ConnectionTimeout>
</m:GetStreamingEvents>
</soap:Body>
</soap:Envelope>
Any ideas, guys?
UPDATE:
This is how I build my subscription:
bcstring SubscribeRequestMessage::buildSoapRequest() const
{
xml_document<> doc;
xml_node<>* decl = doc.allocate_node(node_declaration);
decl->append_attribute(doc.allocate_attribute("version", "1.0"));
decl->append_attribute(doc.allocate_attribute("encoding", "utf-8"));
doc.append_node(decl);
// Envelope
xml_node<>* envelope = doc.allocate_node(node_element, "soap:Envelope");
envelope->append_attribute(doc.allocate_attribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/"));
envelope->append_attribute(doc.allocate_attribute("xmlns:t", "http://schemas.microsoft.com/exchange/services/2006/types"));
doc.append_node(envelope);
{
// Header
xml_node<>* header = doc.allocate_node(node_element, "soap:Header");
{
xml_node<>* reqServerVersion = doc.allocate_node(node_element, "t:RequestedServerVersion");
xml_attribute<>* serverVersionAttribute = doc.allocate_attribute("Version", m_requestedServerVersion.c_str());
reqServerVersion->append_attribute(serverVersionAttribute);
header->append_node(reqServerVersion);
}
envelope->append_node(header);
// Body
xml_node<>* body = doc.allocate_node(node_element, "soap:Body");
{
xml_node<>* subscribe = doc.allocate_node(node_element, "m:Subscribe");
{
subscribe->append_attribute(doc.allocate_attribute("xmlns:m", "http://schemas.microsoft.com/exchange/services/2006/messages"));
xml_node<>* streamingSubscriptionRequest = doc.allocate_node(node_element, "m:StreamingSubscriptionRequest");
{
if (m_folderIds.size() > 0)
{
xml_node<>* folderIds = doc.allocate_node(node_element, "t:FolderIds");
{
for (const bcstring& folderId : m_folderIds)
{
xml_node<>* folderIdNode = doc.allocate_node(node_element, "t:FolderId");
folderIdNode->append_attribute(doc.allocate_attribute("Id", folderId.c_str()));
folderIds->append_node(folderIdNode);
}
}
streamingSubscriptionRequest->append_node(folderIds);
}
else
{
xml_attribute<>* subscribeToAll = doc.allocate_attribute("SubscribeToAllFolders", "true");
streamingSubscriptionRequest->append_attribute(subscribeToAll);
}
xml_node<>* eventTypes = doc.allocate_node(node_element, "t:EventTypes");
{
for (const bcstring& eventType : m_eventTypes)
{
xml_node<>* eventTypeNode = doc.allocate_node(node_element, "t:EventType");
xml_node<>* eventTypeValueNode = doc.allocate_node(node_data, "", eventType.c_str());
eventTypeNode->append_node(eventTypeValueNode);
eventTypes->append_node(eventTypeNode);
}
}
streamingSubscriptionRequest->append_node(eventTypes);
}
subscribe->append_node(streamingSubscriptionRequest);
}
body->append_node(subscribe);
}
envelope->append_node(body);
}
bcstring retVal;
print(std::back_inserter(retVal), doc, print_no_indenting);
return retVal;
}
and this is how I build get streaming xml:
bcstring GetStreamingEventsRequest::buildSoapRequest() const
{
xml_document<> doc;
xml_node<>* decl = doc.allocate_node(node_declaration);
decl->append_attribute(doc.allocate_attribute("version", "1.0"));
decl->append_attribute(doc.allocate_attribute("encoding", "utf-8"));
doc.append_node(decl);
// Envelope
xml_node<>* envelope = doc.allocate_node(node_element, "soap:Envelope");
envelope->append_attribute(doc.allocate_attribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/"));
envelope->append_attribute(doc.allocate_attribute("xmlns:t", "http://schemas.microsoft.com/exchange/services/2006/types"));
doc.append_node(envelope);
{
// Header
xml_node<>* header = doc.allocate_node(node_element, "soap:Header");
{
xml_node<>* reqServerVersion = doc.allocate_node(node_element, "t:RequestServerVersion");
xml_attribute<>* serverVersionAttribute = doc.allocate_attribute("Version", m_requestedServerVersion.c_str());
reqServerVersion->append_attribute(serverVersionAttribute);
header->append_node(reqServerVersion);
}
envelope->append_node(header);
// Body
xml_node<>* body = doc.allocate_node(node_element, "soap:Body");
{
xml_node<>* getStreamingEvents = doc.allocate_node(node_element, "m:GetStreamingEvents");
{
getStreamingEvents->append_attribute(doc.allocate_attribute("xmlns:m", "http://schemas.microsoft.com/exchange/services/2006/messages"));
xml_node<>* subscriptionIds = doc.allocate_node(node_element, "m:SubscriptionIds");
{
for (const bcstring& subscriptionId : m_subscriptionIds)
{
xml_node<>* subscriptionIdNode = doc.allocate_node(node_element, "t:SubscriptionId");
xml_node<>* subscriptionIdValue = doc.allocate_node(node_data, "", subscriptionId.c_str());
subscriptionIdNode->append_node(subscriptionIdValue);
subscriptionIds->append_node(subscriptionIdNode);
}
}
getStreamingEvents->append_node(subscriptionIds);
xml_node<>* connectionTimeout = doc.allocate_node(node_element, "m:ConnectionTimeout");
bcstring connectionTimeoutString = std::to_string(m_connectionTimeout);
xml_node<>* connectionTimeoutValue = doc.allocate_node(node_data, "", connectionTimeoutString.c_str());
connectionTimeout->append_node(connectionTimeoutValue);
getStreamingEvents->append_node(connectionTimeout);
}
body->append_node(getStreamingEvents);
}
envelope->append_node(body);
}
bcstring retVal;
print(std::back_inserter(retVal), doc, print_no_indenting);
return retVal;
}
UPDATE:
This is the XML of Mac Mail app that I see via Charles:
Subscription:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<soap:Header>
<t:RequestServerVersion Version="Exchange2010_SP2" />
</soap:Header>
<soap:Body>
<m:Subscribe xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:StreamingSubscriptionRequest SubscribeToAllFolders="true">
<t:EventTypes>
<t:EventType>CopiedEvent</t:EventType>
<t:EventType>CreatedEvent</t:EventType>
<t:EventType>DeletedEvent</t:EventType>
<t:EventType>ModifiedEvent</t:EventType>
<t:EventType>MovedEvent</t:EventType>
<t:EventType>NewMailEvent</t:EventType>
<t:EventType>FreeBusyChangedEvent</t:EventType>
</t:EventTypes>
</m:StreamingSubscriptionRequest>
</m:Subscribe>
</soap:Body>
</soap:Envelope>
and streaming:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<soap:Header>
<t:RequestServerVersion Version="Exchange2010_SP2" />
</soap:Header>
<soap:Body>
<m:GetStreamingEvents xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:SubscriptionIds>
<t:SubscriptionId>JwBkYjVwcjA2bWIxNDY0LmV1cnByZDA2LnByb2Qub3V0bG9vay5jb20QAAAADv7qpS0xbU6V0mCxt2SvFHYOYoCq1dQIEAAAAMiCIP/mQqJOjzx9Aog45Fk=</t:SubscriptionId>
</m:SubscriptionIds>
<m:ConnectionTimeout>30</m:ConnectionTimeout>
</m:GetStreamingEvents>
</soap:Body>
</soap:Envelope>
and this is the response from EWS arriving upon timeout and containing a notification about a new mail:
<Envelope
xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<soap11:Header
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<ServerVersionInfo
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="15" MinorVersion="1" MajorBuildNumber="1282" MinorBuildNumber="22" Version="V2017_04_14"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
</soap11:Header>
<soap11:Body
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<m:GetStreamingEventsResponse
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:ResponseMessages>
<m:GetStreamingEventsResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:ConnectionStatus>OK</m:ConnectionStatus>
</m:GetStreamingEventsResponseMessage>
</m:ResponseMessages>
</m:GetStreamingEventsResponse>
</soap11:Body>
</Envelope>
<Envelope
xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<soap11:Header
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<ServerVersionInfo
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="15" MinorVersion="1" MajorBuildNumber="1282" MinorBuildNumber="22" Version="V2017_04_14"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
</soap11:Header>
<soap11:Body
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<m:GetStreamingEventsResponse
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:ResponseMessages>
<m:GetStreamingEventsResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:Notifications>
<m:Notification>
<t:SubscriptionId>JwBkYjVwcjA2bWIxNDY0LmV1cnByZDA2LnByb2Qub3V0bG9vay5jb20QAAAA4mbQZqHZf0aA35Z5r1UEvPZtJfmw1dQIEAAAAMiCIP/mQqJOjzx9Aog45Fk=</t:SubscriptionId>
<t:CreatedEvent>
<t:TimeStamp>2017-07-28T12:06:04Z</t:TimeStamp>
<t:ItemId Id="AAMkAGZmMjA4MmM4LTQyZTYtNGVhMi04ZjNjLTdkMDI4ODM4ZTQ1OQBGAAAAAADKa2Su6/EXRrsmOefDSCL3BwBEITO0krAyRbRmLsC3JNeGAAAAAAEMAABEITO0krAyRbRmLsC3JNeGAAAdPAOsAAA=" ChangeKey="CQAAAA==" />
<t:ParentFolderId Id="AAMkAGZmMjA4MmM4LTQyZTYtNGVhMi04ZjNjLTdkMDI4ODM4ZTQ1OQAuAAAAAADKa2Su6/EXRrsmOefDSCL3AQBEITO0krAyRbRmLsC3JNeGAAAAAAEMAAA=" ChangeKey="AQAAAA==" />
</t:CreatedEvent>
<t:NewMailEvent>
<t:TimeStamp>2017-07-28T12:06:04Z</t:TimeStamp>
<t:ItemId Id="AAMkAGZmMjA4MmM4LTQyZTYtNGVhMi04ZjNjLTdkMDI4ODM4ZTQ1OQBGAAAAAADKa2Su6/EXRrsmOefDSCL3BwBEITO0krAyRbRmLsC3JNeGAAAAAAEMAABEITO0krAyRbRmLsC3JNeGAAAdPAOsAAA=" ChangeKey="CQAAAA==" />
<t:ParentFolderId Id="AAMkAGZmMjA4MmM4LTQyZTYtNGVhMi04ZjNjLTdkMDI4ODM4ZTQ1OQAuAAAAAADKa2Su6/EXRrsmOefDSCL3AQBEITO0krAyRbRmLsC3JNeGAAAAAAEMAAA=" ChangeKey="AQAAAA==" />
</t:NewMailEvent>
<t:ModifiedEvent>
<t:TimeStamp>2017-07-28T12:06:04Z</t:TimeStamp>
<t:FolderId Id="AAMkAGZmMjA4MmM4LTQyZTYtNGVhMi04ZjNjLTdkMDI4ODM4ZTQ1OQAuAAAAAADKa2Su6/EXRrsmOefDSCL3AQBEITO0krAyRbRmLsC3JNeGAAAAAAEMAAA=" ChangeKey="AQAAAA==" />
<t:ParentFolderId Id="AAMkAGZmMjA4MmM4LTQyZTYtNGVhMi04ZjNjLTdkMDI4ODM4ZTQ1OQAuAAAAAADKa2Su6/EXRrsmOefDSCL3AQBEITO0krAyRbRmLsC3JNeGAAAAAAEIAAA=" ChangeKey="AQAAAA==" />
<t:UnreadCount>1</t:UnreadCount>
</t:ModifiedEvent>
</m:Notification>
</m:Notifications>
</m:GetStreamingEventsResponseMessage>
</m:ResponseMessages>
</m:GetStreamingEventsResponse>
</soap11:Body>
</Envelope>
<Envelope
xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<soap11:Header
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<ServerVersionInfo
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="15" MinorVersion="1" MajorBuildNumber="1282" MinorBuildNumber="22" Version="V2017_04_14"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
</soap11:Header>
<soap11:Body
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<m:GetStreamingEventsResponse
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:ResponseMessages>
<m:GetStreamingEventsResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:ConnectionStatus>OK</m:ConnectionStatus>
</m:GetStreamingEventsResponseMessage>
</m:ResponseMessages>
</m:GetStreamingEventsResponse>
</soap11:Body>
</Envelope>
<Envelope
xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<soap11:Header
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<ServerVersionInfo
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="15" MinorVersion="1" MajorBuildNumber="1282" MinorBuildNumber="22" Version="V2017_04_14"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
</soap11:Header>
<soap11:Body
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<m:GetStreamingEventsResponse
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:ResponseMessages>
<m:GetStreamingEventsResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:ConnectionStatus>OK</m:ConnectionStatus>
</m:GetStreamingEventsResponseMessage>
</m:ResponseMessages>
</m:GetStreamingEventsResponse>
</soap11:Body>
</Envelope>
<Envelope
xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<soap11:Header
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<ServerVersionInfo
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="15" MinorVersion="1" MajorBuildNumber="1282" MinorBuildNumber="22" Version="V2017_04_14"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
</soap11:Header>
<soap11:Body
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<m:GetStreamingEventsResponse
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:ResponseMessages>
<m:GetStreamingEventsResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:Notifications>
<m:Notification>
<t:SubscriptionId>JwBkYjVwcjA2bWIxNDY0LmV1cnByZDA2LnByb2Qub3V0bG9vay5jb20QAAAA4mbQZqHZf0aA35Z5r1UEvPZtJfmw1dQIEAAAAMiCIP/mQqJOjzx9Aog45Fk=</t:SubscriptionId>
<t:ModifiedEvent>
<t:TimeStamp>2017-07-28T12:07:39Z</t:TimeStamp>
<t:ItemId Id="AAMkAGZmMjA4MmM4LTQyZTYtNGVhMi04ZjNjLTdkMDI4ODM4ZTQ1OQBGAAAAAADKa2Su6/EXRrsmOefDSCL3BwBEITO0krAyRbRmLsC3JNeGAAAAAAEMAABEITO0krAyRbRmLsC3JNeGAAAdPAOsAAA=" ChangeKey="CQAAAA==" />
<t:ParentFolderId Id="AAMkAGZmMjA4MmM4LTQyZTYtNGVhMi04ZjNjLTdkMDI4ODM4ZTQ1OQAuAAAAAADKa2Su6/EXRrsmOefDSCL3AQBEITO0krAyRbRmLsC3JNeGAAAAAAEMAAA=" ChangeKey="AQAAAA==" />
</t:ModifiedEvent>
<t:ModifiedEvent>
<t:TimeStamp>2017-07-28T12:07:39Z</t:TimeStamp>
<t:FolderId Id="AAMkAGZmMjA4MmM4LTQyZTYtNGVhMi04ZjNjLTdkMDI4ODM4ZTQ1OQAuAAAAAADKa2Su6/EXRrsmOefDSCL3AQBEITO0krAyRbRmLsC3JNeGAAAAAAEMAAA=" ChangeKey="AQAAAA==" />
<t:ParentFolderId Id="AAMkAGZmMjA4MmM4LTQyZTYtNGVhMi04ZjNjLTdkMDI4ODM4ZTQ1OQAuAAAAAADKa2Su6/EXRrsmOefDSCL3AQBEITO0krAyRbRmLsC3JNeGAAAAAAEIAAA=" ChangeKey="AQAAAA==" />
<t:UnreadCount>0</t:UnreadCount>
</t:ModifiedEvent>
</m:Notification>
</m:Notifications>
</m:GetStreamingEventsResponseMessage>
</m:ResponseMessages>
</m:GetStreamingEventsResponse>
</soap11:Body>
</Envelope>
<Envelope
xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<soap11:Header
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<ServerVersionInfo
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="15" MinorVersion="1" MajorBuildNumber="1282" MinorBuildNumber="22" Version="V2017_04_14"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />
</soap11:Header>
<soap11:Body
xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<m:GetStreamingEventsResponse
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:ResponseMessages>
<m:GetStreamingEventsResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:ConnectionStatus>Closed</m:ConnectionStatus>
</m:GetStreamingEventsResponseMessage>
</m:ResponseMessages>
</m:GetStreamingEventsResponse>
</soap11:Body>
</Envelope>
"GetStreamingEvents" responses really are streamed.
This means that the http connection stays open, and the notifications are sent through it in more of a continuous flow than a standard request/response mechanism. This is similar to the way streaming a video works. You don't do a single request for the entire video. Instead, you would read the streamed data from the video as it is being sent, and process it accordingly.
You need to handle responses to "GetStreamingEvents" in a similar fashion.
The details provided in the question don't show how you are handling the responses, but I'm guessing that you are using an API that simply fetches a URL and returns a response. That works great for standard requests like the "StreamingSubscriptionRequest", but it won't work for streamed responses.
APIs that simply fetch a url won't return until a complete response is received and/or the connection is closed. That's why it seems like you're not receiving the streamed events until the timeout expires.
In fact, you are receiving those events, you're just not reading them and processing them as they come in.
To do that, you'll need to use an API that allows to you connect to an http server and read from that connection as the data arrives. For iOS, this is as simple as implementing the [connection didReceiveData:] delegate method on NSURLConnection. Presumably, there is some similar higher-level mechanism that can be used in your environment.
I also ran into this problem once, but I don't know if it is the same for you. I was using EWS over HTTP with chunked transfer encoding and also received responses only at timeout. It turned out that a terminating 0 chunk was only sent at timeout, not after each response, therefore I was always waiting for more data until the timeout.
Note that it is not too bad to have a low timeout since you might want to use multiple subscriptions in a single GetEvents request and you can't add or remove subscriptions from a running GetEvents request.
I'm currently trying to build a composite component, and this is how i make use of my component :
Include it with xmlns:albert="http://java.sun.com/jsf/composite/albert"
And here's the usage example
<albert:infoButton
infoId="infoSingleRecord"
params="transDateFrom transDateTo"
mappingMethod="#{tBrowseBean_ConfirmedRPB.mapSendInfoSingleRecord}" />
And this is the component which is put in resources/albert/infoButton.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="infoId" required="true" />
<composite:attribute name="params" />
<composite:attribute name="mappingMethod" method-signature="java.lang.String action()" />
</composite:interface>
<composite:implementation>
<p:commandButton
process="#this #{cc.attrs.params}"
actionListener="#{cc.attrs.mappingMethod}"
update="#{cc.attrs.infoId}Panel"
oncomplete="#{cc.attrs.infoId}Dialog.show()"
image="ui-icon ui-icon-search" />
</composite:implementation>
</html>
But running it, when clicking the infoButton, this exception trace shows in my catalina.out log file :
Apr 25, 2011 10:08:43 AM javax.faces.event.MethodExpressionActionListener processAction
SEVERE: Received 'javax.el.PropertyNotFoundException' when invoking action listener '#{cc.attrs.mappingMethod}' for component 'j_idt71'
Apr 25, 2011 10:08:43 AM javax.faces.event.MethodExpressionActionListener processAction
SEVERE: javax.el.PropertyNotFoundException: /TBrowse_tabConfirmedRPB.xhtml #33,77 mappingMethod="#{tBrowseBean_ConfirmedRPB.mapSendInfoSingleRecord}": Property 'mapSendInfoSingleRecord' not found on type mywebapp.paymentplan.TBrowseBean_ConfirmedRPB
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:111)
at com.sun.faces.facelets.el.ContextualCompositeMethodExpression.invoke(ContextualCompositeMethodExpression.java:184)
at com.sun.faces.facelets.tag.TagAttributeImpl$AttributeLookupMethodExpression.invoke(TagAttributeImpl.java:450)
at javax.faces.event.MethodExpressionActionListener.processAction(MethodExpressionActionListener.java:148)
at javax.faces.event.ActionEvent.processListener(ActionEvent.java:88)
at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:777)
at javax.faces.component.UICommand.broadcast(UICommand.java:300)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:787)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1252)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:306)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:541)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:383)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:243)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:188)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:288)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
ERROR BusinessExceptionHandler - BusinessExceptioHandler handles an unhandled exception:
javax.faces.event.AbortProcessingException: /TBrowse_tabConfirmedRPB.xhtml #33,77 mappingMethod="#{tBrowseBean_ConfirmedRPB.mapSendInfoSingleRecord}": Property 'mapSendInfoSingleRecord' not found on type mywebapp.paymentplan.TBrowseBean_ConfirmedRPB
at javax.faces.event.MethodExpressionActionListener.processAction(MethodExpressionActionListener.java:182)
at javax.faces.event.ActionEvent.processListener(ActionEvent.java:88)
at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:777)
at javax.faces.component.UICommand.broadcast(UICommand.java:300)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:787)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1252)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:306)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:541)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:383)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:243)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:188)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:288)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
Caused by: javax.el.PropertyNotFoundException: /TBrowse_tabConfirmedRPB.xhtml #33,77 mappingMethod="#{tBrowseBean_ConfirmedRPB.mapSendInfoSingleRecord}": Property 'mapSendInfoSingleRecord' not found on type mywebapp.paymentplan.TBrowseBean_ConfirmedRPB
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:111)
at com.sun.faces.facelets.el.ContextualCompositeMethodExpression.invoke(ContextualCompositeMethodExpression.java:184)
at com.sun.faces.facelets.tag.TagAttributeImpl$AttributeLookupMethodExpression.invoke(TagAttributeImpl.java:450)
at javax.faces.event.MethodExpressionActionListener.processAction(MethodExpressionActionListener.java:148)
... 24 more
I wonder why is this happening, because at the mywebapp.paymentplan.TBrowseBean_ConfirmedRPB, i already provide this :
public String mapSendInfoSingleRecord(ActionEvent event) {
System.out.println("mapSendInfoSingleRecord");
return null;
}
And after that, i tried putting the dummy mapSendInfoSingleRecord field, and see what happens :
private String mapSendInfoSingleRecord;
public String getMapSendInfoSingleRecord() {
return mapSendInfoSingleRecord;
}
public void setMapSendInfoSingleRecord(String mapSendInfoSingleRecord) {
this.mapSendInfoSingleRecord = mapSendInfoSingleRecord;
}
But clicking on the button now produces this exception :
Apr 25, 2011 10:32:46 AM javax.faces.event.MethodExpressionActionListener processAction
SEVERE: Received 'javax.el.MethodNotFoundException' when invoking action listener '#{cc.attrs.mappingMethod}' for component 'j_idt71'
Apr 25, 2011 10:32:46 AM javax.faces.event.MethodExpressionActionListener processAction
SEVERE: javax.el.MethodNotFoundException: Method not found: com.sun.faces.el.CompositeComponentAttributesELResolver$ExpressionEvalMap#12f3802.mappingMethod()
at org.apache.el.util.ReflectionUtil.getMethod(ReflectionUtil.java:225)
at org.apache.el.parser.AstValue.invoke(AstValue.java:253)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:278)
at javax.faces.event.MethodExpressionActionListener.processAction(MethodExpressionActionListener.java:153)
at javax.faces.event.ActionEvent.processListener(ActionEvent.java:88)
at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:777)
at javax.faces.component.UICommand.broadcast(UICommand.java:300)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:787)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1252)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:306)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:541)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:383)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:243)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:188)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:288)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
ERROR BusinessExceptionHandler - BusinessExceptioHandler handles an unhandled exception:
javax.faces.event.AbortProcessingException: Method not found: com.sun.faces.el.CompositeComponentAttributesELResolver$ExpressionEvalMap#12f3802.mappingMethod()
at javax.faces.event.MethodExpressionActionListener.processAction(MethodExpressionActionListener.java:182)
at javax.faces.event.ActionEvent.processListener(ActionEvent.java:88)
at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:777)
at javax.faces.component.UICommand.broadcast(UICommand.java:300)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:787)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1252)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:306)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:541)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:383)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:243)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:188)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:288)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
Caused by: javax.el.MethodNotFoundException: Method not found: com.sun.faces.el.CompositeComponentAttributesELResolver$ExpressionEvalMap#12f3802.mappingMethod()
at org.apache.el.util.ReflectionUtil.getMethod(ReflectionUtil.java:225)
at org.apache.el.parser.AstValue.invoke(AstValue.java:253)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:278)
at javax.faces.event.MethodExpressionActionListener.processAction(MethodExpressionActionListener.java:153)
... 24 more
I've tested against mojarra version 2.0.4-b09 and 2.1.1-b04, with the same results.
I wonder what I did wrong here ?
There are two problems:
First, the composite method-signature which you've definied
<composite:attribute name="mappingMethod" method-signature="java.lang.String action()" />
doesn't match with the real method signature
public String mapSendInfoSingleRecord(ActionEvent event)
Second, the actionListener methods should return void not String.
I assume that you just want a action method, you should then change the real method signature to get rid of the ActionEvent argument:
public String mapSendInfoSingleRecord()
As to actionListener vs action, you may find this question/answer useful: Differences between action and actionListener.
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.