adlnet / xapiwrapper Goto Github PK
View Code? Open in Web Editor NEWWrapper to simplify communication to an LRS
Home Page: https://adlnet.gov/projects/xapi/
License: Apache License 2.0
Wrapper to simplify communication to an LRS
Home Page: https://adlnet.gov/projects/xapi/
License: Apache License 2.0
Similar to how ADL.xhrRequestOnError is implemented.
currently log(), this.log() and ADL.XAPIWrapper.log() are the three ways log is being called. this should be limited.
Hi All! I don't know if this is an issue or just under engineering, but Statement.Context can not be set. If I look at xapistatement.js I can see just some context references, but any Context Object. Is this normal? How could I use Context on statements?
Thanks
Commit 0f0c4c0 broke the XAPIWrapper for me. AllI get from Chrome is:
Uncaught TypeError: Cannot read property 'log' of undefinedmergeRecursive @ xapiwrapper.js:1095getLRSObject @ xapiwrapper.js:1148XAPIWrapper @ xapiwrapper.js:111window.ADL @ xapiwrapper.js:1427(anonymous function) @ xapiwrapper.js:1429
Whereafter ADL.XAPIWrapper is not available.
The Problem seems to be that ADL.XAPIWrapper.log() is used before ADL.XAPIWrapper is registered.
Can you please fix that?
getStatements always expects JSON back in the response, even for when using the attachments query. Is it worth discussing this a bit more to have it parse out the statement and do something with the attachments?
The use of try and catch to parse AJAX responses to JSON could currently mask errors in the callback function.
Would better if the callback was handled outside of try/catch:
var callbackData;
try{
callbackData = JSON.parse( xhr.responseText );
}catch( e ){
callbackData = xhr.repsonseText;
}
strictCallbacks ? callback(null, xhr, callbackData) : callback(xhr,callbackData);
I noticed while testing the ADL launch method that sendStatement()
and sendStatements()
are not functionally equivalent. For instance, while testing with Learning Locker v1, xAPI statements sent via sendStatements()
would fail.
The same statements would succeed if used in a for-each call to sendStatement()
.
Test environment
Error Message
{
"description": "Object doesn't support this action",
"number": -2146827843,
"stack": "TypeError: Object doesn't support this action\n at f (http://127.0.0.1:8099/lesson/6918064/xapiwrapper.min.js:2:30984)\n at Anonymous function (http://127.0.0.1:8099/lesson/6918064/demo.html?xAPILaunchKey=abea8db9-da55-4dfa-82ec-98daa675259a&xAPILaunchService=http%3A%2F%2F127.0.0.1%3A8099%2F:22:8)\n at j (http://127.0.0.1:8099/static/js/common/jquery.min.js:2:29992)\n at Anonymous function (http://127.0.0.1:8099/static/js/common/jquery.min.js:2:30313)"
}
The Rustici launch mechanism passes through the mbox
as an array rather than a string. I know the spec should be considered deprecated at this point but there do appear to be some popular learning management systems which still use this. See the comments at RusticiSoftware/launch#6 for background.
Isn't it a security risk to have the password to your LRS be sent client-side to be used with this library? I'm in the process of using this library to connect to our LRSs, but I'm a little confused at the security. Any insight would be greatly appreciated, thanks!
https://github.com/adlnet/xAPIWrapper/blob/master/xapiwrapper.js#L256
"Relative IRL that may be used to fetch more results, including the full path and optionally a query string but excluding scheme, host, and port. Empty string if there are no more results to fetch.
This IRL must be usable for at least 24 hours after it is returned by the LRS. In order to avoid the need to store these IRLs and associated query data, an LRS may include all necessary information within the IRL to continue the query, but should avoid generating extremely long IRLs. The consumer should not attempt to interpret any meaning from the IRL returned. "
So, it must be a relative URL and it must include the full path. Relative URLs (also called relative references or URL fragments) are not combined with a URL by concatenating them together, but by a special set of rules. For instance, combining http://a.example.com/stuff/orange with the relative reference alpha results in http://a.example.com/stuff/alpha . Combining http://a.example.com/stuff/orange with the relative reference /alpha results in http://a.example.com/alpha , and there are a variety of other rules as well. Since the more value is required to include the full path and not the hostname and scheme, the only legal sort of relative reference is one that starts with /, which will replace the whole URL path, not be concatenated to the end.
Hi,
While using xAPIWrapper, I encountered a little issue to get auth in the url. My authentication token ends with a "=" char. I found that if a parameter contain this char, it is not used. Maybe this should be modified.
Thanks,
Corentin
submit statement returns an error "internal server error"
at the same time the statements are saved any clarification?
i use the scorm cloud for my lrs. but node say my endpoint is invalid.
it seem like it only can get the statements from ADL_LRS
"info: 401
info: Credentials invalid for this endpoint.
undefined:1
Credentials invalid for this endpoint."
Content-Type is application/json if state value is Array. But Content-Type is application/octet-stream if you pass Array form one page to another page . The reason is use instanceof to test state value .
see javascript-when-i-pass-array-from-iframe-function-the-array-lose-his-type
Test in chrome with windows 10.
t1. html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function test() {
var arr = [];
arr.push(1);
var r1 = arr instanceof Array;
console.log("is array (instanceof) " + r1.toString());
console.log("is array (Array.isArray) " + Array.isArray(arr))
console.log("call iframe")
window.f1.demo(arr);
}
</script>
</head>
<body>
<iframe src="t2.html" name="f1" height="600" width="100%" frameborder="0" scrolling="no" onload="test()"></iframe>
</body>
</html>
t2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
demo = function (param) {
var r1 = param instanceof Array;
console.log("is array (instanceof) " + r1.toString())
console.log("is array (Array.isArray) " + Array.isArray(param))
}
</script>
</head>
<body>
</body>
</html>
is array (instanceof) true
is array (Array.isArray) true
call iframe
is array (instanceof) false
is array (Array.isArray) true
Hello,
ADL.XAPIWrapper.getStatements() seems to only return the statements produced the last two days. The statements that are not returned are over a month old. Is this on purpose?
https://github.com/adlnet/xAPIWrapper/blob/master/src/xapiwrapper.js#L28-L83
These should probably be removed from global scope.
Looks like the ADL.launch method makes use of the URL API which is not supported by IE. This means the current code cannot initialize the launch.
The parseQueryString method in xAPIWrapper takes no account of hashes potentially appearing after the search paramters.
Something like this would overcome the problem:
function parseQueryString()
{
var loc, qs, pairs, pair, ii, parsed;
qs = window.location.search.substr(1);
pairs = qs.split('&');
parsed = {};
for ( ii = 0; ii < pairs.length; ii++) {
pair = pairs[ii].split('=');
if (pair.length === 2 && pair[0]) {
parsed[pair[0]] = decodeURIComponent(pair[1]);
}
}
return parsed;
}
Hi,
I'm using your library for some tests and I've get an issue :
ADL.XAPIWrapper.sendStatement(stmt);
this line works fine on Chrome, FF (latest versions) and IE11 ... but it doesn't work with IE10.
An exception is sent with message : "Error : Access denied".
Have I missed something, or is it a real bug ?
Thanks,
Corentin
This conditional bugs me. If the first part of the conditional is true, then the second part can't possible be false. Isn't the second part redundant? It is used several times in the documentation.
if (res.more && res.more !== "")
...
Hi adlnet,
I wonder what is the future roadmap for the xAPIWrapper? Are there any structural changes or extensions planned? We are starting to use this great lib as the base for our apps and tests so it would be good to know if important changes are ahead.
It would be fantastic to put this code under semantic versioning, this would make the integration and maintenance easier for app devs.
Thanks
Hi,
firstly, thanks for removing the alerts in ADL.XHR_request()
However I believe there is a syntax error in commit a998c356f6970b99e057da161deb2c7d356bfda0
Instead of console.warning()
it should be console.warn()
, see docs
Firebug is currently throwing an exception and stops logging:)
TypeError: console.warning is not a function
I might do a pull request. What is your preferred workflow in such cases? First an issue and then a pull request or a straight pull request?
Cheers
Rather than forcing the namespace this would let it work properly with modern good practices - there's various patterns, but basically if there's no define
function defined then nothing changes, otherwise it should effectively have define(function(){return ADL;})
- but possibly without using the global namespace at all.
Hi
I use launch server and want to pass some param(ex: a=b&c=d) to launch url. But the xAPIWrapper.js is not pass extended param to LRS. I find #14 , it is delete pass param code. I do not know why? Anyone can help me
The current implementation for sending statements with attachments does not support sending binary data, for example an image or pdf. Currently the code uses strings to store / concat together the multipart/mixed request, to support sending binary data this would need to be update to use something like ArrayBuffers.
Would it be possible to create a folder with several examples of pulling records from an LRS. Something like the following would be enormously helpful to what we are trying to accomplish in my company.
getStatements1.html
This would be an example of a getting LRS records. It would pull down all verbs with no data restrictions and no special formatting. It would look like a large data dump.
getStatements2.html
This would be an example of a getting LRS records but giving the user a way to specify what data they want. For example, I would like to be able sometimes to just grab Actor, verb, object, date/time and context activity (like category). It would also have a variable that can be adjusted that would allow for getting only a certain number of records.
getStatements3.html
This would be like getStatements2 but with additional features that would allow the user to limit data to a date range or a category name. It would also be parsed for a neat appearance.
Thank you very much for your consideration. ~ John M.
The minified file in the /dist folder requires rebuild based on the recently merged pull requests.
ADL.XAPIWrapper.changeConfig({
'endpoint': 'https://lrs.adlnet.gov/xapi/',
'user': 'steven',
'password': 'test123'
});
returns 401 UNAUTHORIZED
Is this repo up on NPM? I'm not able to find it, it would be nice to npm install
I was using this JS to push h5p statements from Moodle to an LRS.
But the new ADL.XAPIStatement(), does not accept result parameter, the result paramenter can be used to send user's response to LRS.
For example answer to a quiz question.
Not exactly an issue, but I cant find anything related to this in the documentation.
Suppose I want to query for multiple parameters like all statements with verbs pause or play.
Or all Users whose id(mail) is on a particular domain, etc. How do I do such queries? If possible please
direct to a complete documentation on the searchParams object as it looks quite essential.
Thanks.
In IE/Edge, if including the xapiwrapper.js
in isolation as detailed in the README, sending string attachments isn't possible as the text-encoding
library isn't listed as a requirement and therefore no TextEncoder
is available in the browser.
<script type="text/javascript" src="./src/xapiwrapper.js"></script>
browser support table
TextEncoder use part 1
TextEncoder use part 2
I noticed this while testing a PDF upload. Setting contentType: application/pdf
on the attachment item it was still being set to application/octet-stream
in the POST request. This caused the LRS (Learning Locker) to save the file as a .bin rather than .pdf.
The special case of "" for the matchHash should not include quotes. For example:
If-None-Match: *
is correct, but
If-None-Match: ""
is not correct.
See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
This can be fixed by using the following function:
function formatHash(hash) {
if (hash === "*") {
return hash;
}
return '"' + hash + '"';
}
Usage example:
headers = { "If-Match": formatHash(matchHash) };
Hey
when I try to use this with IE10 (works great with ie11, chrome) I get an error:
SCRIPT5: Access is denied.
xapiwrapper.js, line 1342 character 13
This is the line it refers to:
xhr.open(ieModeRequest.method, ieModeRequest.url);
Any ideas how to fix this - having the course on a secure server makes no diffence.
Sorry - pressed submit before adding the error!
John
After generating about 5k statements across 40 devices (same model tablet) we encountered a id collision. JavaScript's Math.random()
isn't that great of a PRNG and the Web Crypto API isn't widely supported yet. It may be better to use a v3/v5 UUID (hash versions) and use actor id + course id + activity + timestamp to produce UUIDs that are less likely to collide in this context.
If you're OK switching to v3 or v5 I'm happy to do the work.
A couple of places where variables mistakenly end up in the global scope.
https://github.com/adlnet/xAPIWrapper/blob/master/src/xapiwrapper.js#L146
https://github.com/adlnet/xAPIWrapper/blob/master/src/xapiwrapper.js#L1281
I've been trying out the xAPIWrapper for the first time but I keep getting this error in the browser console (XXXXX refers to my endpoint ID):
cloud.scorm.com/tc/XXXXX/statements:1
POST
https://cloud.scorm.com/tc/XXXXXX/statements 400 (Bad Request)
My authentication code is:
var conf = {
"endpoint": "https://cloud.scorm.com/tc/XXXX/",
"user": "XXXX",
"password": "XXXXX"
};
ADL.XAPIWrapper.changeConfig(conf);
I've used these same credentials with the tin can prototypes (http://tincanapi.com/prototypes/) and it works fine. Not sure what's going on?
I am facing issue processing more statements from XAPI. In my code below, I am using XAPI Wrapper and I am getting around 900 statements from lrs in rek, however, when I convert them into a collection and try to use the where clause, it does not give me any result.
Please Note: I can get the same thing done using fetchAllStatementes and it also works fine with getStatements (for only the first 100 records), however, I wish to do it manually using getMoreStatements.
Is there an example of merging statements using get more statements, creating a collection on top of it and then using the where query to filter the data?
My code:
var stmt1 = JSON.stringify({
"mbox": actor.mbox ,
"name": actor.name
});
var stmt2 = JSON.stringify({
"mbox": "mailto:[email protected]"
});
var stmtVerb = JSON.stringify({
"id": "http://adlnet.gov/expapi/verbs/started"
});
var stmtCtx = JSON.stringify({
"contextActivities": {
"grouping": [
{
"id": competency.iri
}
]
}
});
var search = ADL.XAPIWrapper.searchParams();
search['agent'] = stmt1;
search['authority'] = stmt2;
search['context'] = stmtCtx;
var rek = []
ADL.XAPIWrapper.getStatements(search, null,
function getmore(r){
var res = JSON.parse(r.response);
$.each(res.statements, function(x,y){
rek.push(y) ;
});
if (res.more && res.more !== ""){
ADL.XAPIWrapper.getStatements(search, res.more, getmore);
}
});
console.log(rek);
//var ret = ADL.XAPIWrapper.getStatements(search); //works fine
//var statements = new ADL.Collection(ret.statements);
var stmt = new ADL.Collection(rek);
var filtered_data = stmt.where('actor.name = "ccazabon"').exec(function(data){
console.log(data);//no output-empty array, however, matching data does exists
});
var p = [{'name': 'Steven', 'age': 25, 'dob':{
'mm': 'jan',
'dd': '18'
}},
{'name': 'John', 'age': 35, 'dob':{
'mm': 'feb',
'dd': '19'
}}];
//console.log(p);
var a = new ADL.Collection(p);
//console.log(a);
var d = a.where('dob.mm = "jan"').exec(function(data){console.log(data)});//works as expected
I think there is some issue with merging all the statements and creating the ADL.Collection, however, I am not getting any online help to resolve the same.
Your help is greatly appreciated.
I have a question about the sendActivityProfile functiion. When passing an object as the profileval the function will always use the POST method.
Unfortunately, when using POST the LearningLocker LRS will ignore the If-Match header and will always store the data sent even if it overwrites an updated profile object. If I modify the code so that it uses PUT, LearningLocker behaves as expected and returns http status 412 - precondtion failed.
Just wondering what the correct behaviour here should be? Is this a fault with the wrapper or the LRS?
And what is the reason POST always being used when profileval is an object but not if profileval is an array? Of the top of my head I would expect POST when creating a new profile and PUT when updating an existing profile, why should the method be dependent on the profile data type?
Hi
I ran into a problem while using this API with IE10. The API was throwing invalid state error in IE 10 while trying to fetch statements from SCORM CLOUD. All works well in IE 11 and other browsers.
Debugging further I realized that the issue was actually with the xhr.withCredentials in the ADL.XHR_request function. Moving it after xhr.open fixed the issue. Is it possible to raise a pull request to include it in the code?
xhr.open(method, url, callback != null);
xhr.withCredentials = withCredentials;
or
if(typeof(withCredentials) != "undefined") // if Boolean is not passed for withCredentials
xhr.withCredentials = withCredentials;
xhr.open(method, url, callback != null);
https://github.com/adlnet/xAPIWrapper/blob/master/src/xapiwrapper.js#L3-L26
This should probably be converted to a function called DateToISOString
.
Dependent on #135
Uncaught TypeError: undefined is not a function
Came across this when working with the State API, it applies to any function using toISOString()
which is defined in the top of xapiwrapper.js if the browser doesn't support the native function. I can call Date.prototype.toISOString
in the console, but I cannot process any dates.
RangeError: Invalid time value
This bug has also brought to my attention leniency on the date string in the getStatements
function. This will encodeURIComponent()
any date string and the LRS will process it properly. e.g. 02/11/2015 11:04 AM
will query https://lrs.adlnet.gov/xapi/statements?format=exact&since=02%2F11%2F2015%2011%3A04%20AM
with results returned as if an ISO 8601 date was passed.
The xAPI spec is unclear as to whether timestamps MUST be ISO 8601 everywhere (statements, queries, database).
A timestamp MUST be formatted according to ISO 8601.
Array.prototype.foo = function() {}
var statements = [ ... ];
xapiWrapper.sendStatements(statements, ...);
-> SyntaxError: Unexpected token u in JSON at position 0
at JSON.parse (<anonymous>)
at XAPIWrapper.prepareStatement
at XAPIWrapper.sendStatements
Reason:
for (var key in statements) { console.log(key) }
-> 0
-> 1
-> 2
-> ...
-> foo
-> other prototype extensions...
ADL.XHR_request() and callees: It would be nice to have the opportunity to submit an extra callback for errors. With the alerts removed in #26 (which is great!) we need an easy way to communicate errors to the users. Currently I am monkey-patching the XMLHttpRequest object to do this but that is not a good solution.
Running in to an issue when calling sendState(..., callback)
/sendStatements(..., callback)
the supplied callback is not invoked when an error is encountered (either the request is bad or the LRS is down). Looking through the code I see that a global error handler has been added, but that is not optimal as it doesn't easily facilitate context specific handling of the failure.
IMO, the expected behavior behavior is that the callback is invoked regardless of the outcome. It's up to the dev to inspect the status code (or an error object) and determine the correct course of action inside of the callback. Other platforms, like Node.js, have the first argument in callbacks be the error or null (if no error). That would require that callbacks confirm to function(error, response, body)
where error is null when the request is successful.
Adding a option/flag to enable the expected behavior (the callback is always invoked regardless of outcome) would be a significant step towards correcting the problem. I'm willing to do the work of adding this option and behavior.
Hi
GetState function not store state value or etag. It there a simple way to store state value or etag?
I got 'Invalid Launch Parameters' error while launching course build using adapt-authoring tool and tkhub xAPI plugin.
var wrapper;
ADL.launch(function(err, launchdata, xAPIWrapper) {
if (!err) {
wrapper = xAPIWrapper;
console.log("--- content launched via xAPI Launch ---\n", wrapper.lrs, "\n", launchdata);
} else {
console.log(err);
}
$('#endpoint').text(wrapper.lrs.endpoint);
}, true);
How to provide launch parameter?
Since ADL.verbs exists, I'm just wondering why ADL.activities (or activityTypes) doesn't?
I'm using the xAPIWrapper in a project I'm working on and was surprised that it wasn't there.
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.