UI5 Mock Server with Local Data: "rootUri" Not Working - odata

I'm currently following the UI5 tutorial and am stuck on step 27: Mock Server Configuration.
The problem is the rootUri configuration of the mock server. I am using the Northwind OData service as per tutorial and have configured a dataSource for Invoices in the manifest.json.
Now, to use the local mock data instead of the online service, I created the necessary files as stated by the tutorial. When I then run the mockServer.html, the server doesn't redirect the service request to the local mock data, but instead makes the request and fails because of web security issues.
If I use the following rootUri, the mock server doesn't redirect and the service request fails:
// Snippet from mockserver.js
var oMockServer = new MockServer({
rootUri: "/destinations/northwind/V2/Northwind/Northwind.svc/"
});
Failed to load https://services.odata.org/V2/Northwind/Northwind.svc/$metadata?sap-language=DE:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'https://webidetesting9515320-s0015250556trial.dispatcher.hanatrial.ondemand.com' is therefore not allowed access.
The response had HTTP status code 501.
Another question on Stackoverflow shows the mock server using a "wildcard" root-uri, but this also fails:
// Snippet from mockserver.js
var oMockServer = new MockServer({
rootUri: "/"
});
The only way I can make the mock server configuration work is to use the exact same URL as the rootUri that is written in the manifest.json as the URI of the dataSource I want to mock:
// Snippet from mockserver.js
var oMockServer = new MockServer({
rootUri: "https://services.odata.org/V2/Northwind/Northwind.svc/"
});
The above code works, but the Web IDE states that this is a bad practice:
error: Fiori Architectural Guidelines: ESLint(sap-no-hardcoded-url): Hardcoded (non relative) URL found. (img)
My question now is: How can I make the mock server run the intended way with a relative rootUri?
webapp/manifest.json (excerpt)
{
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "sap.ui.tutorial.walkthrough",
"type": "application",
"i18n": "i18n/i18n.properties",
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"applicationVersion": {
"version": "1.0.0"
},
"dataSources": {
"invoiceRemote": {
"uri": "https://services.odata.org/V2/Northwind/Northwind.svc/",
"type": "OData",
"settings": {
"odataVersion": "2.0"
}
}
}
},
...
webapp/test/mockServer.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<title>Hello World App - Test Page</title>
<script src="/resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-theme="sap_belize"
data-sap-ui-libs="sap.m"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"
data-sap-ui-resourceroots='{
"sap.ui.tutorial.walkthrough": "../"
}'></script>
<script type="text/javascript">
sap.ui.getCore().attachInit(function() {
sap.ui.require([
"sap/ui/tutorial/walkthrough/localService/mockserver",
"sap/m/Shell",
"sap/ui/core/ComponentContainer"
], function(mockserver, Shell, ComponentContainer) {
mockserver.init();
new Shell({
app: new ComponentContainer({
name: "sap.ui.tutorial.walkthrough",
height: "100%"
})
}).placeAt("content")
});
});
</script>
</head>
<body class="sapUiBody" id="content">
</body>
</html>
webapp/localService/mockserver.js
sap.ui.define([
"sap/ui/core/util/MockServer"
], function (MockServer) {
"use strict";
return {
init: function () {
// create
var oMockServer = new MockServer({
rootUri: "https://services.odata.org/V2/Northwind/Northwind.svc/"
});
var oUriParameters = jQuery.sap.getUriParameters();
// configure
MockServer.config({
autoRespond: true,
autoRespondAfter: oUriParameters.get("serverDelay") || 1000
});
// simulate
var sPath = jQuery.sap.getModulePath("sap.ui.tutorial.walkthrough.localService");
oMockServer.simulate(sPath + "/metadata.xml", sPath + "/mockdata");
// start
oMockServer.start();
}
};
});
webapp/localService/metadata.xml
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
<edmx:DataServices m:DataServiceVersion="1.0" m:MaxDataServiceVersion="3.0"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<Schema Namespace="NorthwindModel" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
<EntityType Name="Invoice">
<Key>
<PropertyRef Name="ProductName"/>
<PropertyRef Name="Quantity"/>
<PropertyRef Name="ShipperName"/>
</Key>
<Property Name="ShipperName" Type="Edm.String" Nullable="false" MaxLength="40" FixedLength="false"
Unicode="true"/>
<Property Name="ProductName" Type="Edm.String" Nullable="false" MaxLength="40" FixedLength="false"
Unicode="true"/>
<Property Name="Quantity" Type="Edm.Int16" Nullable="false"/>
<Property Name="ExtendedPrice" Type="Edm.Decimal" Precision="19" Scale="4"/>
</EntityType>
</Schema>
<Schema Namespace="ODataWebV2.Northwind.Model" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
<EntityContainer Name="NorthwindEntities" m:IsDefaultEntityContainer="true" p6:LazyLoadingEnabled="true"
xmlns:p6="http://schemas.microsoft.com/ado/2009/02/edm/annotation">
<EntitySet Name="Invoices" EntityType="NorthwindModel.Invoice"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

There are some rules to watch out for when it comes to defining the rootUri for mock server.
The rootUri
Should be relative
Has to end with a slash ("/")
Has to match with the URI that is assigned to your model.
It is mentioned in the step 27:
(The rootUri) matches the URL of our data source in the descriptor file.
As well as in the topic Mock Server: Frequently Asked Questions:
The root URI has to be relative and requires a trailing '/'. It also needs to match the URI set in OData/JSON models or simple XHR calls in order for the mock server to intercept them.
So it doesn't matter how your rootUri is defined as long as it fulfills those three requirements mentioned above. That's why some arbitrary URIs like rootUri: "/" works as well but only if the uri in the dataSource is the same.
In your case, changing the rootUri value like this below should make the mock server running:
var oMockServer = new MockServer({
rootUri: "/destinations/northwind/V2/Northwind/Northwind.svc/"
});
And in your app descriptor (manifest.json) respectively..:
"dataSources": {
"invoiceRemote": {
"uri": "/destinations/northwind/V2/Northwind/Northwind.svc/",
"type": "OData",
"settings": {
"odataVersion": "2.0"
}
}
}
To make the path work in a non-MockServer scenario, you'll need to register a corresponding destination in SAP Cloud Platform and edit neo-app.json in your project accordingly.
No need to change the application code.

Some details of chapter 27 of the tutorial are quite misleading.
The rootUri of the MockServer must match the uri parameter of the datasource in manifest.json.
Instead of changing datasource's uri to the (wrong) rootUri of MockServer given in the tutorial, you should actually change MockServer's rootUri to the URI of the external source. In webapp/localService/mockserver.js use this corrected block:
var oMockServer = new MockServer({
rootUri: "https://services.odata.org/V2/Northwind/Northwind.svc/"
});
This will create a MockServer that intercepts all calls to that external URI and responds to them locally. And with this construct, it is actually possible to access the MockServer through /webapp/test/mockServer.html and the live data server through /webapp/index.html.
Hint:
You will most likely still have trouble accessing the original (external) data source using /webapp/index.html due to Same Origin Policy restrictions (SOP). With Google Chrome this can be nicely solved by running a second (!) instance of the browser without SOP. This is possible in parallel to other open browser windows of the standard instance WITH SOP, so you don't need to close all your open browser windows. See this answer for details.

Related

Adding configuration options to a basic JIRA gadget

This line in the Atlassian documentation leads me to believe that simply including a UserPref element should be sufficient to automatically generate the UI for updating the configuration of a gadget:
The container handles generation of the configuration UI, saves the settings and provides an API to access the settings in JavaScript.
But it doesn't seem to actually work when I create a test gadget with the following - I don't see any options when I click the "Edit" link on the gadget:
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs
title="Test"
author="Test Test"
author_email="test#example.com"
directory_title="JIRA Tests"
>
<Require feature="setprefs" />
</ModulePrefs>
<UserPref name="show_summ" display_name="Show Summaries?" datatype="bool" default_value="true"/>
<Content type="html">
<![CDATA[
<div id="main">Test Stuff</div>
]]>
</Content>
</Module>
Is there more to do to make that happen? I've seen a few examples that hard-code configuration options, but the above documentation suggest that you don't have to.
Is that something that's only available if you're packaging it as a plugin, or if you include some other js resource? So far I've been doing a bare plugin so I don't have access to stuff like #requireResource, but I'm fine with hard-coding some script elements if that's what's necessary (for now).
Sincerely stumped.
Unfortunatly that neer worked for me eighter. I think it broke in JIRA 6..
Solution: take the javascript approach and you will be just fine. it is also way more powerful.
it works for me, just add this script
<![CDATA[
<script type="text/javascript">
window.onload = function(){
var edit = window.parent.document.getElementById(window.frameElement.id + '-edit');
edit.classList.remove('hidden');
edit.style.display = 'none';
}
</script>
]]>

Jaydata and Odata- HTTP Request Failed

I have my own custom server to expose data from an XML file. I can browse through it in whichever browser of my choosing and I can query the data in Fiddler, but Jaydata (or one of its building blocks) can't seem to grab the same data. What's most frustrating is that my code is (or was, I've tweaked it slightly to try and resolve these errors) pretty much an exact duplicate of code found here and here. Here's my script:
<script type="text/javascript" src="/Scripts/jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="/Scripts/datajs-1.0.3.js"></script>
<script type="text/javascript" src="/Scripts/jaydata.js"></script>
<script type="text/javascript" src="/Scripts/jaydataproviders/oDataProvider.js"></script>
<script type="text/javascript" src="/Scripts/Block.js"></script>
<script type="text/javascript">
var Context = new foo({
name: 'oData',
oDataServiceHost: 'http://localhost:xxx'
});
function createItemLI(user, id, css) {
var li = "<li></li>".append(name).addClass(css).data('id', id);
return li;
}
$.when($.ready, Context.onReady()).then(function () {
Context.Roots.toArray(function (roots) {
roots.forEach(function (root) {
$('#roots').append(
createItemLI(root.User, root.ID, 'root'));
});
});
});
</script>
Block.js, is the file generated by JaySvcUtil.exe
There's only one thing in the body of the .htm file, a simple <ul id="roots"></ul>
When I try to run the project, there's nothing on the page. When I used FireBug, I get "HTTP request failed" The requestUri is http://localhost:xxx/Roots, which works when I manually browse to it, but the StatusCode is 0, statusText is the empty string, and so on and so forth. I've looked at Fiddler, at it gets exactly what I expected.
I'm assuming there's some manner of flag that needs to be set, but none of the tutorials I've found have been of any help. It's assumed that it works out of the box, and I too had high expectations getting simple read access would be easy.
UPDATE:
it turns out Internet Explorer has been receiving the appropriate data as JSON, though it still doesn't populate the roots as it should. In FireFox it returns a "501 not implemented" error, because my GET request is being altered to be OPTION. I don't have a web.config file as would a project I started as a WCF service. This is just a console app in Visual Studio 2010. So I guess my question becomes "how do I better specify cross-domain behavior through JayData?"
Try This:
var oProviderConfig = {
name: 'oData',
oDataServiceHost: 'http://localhost:xxx/Roots/'
,enableJSONP: false
};
Also you have to enable CORS support from your webservice. If you are using .NET
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Max-Age" value="3600" />
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept, MaxDataServiceVersion" />
<add name="Access-Control-Allow-Methods" value="PUT, POST, GET, DELETE, MERGE, OPTIONS" />
</customHeaders>
</httpProtocol>

How to get particular node value from XML Store in Sencha Touch

I have following XML Structure and I want to get Fields tag value dynamically on the basis of Tab, Topic, SubTopic and Screen; is this possible? Plz let me know appropriate solution.
XML Structure:
<?xml version="1.0" encoding="utf-8"?>
<Settings>
<ABC TYPE="Tab">
<Trans TYPE="Topic">
<Rpt TYPE="SubTopic">
<File TYPE="Screen">
<Fields>
<Tin>10</Tin>
<Pin>8</Pin>
</Fields>
</File>
</Rpt>
</Trans>
</ABC>
<XYZ TYPE="Tab">
<Trans TYPE="Topic">
<Rpt TYPE="SubTopic">
<File TYPE="Screen">
<Fields>
<Tin>10</Tin>
<Pin>8</Pin>
</Fields>
</File>
</Rpt>
</Trans>
</XYZ>
</Settings>
Model Structure:
Ext.define('MyApp.model.ScreenValidation', {
extend: 'Ext.data.Model',
config: {
fields: [
{
name: 'Tin',
type: 'string'
},
{
name: 'Pin',
type: 'string'
}
]
}
});
XMLStore Strucuter:
Ext.define('MyApp.store.ScreenValidation', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.ScreenValidation'
],
config: {
autoLoad: true,
model: 'MyApp.model.ScreenValidation',
storeId: 'ScreenValidation',
proxy: {
type: 'ajax',
url: 'ScreenValidation.xml',
reader: {
type: 'xml',
record: 'Fields'
}
}
}
});
Any help is appreciated!!
If you have access to modify the xml structure I would recommend doing that.
<?xml version="1.0" encoding="utf-8"?>
<Settings>
<Setting>
<type>Tab</type>
<transType>Topic</transType>
<rptType>SubTopic</rptType>
<fileType>Screen</fileType>
<Fields>
<Tin>10</Tin>
<Pin>8</Pin>
</Fields>
</File>
</Setting>
....ETC
</Settings
If you don't have access to the api so that you can change the format I think you are going to be stuck either:
1: getting the xml response with a standard ajax request and either parsing your xml result by hand or using a library then manually populating the MyApp.model.ScreenValidation object.
or
2: extend/override the Ext.data.reader.Xml class and making it parse out the information you want into your model.
I hope you have access to the api because both of the latter solutions sound horrible. Also, I have never attempted to solve a problem like this so I may be overlooking a more simple solution.
I look forward to hearing how you get this working.
Good luck, Brad

OPTIONS and then POST executing action twice

When I make a $http.post from my client to the web api on a different project, firebug and chrome both show an OPTIONS method and then a POST method.
The problem
Both request actually execute my API action. If I restrict to POST, the OPTIONS fails and the post never happens. I have CORS turned on in both angular and webapi.
my app.js has these lines:
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
my api call:
$scope.add = function () {
var role = { Name: 'admin' };
$http.post('http://localhost:7514/Roles/add', role).
success(function () {
alert('RolesController.add');
}).
error(function (message) {
alert('FAILED EXECUTE RolesController.add');
});
};
the webapi web.config
<system.webServer>
...
<!-- added for cors handling, remove when using a dedicated cors handler or the system.web.cors dll-->
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*"/>
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
</customHeaders>
</httpProtocol>
</system.webServer>
If you use the nightly CORS package, the OPTIONS request gets intercepted by a message handler which doesn't execute the action, but still sends the appropriate response header. For more information and how to use nightly packages, you should look at this blog post regarding getting the CORS nightly package.
http://blogs.msdn.com/b/yaohuang1/archive/2013/04/05/try-out-asp.net-web-api-cors-support-using-the-nightly-builds.aspx
Make sure to select "Prerelease" from the the drop down.

iBatis - select environment using XML

I have this configuration in ibatis-config.xml
<configuration>
<properties resource="collector.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${dev.jdbc.driver}" />
<property name="url" value="${dev.jdbc.url}" />
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${test.jdbc.driver}" />
<property name="url" value="${test.jdbc.url}" />
</dataSource>
</environment>
</environments>
<mappers>
</mappers>
</configuration>
As shown it will load datasource from <environment id="development">
QUESTION: Is it possible at run time switch to use <environment id="test"> without modifying XML? For example - I have a test file where I'm using SqlSessionFactory and want to set it programmatically to use test environment?
SqlSessionFactoryBuilder.build() method can select a specific environment in XML.
For example,
private Reader reader;
private SqlSessionFactory sqlSessionFactorys;
private SqlSession session;
reader = Resources.getResourceAsReader("ibatis-config.xml");
sqlSessionFactorys = new SqlSessionFactoryBuilder().build(reader, "test");
testSession = sqlSessionFactorys.openSession(); // test env
sqlSessionFactorys = new SqlSessionFactoryBuilder().build(reader, "development");
devSession = sqlSessionFactorys.openSession(); // dev env
According to this site:
http://codenav.org/code.html?project=/org/mybatis/mybatis/3.2.5&path=/Source%20Packages/org.apache.ibatis.session/SqlSessionFactoryBuilder.java
The build() method closes the reader/inputstream before returning SqlSessionFactory now. So you will need to open a new reader/stream in order to load the second session. I discovered this when I separated out my account/security tables to a separate database from the main application DB. My first go around I kept getting errors when the bean was trying to load the session factory due to an input stream error (closed).
e.g.
try {
inputStream = Resources.getResourceAsStream(MYBATIS_CONFIG_PATH);
prodDbSqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, prodDbEnvironment);
inputStream = Resources.getResourceAsStream(MYBATIS_CONFIG_PATH);
securityDbSqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, securityDbEnvironment);
} catch (IOException ex) {
String msg = "Unable to get SqlSessionFactory";
CustomizedLogger.LOG(Level.SEVERE, this.getClass().getCanonicalName(), "methodName", msg, ex);
}
Although I put them in separate try catch blocks so that I know which one failed specifically right away in the log file.
I also implement this as a singleton so that it only has to load load resources once.
Context: I run this in a Java EE container and use MyBatis for straight forward queries and for where I would use native queries since it is a much simpler and straight forward framework. I might switch to using it over JPA everywhere, but that is still up for debate.

Resources