janhommes / o.js Goto Github PK
View Code? Open in Web Editor NEWo.js - client side oData lib.
Home Page: https://janhommes.github.io/o.js/example/
License: MIT License
o.js - client side oData lib.
Home Page: https://janhommes.github.io/o.js/example/
License: MIT License
o("Product").expand(["Address","MainIMage_fk"]).get()
Hi,
I just wanted to submit a small issue. The Readme file and typescript declaration files are not up to date with the latest released version. At least "isCors" option is not documented, which can be important for some projects (it was a prerequisite in my case). I am making 2 pull requests atm for each modification.
will be helpfull to use jspm, add a release on git
I have a project set up using Bower: "o.js": "0.2.2"
While executing examples i get an error because defer dependency is not defined:
o('http://services.odata.org/V4/OData/OData.svc/Products').find(':0').route('Product/Detail/:0/:1',function(data) {
console.log('Route Product/Detail/'+this.param[0]+'/'+this.param[1]+' triggered. Result:');
console.log(data);
});
t {data: Array[0], inlinecount: null, param: Object, oConfig: Object}any: (e,n)arguments: nullcaller: nulllength: 2name: ""prototype: Object__proto__: ()<function scope>batch: (e)arguments: nullcaller: nulllength: 1name: ""prototype: Object__proto__: ()<function scope>beforeRouting: (e)arguments: nullcaller: nulllength: 1name: ""prototype: Object__proto__: ()<function scope>count: ()data: Array[0]length: 0__proto__: Array[0]delete: (e)arguments: nullcaller: nulllength: 1name: ""prototype: Object__proto__: ()<function scope>deleteRef: (e,n)arguments: nullcaller: nulllength: 2name: ""prototype: Object__proto__: ()<function scope>exclude: (e,n)arguments: nullcaller: nulllength: 2name: ""prototype: Object__proto__: ()<function scope>expand: (e)arguments: nullcaller: nulllength: 1name: ""prototype: Object__proto__: ()<function scope>filter: (e)arguments: nullcaller: nulllength: 1name: ""prototype: Object__proto__: ()<function scope>filterByList: (e,n)find: (e)first: ()get: (e,n)include: (e,n)inlineCount: (e)inlinecount: nullisEndpoint: ()link: (e,n)loading: (e,n)oConfig: Objectappending: ""endpoint: nullerror: nullformat: "json"headers: Array[0]isAsync: trueisCors: trueisHashRoute: trueopenAjaxRequests: 0password: nullready: nullstart: nullstrictMode: trueusername: nullversion: 4__proto__: ObjectorderBy: (e,n)arguments: nullcaller: nulllength: 2name: ""prototype: Object__proto__: ()<function scope>orderByDesc: (e)arguments: nullcaller: nulllength: 1name: ""prototype: Object__proto__: ()<function scope>param: Object__proto__: Objectpatch: (e,n)arguments: nullcaller: nulllength: 2name: ""prototype: Object__proto__: ()<function scope>post: (e,n)progress: (e)put: (e,n)query: (e)arguments: nullcaller: nulllength: 1name: ""prototype: Object__proto__: ()<function scope>ref: (e,n)remove: (e)removeRef: (e,n)route: (e,n)routes: (e,n)save: (e,n)search: (e,n,t,r)select: (e)skip: (e)take: (e)top: (e)triggerRoute: (e)where: (e)__proto__: Object
var oHandler = o('http://services.odata.org/V4/OData/OData.svc/Products');
undefined
oHandler.find(1);
t {data: Array[0], inlinecount: null, param: Object, oConfig: Object}any: (e,n)batch: (e)beforeRouting: (e)count: ()data: Array[0]delete: (e)deleteRef: (e,n)exclude: (e,n)expand: (e)filter: (e)filterByList: (e,n)find: (e)first: ()get: (e,n)include: (e,n)inlineCount: (e)inlinecount: nullisEndpoint: ()link: (e,n)loading: (e,n)oConfig: ObjectorderBy: (e,n)orderByDesc: (e)param: Objectpatch: (e,n)post: (e,n)progress: (e)put: (e,n)query: (e)ref: (e,n)remove: (e)removeRef: (e,n)route: (e,n)routes: (e,n)save: (e,n)search: (e,n,t,r)select: (e)skip: (e)take: (e)top: (e)triggerRoute: (e)where: (e)__proto__: Object
oHandler.get(function(data) {
console.log(data);
//or the saved var also contains the data:
console.log(oHandler.data);
});
o.min.js:1 Uncaught TypeError: Cannot read property 'defer' of null(…)G.get @ o.min.js:1(anonymous function) @ VM5133:1
Hello,
I'm using this library to request data from a java server with olingo v4
o().config({
endpoint: '/api'
})
o('ShopWebsites').get(function (data) {
console.log(`Data is {data}`)
})
I get this
o.js:1384 GET http://localhost:3000/api/ShopWebsites/?$format=json 400 (Bad Request)
The request is bad because it adds the last slash after ShopWebsites
. If I make the request like: /api/ShopWebsites?$format=json
it works.
Hi,
You made a fix for $count, to make it "/$count."
So, you may need to do this fix for $ref too, since it needs to be: /$ref at the end of URL, per OData v4 specs.
And yes, still need to get your fixes into "npm install odata," since your last fix is only in this Git Repo.
And can you update your this README page of the supported query commands vs what is listed inside the top of the o.js lib file?
In the example code , it talks about the response payload in result variable, like so:
console.log(result);
However, when I make a curl call directly, I get items outside of "value," as shown:
...
{
"@odata.context": "https://localhost//odata/$metadata#tm/something/",
"@odata.nextLink": "https://localhost/odata/tm/something/?$skip=50&$top=50",
"value": [
{
"@odata.etag": "1",
"@odata.id": "https://localhost/odata/tm/something('ABC1)",
"description_field1": true,
... etc...
Basically o.js returns me the "value" portion of the payload, but I need to get the items outside of that "value" portion as well (eg, the context and nextLink fields)... How can I get those back from o.js?
Also, how can I get the RESPONSE HEADERs back from o.js get() call too?
Hello again,
Maybe i'm misunderstanding something but the regex used in the startAjaxReq function to parse $batch results didn't work for me. It was:
var regex = /({[\s\S]*?--batchresponse_)/g;
...
parseResponse(result[0].substring(0, result[0].length - 16), tempBase);
which I replaced with:
var regex = /({[\s\S]*?})/g;
...
parseResponse(result[0], tempBase);
The original regex and substring handling looks very intentional but clearly wasn't working for me. Am I missing something?
Thanks,
Chris
Is Q a required dependency?
<script src="../bower_components/o.js/o.js"></script>
<script>
o('...').get(function(data){
console.log(data);
});
</script>
Currently, the unit tests rely on the "Trippin" example service. Which means that the unit tests actually rely on this API to work. I've seen multiple examples where the library produces the correct request, but the API fails with HTTP status code 500, causing the tests to fail anyway.
IMO, it would be better to simply test the request object before it goes out to any server. Since the project currently uses Jasmine for testing, one possibility is to use jasmine-ajax to intercept the requests and run tests on them.
This way, the URL, method, data, etc can be tested without actually relying on a real API to respond. This also allows tests to be run in a offline-only scenario when developing.
Thoughts? As long as the tests enforce the OData protocol spec, it should be a better approach and allow for corner case testing without the Trippin service implementing that endpoint.
I'm looking through the source and it seems like you never add in the headers
which get set in the configuration.
I could not get this library to work from behind a proxy.
IMHO the best way to support globals, Node, and AMD is by conforming to the UMD API (https://github.com/umdjs/umd).
I tried wrapping your library in this IIFE
;(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['q'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('q'));
} else {
root.o = factory(root.Q);
}
}(this, function(Q) {
function o(res) {
...
}
return o;
}));
and it works like a charm. What do you think?
Antony
Hey,
I have an OData Endopoint that I need to auth against w/ NTLM. Dont seem to work.
Any ideas?
Tx,Gabor
check if every request is done and only then fire the oConfig.ready function..
I tried using this library but when doing the following:
o().config(myConfig);
o('appointments').count().get();
I get a 404 because the url is /products$count instead of /products/$count.
And the following works:
o().config(myConfig);
o('appointments/').count().get();
I am new with OData so I am not sure I am using the library well.
If this is not a bug, can you correct me with how to use the .count()
method?
Hi
could you double check if q.js dependency was fixed in V-0.3.1? I am still seeing the 'defer' undefined error once I remove Q.js link from my markup.
I am also seeing unless I configure "content-type: application/json" in the header, SharePoint REST service returns xml payload instead of json, so the code bombs out at the first character of JSON.parse(response) . I am not sure if this is a particular issue with Microsoft SharePoint service.
Stumbled upon this library - and it looks awesome.
If you create a release (see #11 ) - would it be possible to submit this to npm?
I can wrap it in a package myself, but it'd be awesome if you'd add the required bits (a suitable package.json, mostly?) for npm to be happy. Would you accept a PR for that?
{
"error":{
"code":"",
"message":"The query specified in the URI is not valid.",
}
}
I use TripPinService of OData.org to test, but I found the request's url is wrong.
Here is the test: https://jsfiddle.net/billskate/su3b32mv/1/
The correct url is http://services.odata.org/V4/(S(ms4wufavzmwsg3fjo3eqdgak))/TripPinServiceRW/People,
but parsed to
http://services.odata.org/V4//TripPinServiceRW/People/
This part (S(ms4wufavzmwsg3fjo3eqdgak)) is missed.
Another bug is you can not add Content-Length header to XMLHttpRequest.
Is there a way that I can just get the generated URL and handle the actual HTTP request manually?
Example:
//get a product list on product click
o("ProductGroup/Group").filter("Group/Parent_fk eq {0} {1} {0} {2}").route("Group?",function(data) {
console.log(data);
self.route("Group");
self.groups(data);
});
If the route is Group/1/Test/x
the query should return a filter like this: $filter=Group/Parent_fk eq 1 Test 1 x
.
Hi,
So I have OData service that supports version "4.0" string exactly, and if I use OData-Version header directly in o.js config or using curl, it works as I would expect.
But if I set this o() config to the above and use any value, like 2, or 5, etc... my OData service seems to ignore that value and continues to return all my data. I expected my service to reject me with an error, like "version 2 not supported."
Is this version field working correctly?
When a CORS request is made, currently, no cookies are sent to the odata server, even if isCors is enabled.
There seems to be a missing line of code right before opening the XHR, the withCredential XHR field is not set, but it is mandatory to do CORS requests (at least into Chrome). I have made a simple pull request ( #53 ) to correct this but there are evidences in the code that the withCredential field has been taken into account (at least partially), so I don't know if I am not missing something by just setting it to true.
Hi @janhommes
We use o.js in our intranet, all the pc are xp with ie8. And we found a problem, ie8 XMLHttpRequest not support patch method. Maybe, it should check the browser, if it is ie8, xhr should be activexhttprequest instead of xmlhttprequest.
Thanks
function createCORSRequest(method, url) {
1446 var xhr = null;
1447
1448 //if no window assume node.js
1449 if (typeof window === 'undefined') {
1450 var Xhr2 = require('xhr2');
1451 xhr = new Xhr2();
1452 }
1453 else {
1454 xhr = new XMLHttpRequest();
1455 }
Operation mapping is a nice feature, but can be dangerous in some contexts: I had to disable it when dealing with dates, since they contain dashes ('-'). This results in a date like '2016-03-01' being processed as '2016sub03sub01'.
Maybe we need a smarter jsToOdata function, or remove operation mapping support completely for arithmetic operations.
Antony
throws n.on is not a function
error.
i dont know if this plugin is wired only to work on browsers or if it has such dependencies . or should i use Nodify
I'm not sure if the example app has a bug or if I'm not running it correctly. What are the instructions for getting the example up and running?
I tried running 'node app.js' in the node js console window and I get
ko.applyBindings(new ViewModel());
^
ReferenceError: ko is not defined
at Object. (E:\Playground\OData\o.js-master\example\app.js:78:1)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Function.Module.runMain (module.js:693:10)
at startup (bootstrap_node.js:188:16)
at bootstrap_node.js:609:3
I tried just opening up the index.html app in my browser and when it runs the query to the service I get an error:
oException
message : "Request to http://services.odata.org/V4/%28S%28wptr35qf3bz4kb5oatn432ul%29%29/TripPinServiceRW/People?$format=json&$top=3 failed with HTTP status 404."
name : "o.js exception"
I used the Chrome debugging tool to trace the operation and it's happening at o.js:1413 and the ajaxRequest.status is 0. If I copy the uri and paste it into chrome as-is then I get the data I'm looking for without issues.
Perhaps I didn't set it up right. I downloaded the zip file and extracted it into a local playtime folder... E:\Playground\OData\o.js-master\example and I'm trying to run it from there. Am I running the example incorrectly? Is it supposed to be running through a server environment via localhost such as node? How should that be invoked?
TIA - mike
add support to typescript
I need set custom header in my project when executing some special request. eg:
Accept : "application/vnd.ms-excel"
but I only found the global setting method .
o().config({ headers:[{
name: "Accept",
value: "application/vnd.ms-excel"
}]});
Can you provide an interface to do this , like changing "get()" method to add an extra prarameter named "headers" ?
how do I add a $select clause to the query? I don't see any part of the API that supports that.
When I tried to set this header for my OData request, using:
o().config({headers:[{name:'Accept-Charset',value:'application/json'}]});
o(MYURL).get();
Here is the error back in my response ( and note I turned off CORS):
Refused to set unsafe header "Accept-Charset" <--- how to get past this error?
oData {
data:
[ { name: 'Photos', kind: 'EntitySet', url: 'Photos' },
{ name: 'People', kind: 'EntitySet', url: 'People' },
{ name: 'Airlines', kind: 'EntitySet', url: 'Airlines' },
{ name: 'Airports', kind: 'EntitySet', url: 'Airports' },
{ name: 'Me', kind: 'Singleton', url: 'Me' },
{ name: 'GetNearestAirport',
kind: 'FunctionImport',
url: 'GetNearestAirport' } ],
inlinecount: null,
param: {},
oConfig:
{ endpoint: 'http:///mgmt/odata',
format: 'json',
autoFormat: true,
version: 4,
strictMode: true,
start: null,
ready: null,
error: null,
headers: [ [Object] ],
username: null,
password: null,
isAsync: true,
isCors: false, <--- set to false
openAjaxRequests: 0,
isHashRoute: true,
appending: '' },
raw:
{ '@odata.context': 'http://services.odata.org/V4/TripPinService/$metadata'
...etc...
Any idea how to get o.js to set/use my headers I specified in my request?
I'm running with the @types/odata typings from npm, but as far as I can tell they've been set up in a way that doesn't support imports for es2015 modules, without an export of the interfaces in a way that I can reference in my code.
How to build like this: Products/?$expand=ProductUnit($expand=ProductPrice)
If my service has $metadata available, can o.js make use of it and generate client-code from that, similar to Java Olingo OData module or C#?
Hi @janhommes,
I'm not sure of what I'm about to say here, but when I perform a request to an OData service without specifying any page size I get an additional field in the response that tells me what to do next:
http://services.odata.org/V4/Northwind/Northwind.svc/Orders
{
...
"@odata.nextLink": "Orders?$skiptoken=10447"
}
Is there a way already to get this info inside the oHandler? How could we add it in case? Thanks in advance,
Antony
So I don't want to use $expand
. I want to run a query that returns only the related entity or related entities of something. I think this is part of the OData 4 spec...
So I want to run something like this -
http://services.odata.org/V4/OData/OData.svc/Products(4)/People
Where People
would be all the people who bought Product with the ID 4.
Is this possible with the o.js
api?
Hello,
I would like to add a timeout of 2 seconds to all my requests (using NodeJS). I tried modifying the o.js
file at line 1450 like this:
var Xhr2 = require('xhr2');
xhr = new Xhr2();
xhr.timeout = 2000;
to no avail. Is there some way I can remedy this issue?
Thank you.
When I define this:
o().config({autoFormat: false}); <-- so I don't want o.js to attach ?$format=json at end of my URL
And make my OData GET call, it returns with a 406 HTTP error:
o.js exception: Request to https://MY_URL/mgmt/odata/tm/ltm/pool failed with HTTP status 406.
Yet when I make a curl call to my URL (as shown above), it works fine to return my resources, such as:
curl -s -k -u user:password -H 'Content-Type: application/json' -X GET https://MY_URL/mgmt/odata/tm/ltm/pool
Can someone confirm setting that field to false works or fails. If I set it to true, then it does attach the "$format" at the end of my URL and my o.js call works.
I use patch/put like the document mentioned:
o('http://services.odata.org/V4/OData/OData.svc/Products(1)').patch({Name:'NewName'}).save(function(data) {
console.log("Product Name changed");
});
but a exception throw:
Bulk updates are not supported. You need to query a unique resource with find() to patch/put it.
so, how can I use it correct?
thx
URLs are normally not case sensetive. So we should make Routes also not case sensetive.
I need to make multiple PATCH
requests for different entities in single $batch
, how to do that?
Is there a way to omit the $format=json
part from the generated query string? I would love to use this library for my extension libraries for MS Dynamics CRM but all the requests fail with the following resonse:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code></code>
<message xml:lang="de-DE">The query parameter '$format' begins with a system-reserved '$' character but is not recognized.</message>
</error>
OData V4 Url convention (section 5.1) doesn't imply that this query option in mandatory anyways (just like any other queryoption, really) so maybe this should just be present when the format
-option is beeing set?
For $skiptoken, OData 4.0 spec states:
11.2.5.7 Server-Driven Paging
Responses that include only a partial set of the items identified by the request URL MUST contain a link
that allows retrieving the next partial set of items. This link is called a next link; its representation is
format-specific. The final partial set of items MUST NOT contain a next link.
The client can request a maximum page size through the odata.maxpagesize preference. The service
may apply this requested page size or implement a page size different than, or in the absence of, this
preference.
OData clients MUST treat the URL of the next link as opaque, and MUST NOT append system query
options to the URL of a next link. Services may not allow a change of format on requests for subsequent
pages using the next link. Clients therefore SHOULD request the same format on subsequent page
requests using a compatible Accept header. OData services may use the reserved system query option
$skiptoken when building next links. Its content is opaque, service-specific, and must only follow the
rules for URL query parts.
OData clients MUST NOT use the system query option $skiptoken when constructing requests.
And for $id in GET request, it states:
11.2.8 Resolving an Entity-Id
To resolve an entity-id, e.g. obtained in an entity reference, into a representation of the identified entity,
the client issues a GET request to the $entity resource which located at the URL $entity relative to
the service root. The entity-id MUST be specified using the system query option $id.
Example 62: return the entity representation for a given entity-id
http://host/service/$entity?$id=http://host/service/Products(0)
A type segment following the $entity resource casts the resource to the specified type. If the identified
entity is not of the specified type, or a type derived from the specified type, the service returns 404 Not
Found.
After applying a type-cast segment to cast to a specific type, the system query options $select and
$expand can be specified in GET requests to the $entity resource.
Example 63: return the entity representation for a given entity-id and specify properties to return
http://host/service/$entity/Model.Customer?
$id=http://host/service/Customers('ALFKI')
&$select=CompanyName,ContactName&$expand=Orders
I would like O.js to support these query options. I think there is $id support when DELETing... but not for GET.
Currently I'm using ODataAngularResources
which has support for some very advanced queries. Any thoughts about bringing this type of api/functionality over to o.js
?
Also, their transformUrl function is very handy as well to add support for anything missing, such as aggregates. For exaxmple, I have the following which produces a group by count summary based on some filtered columns
odataQuery.transformUrl(url => url + `&$apply=groupby((${columns.join(',')}),aggregate(Id with countdistinct as Total))`);
please register o.js to bower repository, thanks
Hi Jan,
First of all thank you for this amazing library, I was just looking for a clean way to perform OData operations and yours is just what I needed: simple, elegant, intuitive.
However I had to adapt it to my needs,and would like to share some suggestions in the following issues.
First of all, the "If-Match" header: I had to remove it otherwise it triggers CORS on calls to http://services.odata.org/V4/Northwind/Northwind.svc/. Is it necessary for other purposes? I would make it totally optional.
Keep up the good work!
Antony
Hi Jan,
I'm trying to integrate o.js inside an isomorphic app, which in my case means loading the same library on Node with
var o = require('o.js')
and at the same time on the browser (through RequireJS) with
define(['o.js'], function(o) {})
Unluckily, since o.js has the '.js' extension in its name, RequireJS seems to resolve the dependency as http://localhost:3000/o.js. As you may know, RequireJS can be configured with aliases, so you can e.g. require 'jquery' instead of 'bower_components/jquery/dist/jquery.min', but this mechanism does not work when modules have the suffix '.js' in their name.
So in short the question is: is there a way to rename this package or add an alias on NPM so it can be easily loaded in every environment? I saw that there are already packages named o-js, o, o-, ojs, etc. so it may not be a simple task... What do you think?
Antony
buildBatchBody method, changeset body string concatenation:
body += 'Content-Type: application/json\n';
//body += 'Content-Length:' + stringData.length + '\n\n';
should be:
body += 'Content-Type: application/json\n\n';
//body += 'Content-Length:' + stringData.length + '\n\n';
as long as second line is commented out. I believe the second newline is necessary for the changeset to be parsed properly (it was for me).
When making O() call, can you return the response HTTP headers as well, so I can parse that?
This is feature request for o.js lib.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.