The CrossRef Search API (docs here) provides citation information from DOI identifiers. I tried using it to get this info but am oddly getting 404 responses.
The headers I set were
Content-type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400
I get the same result from this appspot tester, so I wouldn't think it's my code.
Can anyone advise how I could get it working? It works just fine from their own domain.
It's possible they don't allow cross-domain at all, but I'm not sure if/how I could check that.
Reproducible example:
function doiInfo(doi) {
var doienc = encodeURIComponent(doi);
var doiXHR;
window.XMLHttpRequest ? doiXHR=new XMLHttpRequest() : doiXHR=new ActiveXObject("Microsoft.XMLHTTP");
doiXHR.onreadystatechange=function()
{
if (doiXHR.readyState==4 && doiXHR.status==200)
{
console.log(doiXHR.responseText);
} else if (doiXHR.readyState==4) {
// something went wrong
}
}
doiXHR.open("GET", "http://search.crossref.org/dois?q=" + doienc, true);
doiXHR.setRequestHeader("Content-type", "application/json;");
doiXHR.setRequestHeader("Access-Control-Allow-Origin", "*");
doiXHR.setRequestHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
doiXHR.setRequestHeader("Access-Control-Allow-Headers", "Content-Type");
doiXHR.setRequestHeader("Access-Control-Max-Age", "86400"); // cache for 1 day
// doiXHR.withCredentials = "true";
doiXHR.send();
}
doiInfo('10.1002/bies.201000071')
In the browser console from crossref.org I get
[
{
"doi": "http://dx.doi.org/10.1002/bies.201000071",
"score": 18.623272,
"normalizedScore": 100,
"title": "The phage-host arms race: Shaping the evolution of microbes",
"fullCitation": "Adi Stern, Rotem Sorek, 2010, 'The phage-host arms race: Shaping the evolution of microbes', <i>BioEssays</i>, vol. 33, no. 1, pp. 43-51",
"coins": "ctx_ver=Z39.88-2004&rft_id=info%3Adoi%2Fhttp%3A%2F%2Fdx.doi.org%2F10.1002%2Fbies.201000071&rfr_id=info%3Asid%2Fcrossref.org%3Asearch&rft.atitle=The+phage-host+arms+race%3A+Shaping+the+evolution+of+microbes&rft.jtitle=BioEssays&rft.date=2010&rft.volume=33&rft.issue=1&rft.spage=43&rft.epage=51&rft.aufirst=Adi&rft.aulast=Stern&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&rft.genre=article&rft.au=Adi+Stern&rft.au=+Rotem+Sorek",
"year": "2010"
}
]
Running it from my website (not https) I get
OPTIONS http://search.crossref.org/dois?q=10.1002%2Fbies.201000071 404 (Not Found)
XMLHttpRequest cannot load http://search.crossref.org/dois?q=10.1002%2Fbies.201000071. Invalid HTTP status code 404
The GET/OPTIONS issue aside, it definitely seems to get a 404 on the page, which doesn't seem right.
I think you can get around it with an iframe and window.postMessage(?) but that sounds messy.
Please comment if I can provide more details and I'll be happy to, doesn't seem like anyone's done this before online - hopefully not because it's impossible!
Answering the title of your question: yes it allows Cross-Origin requests. A 404 indicates a wrong resource. Cross-origin problems would give you a 401.
The allow-origin header indicates that the resource can be accessed from all locations. Take a look at my working example: http://pastebin.com/8W23P48Z
Related
I'm implementing an API integration for DocuSign, and I'm currently hitting the following endpoint: /v2/organizations/{organizationId}/users
The documentaton for this: https://developers.docusign.com/docs/admin-api/reference/users/users/getusers/#response200_docusign.api.organizations.web.models.restapi.v2.response.organizationuserresponse
The documentation is showing a response field, user_status. However, when I call the API, I get a response as follows:
{
"users":[
{
"id":"xxx-xxx-xxx-xxx-xxx",
"user_name":"Xxxx",
"first_name":"",
"last_name":"Xxxx",
"membership_status":"active",
"email":"xxxx#gmail.com",
"membership_created_on":"2021-07-30T02:24:20.243",
"membership_id":"xxxx-xxxx-xxxx-xxxx-xxxx"
},
{
"id":"yyy-yyy-yyy-yyy-yyy",
"user_name":"Yyyyy",
"first_name":"Yyyyy",
"last_name":"2",
"membership_status":"active",
"email":"yyyyyyy#yyy.yyy",
"membership_created_on":"2021-07-30T02:26:59.313",
"membership_id":"yyy-yyy-yyy-yyy-yyy"
},
{
"id":"zzz-zzz-zzz-zzz-zzz",
"user_name":"Zzzzz",
"first_name":"Zzzz",
"last_name":"Zzzz",
"membership_status":"active",
"email":"zzz#zzz-zzz.net",
"membership_created_on":"2021-07-15T04:05:18.803",
"membership_id":"zzz-zzz-zzz-zzz-zzz"
}
],
"paging":{
"result_set_size":3,
"result_set_start_position":0,
"result_set_end_position":2,
"total_set_size":3
}
}
As you can see, we have no user_status. Do we need to send any request parameters, to expand the response, or has this field been removed from the API response without being updated on the API documentation?
Or, could I assume that the user is active, if it appears in the API response, with a membership_status of active?
Thank you very much!
membership_status is probably what you're looking for.
there's no such thing as user_status because a user can be a member of multiple accounts and each membership can have a different status.
Here is a useful diagram:
I am currently using SDK version 3.39.0 and version 0004 of the API_MKT_CONTACT service definition to try to create a new Contact with an AdditionalID in Marketing Cloud with the following code:
ODataRequestUpdate contactRequest =
contactService
.updateContactOriginData(contact)
.withHeader("Sap-Cuan-RequestTimestamp", getFormattedTime(System.currentTimeMillis()))
.withHeader("Sap-Cuan-SequenceId", "UpdatePatch")
.withHeader("Sap-Cuan-SourceSystemType", "EXT")
.withHeader("Sap-Cuan-SourceSystemId", "sdk-test")
.toRequest();
var additionalId =
AdditionalID.builder()
.externalContactID(pii.getId().toString())
.originOfContact(origin)
.originOfContact_2("EMAIL") //ContactAdditionalOrigin
.externalContactID_2(pii.getEmail()) //ContactAdditionalID
.build();
var additionalIdRequest = contactService
.updateAdditionalIDs(additionalId)
.replacingEntity()
.withHeader("Sap-Cuan-RequestTimestamp", getFormattedTime(System.currentTimeMillis()))
.withHeader("Sap-Cuan-SourceSystemType", "EXT")
.withHeader("Sap-Cuan-SourceSystemId", "sdk-test")
.toRequest();
// use low level API as a work around for https://github.com/SAP/cloud-sdk/issues/156
ODataRequestBatch requestBatch = new ODataRequestBatch(ContactService.DEFAULT_SERVICE_PATH, ODataProtocol.V2);
requestBatch
.beginChangeset()
.addUpdate(contactRequest)
.addUpdate(additionalIdRequest)
.endChangeset();
HttpClient httpClient = HttpClientAccessor.getHttpClient(contactsDestination);
ODataRequestResultMultipartGeneric batchResult = requestBatch.execute(httpClient);
batchResult.getResult(additionalIdRequest);
This results in the following error:
{
"error": {
"code": "SY/530",
"message": {
"lang": "en",
"value": "Inline component is not defined or not allowed (HTTP PUT)"
},
"innererror": {
"application": {
"component_id": "CEC-MKT-DM-IC",
"service_namespace": "/SAP/",
"service_id": "API_MKT_CONTACT_SRV",
"service_version": "0004"
},
"transactionid": "3B63A2A6CC9205E0E00604E1D31F1CDF",
"timestamp": "20210315142401.8432680",
"Error_Resolution": {
"SAP_Transaction": "For backend administrators: use ADT feed reader \"SAP Gateway Error Log\" or run transaction /IWFND/ERROR_LOG on SAP Gateway hub system and search for entries with the timestamp above for more details",
"SAP_Note": "See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)",
"Batch_SAP_Note": "See SAP Note 1869434 for details about working with $batch (https://service.sap.com/sap/support/notes/1869434)"
},
"errordetails": []
}
}
}
I am using this documentation as a guide for building my requests (under the section "Create Contacts with Additional IDs"). When I run the example code in Postman it works as expected. Note that the payload for the AdditionalIDs is an empty JSON object.
So I enabled HTTP wire logs and noticed that the SDK seems to be including the following payload:
PUT AdditionalIDs(ContactAdditionalOrigin='EMAIL',ContactAdditionalID='wade.watts#theoasis.com',ContactID='ae46e174-52a3-4de6-8caa-57213151b295',ContactOrigin='<CONTACT_ORIGIN>') HTTP/1.1
Sap-Cuan-SourceSystemId: sdk-test
Accept: application/json
Sap-Cuan-SourceSystemType: EXT
Content-Type: application/json
Sap-Cuan-RequestTimestamp: '2021-03-15T14:24:00.828'
{"ContactOrigin":"<CONTACT_ORIGIN>","ContactID":"ae46e174-52a3-4de6-8caa-57213151b295","ContactAdditionalOrigin":"EMAIL","ContactAdditionalID":"wade.watts#theoasis.com","ContactAdditionalIdUUID":null,"ContactUUID":null,"ContactAddlIDIsInvalid":null,"MarketingAreas":[]}
Unfortunately, I can't seem to find a way to omit the payload/inline component while using the SDK so that it matches the example code. Is this an issue with the SDK or am I doing something wrong? Any help would be much appreciated!
Cheers!
UPDATE
Applying the suggested workaround from #matkuhr I changed my additionalIdRequest above to this and it worked:
ODataEntityKey key = new ODataEntityKey(ODataProtocol.V2)
.addKeyProperty(AdditionalID.EXTERNAL_CONTACT_ID.getFieldName(), mcContact.getContactId())
.addKeyProperty(AdditionalID.ORIGIN_OF_CONTACT.getFieldName(), origin)
.addKeyProperty(AdditionalID.ORIGIN_OF_CONTACT_2.getFieldName(), "EMAIL")
.addKeyProperty(AdditionalID.EXTERNAL_CONTACT_I_D_2.getFieldName(), mcContact.getEmailAddress());
var request = new ODataRequestUpdate(
ContactService.DEFAULT_SERVICE_PATH,
"AdditionalIDs",
key,
"{}",
UpdateStrategy.REPLACE_WITH_PUT,
null,
ODataProtocol.V2);
request.addHeader("Sap-Cuan-RequestTimestamp", getFormattedTime(System.currentTimeMillis()));
request.addHeader("Sap-Cuan-SourceSystemType", "EXT");
request.addHeader("Sap-Cuan-SourceSystemId", "sdk-test);
It's not you doing something wrong, it's also not the SDK, it's the service. The service seems to substantially deviate from the OData V2 conventions as well as basic HTTP conventions.
You can work around this by leveraging the low-level APIs of the SDK even more. Create the update request fully manually with the payload the service requires, e.g.:
ODataEntityKey key = new ODataEntityKey(ODataProtocol.V2)
.addKeyProperty(Contact.XYZ.getFieldName(), contact.getXyz())
request = new ODataRequestUpdate(
contactService.getServicePath(),
contact.getEntityCollection(),
key,
"{ }", // this will be the payload
UpdateStrategy.REPLACE_WITH_PUT,
null,
ODataProtocol.V2);
request.addHeader("key", "val");
// add more headers & parameters if needed and execute
For a web service I'm working on, we're using varnish to map "user friendly" URLs to the longer URL where the page is actually located. So for example:
real URL: https://example.com/stuff/things/page.php
user-friendly URL: https://example.com/page
So the way we're achieving this in vcl is as follows:
if (req.url ~ "page") {
set req.url = stuff/things/page.php
}
But now I'm running into a problem in which the query string is getting wiped out in the process. So something to the effect of https://example.com/page?username=Ataraxia just gets mapped to https://example.com/stuff/things/page.php, but I need the query string to pass through to the mapped URL. Does anyone know how I could achieve this through varnish? I'm thinking I'll need to extract the query string via a regex operation and append it to the mapped URL, but I'm new to vcl and am unsure if this is possible, or if there's a better way to do it.
I did not test it but this might do the job:
if (req.url ~ "page") {
set req.url = "/stuff/things/page.php" + regsub(req.url, "[^?]*", "");
}
This is what i do, and works like a charm, i know this is 2 years later response, but might help someone.
if (req.url ~ "page") {
set req.url = regsub(req.url, "page", "stuff/things/page.php\1");
}
In my chrome extension I need to add a line to header of every site browsed. In background.js file I add such code:
var responseListener = function(details){
var rule = {
"name": "Access-Control-Allow-Origin",
"value": "*"
};
details.responseHeaders.push(rule);
return {responseHeaders: details.responseHeaders};
};
chrome.webRequest.onHeadersReceived.addListener(responseListener,
{urls: [ "*://*/*" ] },
["blocking", "responseHeaders"]);
While debugging the handler is called and newly added header successfully passes any filters I have found upper in the stack. But it is not seen on network tab's Response headers section and does not effects any code. I use these permissions:
"tabs","<all_urls>", "http://*/*" ,"webRequest","webRequestBlocking", "webNavigation"
Is there a new policy or API changed which disallow to do such things or there is some bug in my 10 lines of a code?
The Network tab of the Chrome Developer tools does not show the modifications from extensions. See https://crbug.com/258064
If you wish to see whether your extension has successfully modified a request, visit chrome://net-internals/#events, click on a request of type URL_REQUEST and look for URL_REQUEST_DELEGATE entries, e.g. URL_REQUEST_FAKE_RESPONSE_HEADERS_CREATED (this is an example of a log entry generated via the chrome.declarativeWebRequest API) or "delegate_info = "extension [extension name]" (generated by chrome.webRequest).
I'm displaying a list of articles in a page that are fetched using the Ember Data RESTAdapter. I need to implement a bootstrap'esque paginator (see: http://twitter.github.com/bootstrap/components.html#pagination) and cant seem to find a sane pattern for returning pagination data such as, page count, article count, current page, within a single request.
For example, I'd like the API to return something like:
{
articles: [{...}, {...}],
page: 3,
article_count: 4525,
per_page: 20
}
One idea was to add an App.Paginator DS.Model so the response could look like:
{
articles: [{...}, {...}],
paginator: {
page: 3,
article_count: 4525,
per_page: 20
}
}
But this seems like overkill to hack together for something so trivial. Has anyone solved this problem or found a particular pattern they like? Is there a simple way to manage the RESTAdapter mappings to account for scenarios such as this?
Try to use Ember Pagination Support Mixin and provide your own implementation of the following method. Instead of loading all the content, you can fetch the required content when the user is navigating the pages. All what you need initially is the total account of your records.
didRequestRange: function(rangeStart, rangeStop) {
var content = this.get('fullContent').slice(rangeStart, rangeStop);
this.replace(0, this.get('length'), content);
}
With ember-data-beta3 you can pass a meta-property in your result. The default RESTSerializer looks for that property and stores it.
You can access the meta-data like this:
var meta = this.get("store").metadataFor("post");
If you are not able to change the JSON returned from the server you could override the extractMeta-hook on the ApplicationSerializer (or any other Model-specific serializer).
App.ApplicationSerializer = DS.RESTSerializer.extend({
extractMeta: function(store, type, payload) {
if (payload && payload.total) {
store.metaForType(type, { total: payload.total }); // sets the metadata for "post"
delete payload.total; // keeps ember data from trying to parse "total" as a record
}
}
});
Read more about meta-data here