Spring hateoas pagination without using Spring Data - rest

I want to implement pagination for my REST API.
The application uses Spring Hateoas 0.8, Spring MVC 3.2.4 and Spring Integration 2.2.4.
I see that Spring Hateoas has the PagedResources class but this does not split up the results into pages.
It seems that spring-data is required to have properly created pages with previous and next links correctly created.
I have tried this but the links are not correct and all results are displayed:
#RequestMapping(value = "/resources", method = RequestMethod.GET, produces="application/json")
public HttpEntity<Resources<RequestResource>> showAllAsResources() {
Iterable<? extends Request> people = requestAccess.getRequests();
RequestAssembler assembler = new RequestAssembler();
List<RequestResource> resources = assembler.toResources(people);
Resources<RequestResource> wrapped = new Resources<RequestResource>(resources, linkTo(RequestsController.class)
.withSelfRel());
return new HttpEntity<Resources<RequestResource>>(wrapped);
}
All google searches seem to point to spring-data.
I can't use spring-data as I get my data from spring-integration which calls a soap web service and it returns all results for a search.
I did try adding spring-data classes:
#RequestMapping(value = "/resources2", method = RequestMethod.GET, produces="application/json")
public HttpEntity<PagedResources<Resource<RequestResource>>> viewResources()
{
List<Request> requests = requestAccess.getRequests2();
Page page = new PageImpl<Request>(requests, new PageRequest(0, 5), requests.size());
RequestAssembler requestAssembler = new RequestAssembler();
return new ResponseEntity<PagedResources<Resource<RequestResource>>>(pagedResourcesAssembler.toResource(page, requestAssembler), HttpStatus.OK);
}
This presents the response below which seems to have correct links but all data is presented and not just the page data:
{
"links": [
{
"rel": "next",
"href": "http://lwss:9084/lerest/v1/resources2?page=2&size=5"
},
{
"rel": "prev",
"href": "http://lwss:9084/lerest/v1/resources2?page=0&size=5"
}
],
"content": [
{
"givenName": "Joe",
"familyName": "Bloggs101",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/101"
}
]
},
...
{
"givenName": "Joe",
"familyName": "Bloggs200",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/101"
}
]
},
]
}
],
"page": {
"size": 5,
"totalElements": 100,
"totalPages": 20,
"number": 1
}
}
Is there any Spring classes that can help me achieve this or do I need to implement my own paging?
Many thanks,
Martin
UPDATE
Using the solution from Custom pagination by array list the following code produces the response below, though I think this is a bit of overkill. Are there any Spring helper classes that could achieve the same thing?
#RequestMapping(value = "/resources1", method = RequestMethod.GET, produces="application/json")
public HttpEntity<PagedResources<Resource<RequestResource>>> viewResources1(Pageable pageable)
{
int page = pageable.getPageNumber();
int count = pageable.getPageSize();
RequestAssembler requestAssembler = new RequestAssembler();
List<Request> requests = requestAccess.getRequests2();
int max = (count*(page+1)>requests.size())? requests.size(): count*(page+1);
Page<Request> pageRequests = new PageImpl<Request>(requests.subList(page*count, max), pageable, requests.size());
return new ResponseEntity<PagedResources<Resource<RequestResource>>>(pagedResourcesAssembler.toResource(pageRequests, requestAssembler), HttpStatus.OK);
}
{
"links": [
{
"rel": "next",
"href": "http://lwss:9084/lerest/v1/resources1?page=4&size=20"
},
{
"rel": "prev",
"href": "http://lwss:9084/lerest/v1/resources1?page=2&size=20"
}
],
"content": [
{
"givenName": "Joe",
"familyName": "Bloggs161",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/161"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs162",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/162"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs163",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/163"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs164",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/164"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs165",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/165"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs166",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/166"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs167",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/167"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs168",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/168"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs169",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/169"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs170",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/170"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs171",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/171"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs172",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/172"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs173",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/173"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs174",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/174"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs175",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/175"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs176",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/176"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs177",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/177"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs178",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/178"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs179",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/179"
}
]
},
{
"givenName": "Joe",
"familyName": "Bloggs180",
"links": [
{
"rel": "self",
"href": "http://lwss:9084/lerest/v1/180"
}
]
}
],
"page": {
"size": 20,
"totalElements": 100,
"totalPages": 5,
"number": 3
}
}

Related

Find if array has a value and then copy id from that array

I'm working on a script in Okta and am trying to return only specific data. Is it possible to look to see if the value of factorType is equal to sms then to get the corresponding ID?
This is an example of what I am being returned:
[
{
"id": "ufs2bysphxKODSZKWVCT",
"factorType": "question",
"provider": "OKTA",
"vendorName": "OKTA",
"status": "ACTIVE",
"created": "2014-04-15T18:10:06.000Z",
"lastUpdated": "2014-04-15T18:10:06.000Z",
"profile": {
"question": "favorite_art_piece",
"questionText": "What is your favorite piece of art?"
},
"_links": {
"questions": {
"href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/questions",
"hints": {
"allow": [
"GET"
]
}
},
"self": {
"href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/ufs2bysphxKODSZKWVCT",
"hints": {
"allow": [
"GET",
"DELETE"
]
}
},
"user": {
"href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL",
"hints": {
"allow": [
"GET"
]
}
}
}
},
{
"id": "ostf2gsyictRQDSGTDZE",
"factorType": "token:software:totp",
"provider": "OKTA",
"status": "PENDING_ACTIVATION",
"created": "2014-06-27T20:27:33.000Z",
"lastUpdated": "2014-06-27T20:27:33.000Z",
"profile": {
"credentialId": "dade.murphy#example.com"
},
"_links": {
"next": {
"name": "activate",
"href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/ostf2gsyictRQDSGTDZE/lifecycle/activate",
"hints": {
"allow": [
"POST"
]
}
},
"self": {
"href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/ostf2gsyictRQDSGTDZE",
"hints": {
"allow": [
"GET"
]
}
},
"user": {
"href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL",
"hints": {
"allow": [
"GET"
]
}
}
},
"_embedded": {
"activation": {
"timeStep": 30,
"sharedSecret": "HE64TMLL2IUZW2ZLB",
"encoding": "base32",
"keyLength": 16
}
}
},
{
"id": "sms2gt8gzgEBPUWBIFHN",
"factorType": "sms",
"provider": "OKTA",
"status": "ACTIVE",
"created": "2014-06-27T20:27:26.000Z",
"lastUpdated": "2014-06-27T20:27:26.000Z",
"profile": {
"phoneNumber": "+1-555-415-1337"
},
"_links": {
"verify": {
"href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/sms2gt8gzgEBPUWBIFHN/verify",
"hints": {
"allow": [
"POST"
]
}
},
"self": {
"href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/sms2gt8gzgEBPUWBIFHN",
"hints": {
"allow": [
"GET",
"DELETE"
]
}
},
"user": {
"href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL",
"hints": {
"allow": [
"GET"
]
}
}
}
}
]
As #IronMan commented, use ConvertFrom-Json to create an object. Then you can filter/select as desired.
$str = '[ { "id": "ufs2bysphxKODSZKWVCT", "factorType": "question", "provider": "OKTA", "vendorName": "OKTA", "status": "ACTIVE", "created": "2014-04-15T18:10:06.000Z", "lastUpdated": "2014-04-15T18:10:06.000Z", "profile": { "question": "favorite_art_piece", "questionText": "What is your favorite piece of art?" }, "_links": { "questions": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/questions", "hints": { "allow": [ "GET" ] } }, "self": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/ufs2bysphxKODSZKWVCT", "hints": { "allow": [ "GET", "DELETE" ] } }, "user": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL", "hints": { "allow": [ "GET" ] } } } }, { "id": "ostf2gsyictRQDSGTDZE", "factorType": "token:software:totp", "provider": "OKTA", "status": "PENDING_ACTIVATION", "created": "2014-06-27T20:27:33.000Z", "lastUpdated": "2014-06-27T20:27:33.000Z", "profile": { "credentialId": "dade.murphy#example.com" }, "_links": { "next": { "name": "activate", "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/ostf2gsyictRQDSGTDZE/lifecycle/activate", "hints": { "allow": [ "POST" ] } }, "self": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/ostf2gsyictRQDSGTDZE", "hints": { "allow": [ "GET" ] } }, "user": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL", "hints": { "allow": [ "GET" ] } } }, "_embedded": { "activation": { "timeStep": 30, "sharedSecret": "HE64TMLL2IUZW2ZLB", "encoding": "base32", "keyLength": 16 } } }, { "id": "sms2gt8gzgEBPUWBIFHN", "factorType": "sms", "provider": "OKTA", "status": "ACTIVE", "created": "2014-06-27T20:27:26.000Z", "lastUpdated": "2014-06-27T20:27:26.000Z", "profile": { "phoneNumber": "+1-555-415-1337" }, "_links": { "verify": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/sms2gt8gzgEBPUWBIFHN/verify", "hints": { "allow": [ "POST" ] } }, "self": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/sms2gt8gzgEBPUWBIFHN", "hints": { "allow": [ "GET", "DELETE" ] } }, "user": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL", "hints": { "allow": [ "GET" ] } } } } ]'
$obj = $str | ConvertFrom-Json
$obj | Where-Object factorType -eq 'sms' | Select-Object -ExpandProperty id
or
$str = '[ { "id": "ufs2bysphxKODSZKWVCT", "factorType": "question", "provider": "OKTA", "vendorName": "OKTA", "status": "ACTIVE", "created": "2014-04-15T18:10:06.000Z", "lastUpdated": "2014-04-15T18:10:06.000Z", "profile": { "question": "favorite_art_piece", "questionText": "What is your favorite piece of art?" }, "_links": { "questions": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/questions", "hints": { "allow": [ "GET" ] } }, "self": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/ufs2bysphxKODSZKWVCT", "hints": { "allow": [ "GET", "DELETE" ] } }, "user": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL", "hints": { "allow": [ "GET" ] } } } }, { "id": "ostf2gsyictRQDSGTDZE", "factorType": "token:software:totp", "provider": "OKTA", "status": "PENDING_ACTIVATION", "created": "2014-06-27T20:27:33.000Z", "lastUpdated": "2014-06-27T20:27:33.000Z", "profile": { "credentialId": "dade.murphy#example.com" }, "_links": { "next": { "name": "activate", "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/ostf2gsyictRQDSGTDZE/lifecycle/activate", "hints": { "allow": [ "POST" ] } }, "self": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/ostf2gsyictRQDSGTDZE", "hints": { "allow": [ "GET" ] } }, "user": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL", "hints": { "allow": [ "GET" ] } } }, "_embedded": { "activation": { "timeStep": 30, "sharedSecret": "HE64TMLL2IUZW2ZLB", "encoding": "base32", "keyLength": 16 } } }, { "id": "sms2gt8gzgEBPUWBIFHN", "factorType": "sms", "provider": "OKTA", "status": "ACTIVE", "created": "2014-06-27T20:27:26.000Z", "lastUpdated": "2014-06-27T20:27:26.000Z", "profile": { "phoneNumber": "+1-555-415-1337" }, "_links": { "verify": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/sms2gt8gzgEBPUWBIFHN/verify", "hints": { "allow": [ "POST" ] } }, "self": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL/factors/sms2gt8gzgEBPUWBIFHN", "hints": { "allow": [ "GET", "DELETE" ] } }, "user": { "href": "https://${yourOktaDomain}/api/v1/users/00u15s1KDETTQMQYABRL", "hints": { "allow": [ "GET" ] } } } } ]'
($str | ConvertFrom-Json) | Where-Object factorType -eq 'sms' | Select-Object -ExpandProperty id

Fetching actual data from Netsuite rather than HATEOAS links

While fetching the data from Netsuite using REST web Services. I am getting the following data as response. Is there any way where instead of HATEOAS links, I can get the actual data? I want to dump all the data to a file and in doing so I would have to make multiple calls with each id to get the data. Is there any way I can fetch all of the data with a single API call?
{
"links": [
{
"rel": "next",
"href": "https://demo123.suitetalk.api.netsuite.com/services/rest/record/v1/salesorder?limit=2&offset=2"
},
{
"rel": "last",
"href": "https://demo123.suitetalk.api.netsuite.com/services/rest/record/v1/salesorder?limit=2&offset=8"
},
{
"rel": "self",
"href": "https://demo123.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder/?limit=2"
}
],
"count": 2,
"hasMore": true,
"items": [
{
"links": [
{
"rel": "self",
"href": "https://demo123.suitetalk.api.netsuite.com/services/rest/record/v1/salesorder/39"
}
],
"id": "39"
},
{
"links": [
{
"rel": "self",
"href": "https://demo123.suitetalk.api.netsuite.com/services/rest/record/v1/salesorder/40"
}
],
"id": "40"
}
],
"offset": 0,
"totalResults": 8
}
Use the expandSubResources parameter as part of your get request. Set it to true to expand subitems and sublists.
hat tip #johnny for the assist!

Get BIM 360 Docs Project and Plans Folders

Sounds like an easy question right?
I want to the ID of the 2 ROOT Folders of BIM360 Docs.
The Plans & Project Folders.
2 Problems.... :(
The first is the ROOT Folders can be renamed inside of BIM360 Docs, so I can look by name or string value. (in the case of the renamed ROOT Folder it is renamed in the NAME and DISPLAYNAME)
The Second is if you use GetTopLevel Folder from Data Management API it gives me all the hidden folders as well.
So the automatic response is to say why not use the attributes of the folders and ignore the hidden folders....... low and behold the attributes of all folders are all set HIDDEN = False. Although clearly some of the folders do not appear in BIM360 Docs, so must be hidden another way.
Has anyone got any suggestions?
Thanks in Advance.
Happy New Year.
If you are calling the top folders API with an OAuth 2.0 Authorization Code Grant access token ("three-legged" token) you should be getting only the "Plans" and "Project Files" folders
Request:
curl -X GET -H "Authorization: Bearer YOUR_JWT_TOKEN"
"https://developer.api.autodesk.com/project/v1/hubs/b.hub.id.123/projects/b.project.id.xyz/topFolders"
Response:
{
"jsonapi": {
"version": "1.0"
},
"links": {
"self": {
"href": "https://developer.api.autodesk.com/project/v1/hubs/b.hub.id.123/projects/b.project.id.xyz/topFolders"
}
},
"data": [
{
"type": "folders",
"id": "urn:adsk.wipprod:fs.folder:co.CpjEV04YT_WDKXRkK5uiEg",
"attributes": {
"name": "Plans",
"displayName": "Plans",
"createTime": "2017-06-26T09:27:39.0000000Z",
"createUserId": "",
"createUserName": "",
"lastModifiedTime": "2017-06-26T09:27:39.0000000Z",
"lastModifiedUserId": "",
"lastModifiedUserName": "",
"lastModifiedTimeRollup": "2017-06-26T09:27:39.0000000Z",
"objectCount": 0,
"hidden": false,
"extension": {
"type": "folders:autodesk.bim360:Folder",
"version": "1.0",
"schema": {
"href": "https://developer.api.autodesk.com/schema/v1/versions/folders:autodesk.bim360:Folder-1.0"
},
"data": {
"visibleTypes": [
"items:autodesk.bim360:Document"
],
"actions": [
"CONVERT",
"SPLIT",
"OCR"
],
"allowedTypes": [
"folders:autodesk.bim360:Folder",
"items:autodesk.bim360:File",
"items:autodesk.bim360:Document",
"items:autodesk.bim360:TitleBlock",
"items:autodesk.bim360:ReviewDocument"
]
}
}
},
"links": {
"self": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.CpjEV04YT_WDKXRkK5uiEg"
}
},
"relationships": {
"contents": {
"links": {
"related": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.CpjEV04YT_WDKXRkK5uiEg/contents"
}
}
},
"parent": {
"data": {
"type": "folders",
"id": "urn:adsk.wipprod:fs.folder:co.8w-SI-ggRAuTQcTlD0GK6g"
},
"links": {
"related": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.CpjEV04YT_WDKXRkK5uiEg/parent"
}
}
},
"refs": {
"links": {
"self": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.CpjEV04YT_WDKXRkK5uiEg/relationships/refs"
},
"related": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.CpjEV04YT_WDKXRkK5uiEg/refs"
}
}
},
"links": {
"links": {
"self": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.CpjEV04YT_WDKXRkK5uiEg/relationships/links"
}
}
}
}
},
{
"type": "folders",
"id": "urn:adsk.wipprod:fs.folder:co.q7ElPL9HRTSpVuMP5-xq7w",
"attributes": {
"name": "Project Files",
"displayName": "Project Files",
"createTime": "2017-06-26T09:27:41.0000000Z",
"createUserId": "",
"createUserName": "",
"lastModifiedTime": "2017-11-08T13:06:58.0000000Z",
"lastModifiedUserId": "XXXXXXXX",
"lastModifiedUserName": "John%20Smith",
"lastModifiedTimeRollup": "2018-01-10T09:09:36.0000000Z",
"objectCount": 6,
"hidden": false,
"extension": {
"type": "folders:autodesk.bim360:Folder",
"version": "1.0",
"schema": {
"href": "https://developer.api.autodesk.com/schema/v1/versions/folders:autodesk.bim360:Folder-1.0"
},
"data": {
"visibleTypes": [
"items:autodesk.bim360:File"
],
"actions": [
"CONVERT"
],
"allowedTypes": [
"items:autodesk.bim360:File",
"folders:autodesk.bim360:Folder"
]
}
}
},
"links": {
"self": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.q7ElPL9HRTSpVuMP5-xq7w"
}
},
"relationships": {
"contents": {
"links": {
"related": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.q7ElPL9HRTSpVuMP5-xq7w/contents"
}
}
},
"parent": {
"data": {
"type": "folders",
"id": "urn:adsk.wipprod:fs.folder:co.8w-SI-ggRAuTQcTlD0GK6g"
},
"links": {
"related": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.q7ElPL9HRTSpVuMP5-xq7w/parent"
}
}
},
"refs": {
"links": {
"self": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.q7ElPL9HRTSpVuMP5-xq7w/relationships/refs"
},
"related": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.q7ElPL9HRTSpVuMP5-xq7w/refs"
}
}
},
"links": {
"links": {
"self": {
"href": "https://developer.api.autodesk.com/data/v1/projects/b.project.id.xyz/folders/urn:adsk.wipprod:fs.folder:co.q7ElPL9HRTSpVuMP5-xq7w/relationships/links"
}
}
}
}
}
]
}

JSON API: Correct way to show links?

I made a JSON API using Laravel with Laravel 5 JSON API Transformer package which was listed on jsonapi.org
For now, everything works as expected, a sample response from my api is like (btw the validation failed so i took a look at the jsonapi specs):
{
"data": [
{
"type": "inventory",
"id": "INV0001",
"attributes": {
"inv_inventory_id": "INV0001",
"inv_owner_company_id": 1,
"inv_owner_department_id": 1,
"inv_user_department_id": 1,
"inv_user_worker_id": 1,
"title": "Schreibtisch"
},
"links": {
"self": {
"href": "http://127.0.0.1:8000/api/v2/inventory/INV0001"
},
"user": {
"href": "http://127.0.0.1:8000/api/v2/worker/1"
},
"owner_dept": {
"href": "http://127.0.0.1:8000/api/v2/department/1"
},
"owner_comp": {
"href": "http://127.0.0.1:8000/api/v2/company/1"
}
},
"relationships": {
"worker": {
"data": {
"type": "worker",
"id": "1"
}
},
"department": {
"data": {
"type": "department",
"id": "1"
}
},
"company": {
"data": {
"type": "company",
"id": "1"
}
}
}
}
],
"included": [
{
"type": "worker",
"id": "1",
"attributes": {
"wrk_forename": "Moritz",
"wrk_surname": "ASDF",
"wrk_department_id": 2,
"wrk_homeoffice": true,
"wrk_room_id": 1
},
"links": {
"self": {
"href": "http://127.0.0.1:8000/api/v2/worker/1"
},
"hardware": {
"href": "http://127.0.0.1:8000/api/v2/worker/1/hardware"
},
"software": {
"href": "http://127.0.0.1:8000/api/v2/worker/1/software"
},
"inventory": {
"href": "http://127.0.0.1:8000/api/v2/worker/1/inventory"
},
"accessory": {
"href": "http://127.0.0.1:8000/api/v2/worker/1/accessory"
}
}
},
{
"type": "department",
"id": "1",
"attributes": {
"department": "Entwicklung",
"dept_floor_id": 3
},
"links": {
"self": {
"href": "http://127.0.0.1:8000/api/v2/department/1"
},
"floor": {
"href": "http://127.0.0.1:8000/api/v2/floor/3"
},
"hardware": {
"href": "http://127.0.0.1:8000/api/v2/department/1/hardware"
},
"software": {
"href": "http://127.0.0.1:8000/api/v2/department/1/software"
},
"inventory": {
"href": "http://127.0.0.1:8000/api/v2/department/1/inventory"
},
"accessory": {
"href": "http://127.0.0.1:8000/api/v2/department/1/accessory"
}
}
},
{
"type": "company",
"id": "1",
"attributes": {
"company": "GermanPersonnel",
"com_building_id": 1
},
"links": {
"self": {
"href": "http://127.0.0.1:8000/api/v2/company/1"
}
}
}
],
"links": {
"self": {
"url": "http://127.0.0.1:8000/api/v2/inventory?page[number]=1&page[size]=10"
},
"first": {
"url": "http://127.0.0.1:8000/api/v2/inventory?page[number]=1&page[size]=10"
},
"last": {
"url": "http://127.0.0.1:8000/api/v2/inventory?page[number]=1&page[size]=10"
}
},
"meta": {
"page": {
"total": 1,
"last": 1,
"number": 1,
"size": 10
}
},
"jsonapi": {
"version": "1.0"
}
}
But according to the specifications on jsonapi.org a link should look like
"links": {
"self": "http://127.0.0.1:8000/api/v2/inventory/INV0001"
},
My question is:
Is it legit to display a link like in my sample ouput as an object
with "href"? I'm pretty confused, because the package i used was
listed on jsonapi.org, but seems not to meet the specs.
BTW: My English might be a little bit confusing, but i hope i descriped my problem as well as possible
This is actually valid JSON API output base on the specification,
http://jsonapi.org/format/#document-links
Where specified, a links member can be used to represent links. The value of each links member MUST be an object (a “links object”).
A Valid Sample From Specification
"links": {
"related": {
"href": "http://example.com/articles/1/comments",
"meta": {
"count": 10
}
}
}
Each member of a links object is a “link”. A link MUST be represented as either:
a string containing the link’s URL.
an object (“link object”) which can contain the following members:
href: a string containing the link’s URL.
meta: a meta object containing non-standard meta-information about the link.
Therefore, your output is actually valid.

PayPal REST API - List of Refund Transactions

A few months ago I asked the following for a List of Sale Transactions:
PayPal Restful API - Pull List of Sale Transactions
In that question I got an answer referencing the List of Payment Resources call, which is great, but now I need to do something similar for refunds. However it's unclear to me from the documentation whether this includes Refunds.
Does the List of Payment Resources include Refund transactions (which would be a reverse payment of sorts), or is there some other way of accessing a list of Refund transactions?
Thanks in advance.
You currently cannot specifically get a list of refunded transactions. However, as you mention, you can get a list of payments and within that you will see the state of the sale object as refunded and a refund object with the state of completed:
Example:
{
"id": "PAY-ABCDEFGHIJKLMNO1234567890",
"create_time": "2013-09-20T15:44:04Z",
"update_time": "2013-09-20T16:10:05Z",
"state": "approved",
"intent": "sale",
"payer": {
"payment_method": "paypal",
"payer_info": {
"email": "bob-facilitator#example.com",
"first_name": "Bob",
"last_name": "Example",
"payer_id": "ABCDEFG123456"
}
},
"transactions": [
{
"amount": {
"total": "1.00",
"currency": "USD",
"details": {
"subtotal": "1.00"
}
},
"description": "example description",
"related_resources": [
{
"sale": {
"id": "ABCDEFGH123456789",
"create_time": "2013-09-20T15:44:04Z",
"update_time": "2013-09-20T16:10:05Z",
"state": "refunded",
"amount": {
"total": "1.00",
"currency": "USD"
},
"parent_payment": "PAY-ABCDEFGHIJKLMNO1234567890",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/12345678ABCDEFGIJ",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/12345678ABCDEFGIJ/refund",
"rel": "refund",
"method": "POST"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-ABCDEFGHIJKLMNO1234567890",
"rel": "parent_payment",
"method": "GET"
}
]
}
},
{
"refund": {
"id": "ABCDEFGH123456789",
"create_time": "2013-09-20T16:10:05Z",
"update_time": "2013-09-20T16:10:05Z",
"state": "completed",
"amount": {
"total": "1.00",
"currency": "USD"
},
"sale_id": "12345678ABCDEFGIJ",
"parent_payment": "PAY-57E15446PJ712294VKI6G2RA",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/refund/ABCDEFGH123456789",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-ABCDEFGHIJKLMNO1234567890",
"rel": "parent_payment",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/12345678ABCDEFGIJ",
"rel": "sale",
"method": "GET"
}
]
}
}
]
}
],
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-ABCDEFGHIJKLMNO1234567890",
"rel": "self",
"method": "GET"
}
]
}