Spring Data Rest : How to expose a json schema from a repository (2.0.0.M1) - spring-data-rest

I saw in the source code that Spring DATA Rest can expose a Json Schema for a repository with this URL : /{repository}/schema.
Is there anybody who know how to configure this ?
There is the RepositorySchemaController (org.springframework.data.rest.webmvc) but i have not found how to use it.
version : 2.0.0.M1

Make sure you set the right headers...
Request - /{repository}/schema
Header - Accept: application/json+schema
Also if you haven't looked into 2.0 snapshots, there are lot more features and changes coming up
EDIT: Jan 27 2014
Correction:
Accept should be "application/schema+json" instead of "application/json+schema"
Request - /{repository}/schema
Header - Accept: application/schema+json

Just an update for version 2.4.0 of Spring Data REST:
JSONSchema is now exposed under the profile link, so you need to change your request as follows:
Request: /profile/{repository}
Header: Accept: application/schema+json
You can find the updated documentation here.

This actually returns the ALPS style representation. If you are interested in other formats, e.g. JSON Schema, you might want to add your own controller to be flexible:
#BasePathAwareController
public class JsonSchemaController {
public static final String PROFILE_ROOT_MAPPING = "/schema";
public static final String RESOURCE_PROFILE_MAPPING = PROFILE_ROOT_MAPPING + "/{repository}";
#RequestMapping(value = RESOURCE_PROFILE_MAPPING, method = GET)
public ResponseEntity<JsonNode> descriptor(RootResourceInformation information) {
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(
SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON);
SchemaGeneratorConfig config = configBuilder.build();
SchemaGenerator generator = new SchemaGenerator(config);
JsonNode jsonSchema = generator.generateSchema(information.getPersistentEntity().getType());
return ResponseEntity.ok(jsonSchema);
}
}
Dependencies for Snow:
implementation 'com.github.victools:jsonschema-generator:4.16.0'

Related

Generate Link To Spring Data Rest Controller

I created a REST API with Spring Data Rest that forks fine. It must be possible to clone Projects via the API, so I added a custom #RestController to implement that via POST /projects/{id}/clone.
#RestController
#RequestMapping(value = "/projects", produces = "application/hal+json")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class ProjectCloneController {
private final ProjectRepo projectRepo;
#PostMapping("/{id}/clone")
public EntityModel<Project> clone(#PathVariable String id) {
Optional<Project> origOpt = projectRepo.findById(id);
Project original = origOpt.get();
Project clone = createClone(original);
EntityModel<Project> result = EntityModel.of(clone);
result.add(linkTo(ProjectRepo.class).slash(clone.getId()).withSelfRel());
return result;
}
I am stuck at the point where I need to add a Link to the EntityModel that points to an endpoint provided by Spring Data Rest. It will need to support a different base path, and act correctly to X headers as well.
Unfortunately, the line above (linkTo and slash) just generates http://localhost:8080/636f4aaac9143f1da03bac0e which misses the name of the resource.
Check org.springframework.data.rest.webmvc.support.RepositoryEntityLinks.linkFor

Does StringContentProvider set Content-Type header in HTTP request?

I am trying to use Firebase Cloud Messaging by Google with the help of Jetty HTTP client:
public static final String FCM_URL = "https://fcm.googleapis.com/fcm/send";
public static final String FCM_KEY = "key=AAAA....";
private final HttpClient mHttpClient = new HttpClient();
private final CompleteListener mFcmListener = new CompleteListener() {
#Override
public void onComplete(Result result) {
if (result.isFailed()) {
// TODO delete FCM token in database for certain responses
}
}
};
mHttpClient.start();
mHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.content(new StringContentProvider(notificationStr), "application/json")
.send(mFcmListener);
My question is very simple, but I couldn't find the answer myself yet by looking at the StringContentProvider and its base classes -
If I need to set the request HTTP header for FCM:
Content-Type: application/json
then do I have to add the line:
.header(HttpHeader.CONTENT_TYPE, "application/json")
or will that class already do it for me?
A couple of points:
Yes, if you don't set content type header explicitly, it would be auto set based on the selected Content Provider.
By default, the StringContentProvider sets Content-Type to text/plain. To override, you need to use another constructor -
new StringContentProvider("application/json", content, StandardCharsets.UTF_8);
Request #setContent method auto sets Content-Type header to the provided value. Hence, you need not make any change to the code.

How can I make RestSharp use BSON?

I'm using RestSharp, and using Json.NET for serialization (see here).
Json.NET supports BSON, and since some of my requests have huge blocks of binary data, I think this would speed up my application dramatically. However, as far as I can tell, RestSharp does not seem to have any in-built support for BSON.
The use of Json.NET is implemented as a custom serializer for RestSharp, and so at first glance it looks like it would be possible to modify that custom serializer to use BSON. But, the Serialize method of the RestSharp.Serializers.ISerializer interface returns a string - which is (I assume) unsuitable for BSON. So, I assume that it would take some more significant changes to RestSharp to implement this change.
Has anyone figured out a way to do this?
Update: I looked at the RestSharp source, and discovered that the RestRequest.AddBody method that takes my object and serializes it into the request body eventually calls Request.AddParameter (with the serialized object data, and the parameter type RequestBody).
I figured that I might be able to serialize my object to BSON and then call Request.AddParameter directly - and indeed I can. However, when RestSharp then executes the RestRequest, it fails to put the binary content into the request, because there are other embedded assumptions about the request content being UTF-8 encoded.
Thus it looks like this hack would not work - there would need to be some changes made to RestSharp itself, and I'm not the man for the job...
Update 2: I decided to have a go at using the debugger to figure out how much of RestSharp I'd have to change to overcome the body-encoding issue, so I swapped out my NuGet version of RestSharp and included the RestSharp project in my solution. And... it worked.
It turns out that there has been a change to RestSharp in the last few months that isn't yet in the NuGet version.
So, you can now use AddParameter and pass in an already-BSON-encoded object, and RestSharp will send it off to the server without complaint.
Per the updates in my question, it turns out that if you have the latest RestSharp source, then instead of this:
request.AddBody(myObject);
... you can do this instead whenever you have a payload that would benefit from using BSON:
using (var memoryStream = new System.IO.MemoryStream())
{
using (var bsonWriter = new Newtonsoft.Json.Bson.BsonWriter(memoryStream))
{
var serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Serialize(bsonWriter, myObject);
var bytes = memoryStream.ToArray();
request.AddParameter("application/bson", bytes, RestSharp.ParameterType.RequestBody);
}
}
Note that the first parameter to AddParameter is supposedly the parameter name, but in the case of ParameterType.RequestBody it's actually used as the content type. (Yuk).
Note that this relies on a change made to RestSharp on April 11 2013 by ewanmellor/ayoung, and this change is not in the current version on NuGet (104.1). Hence this will only work if you include the current RestSharp source in your project.
Gary's answer to his own question was incredibly useful for serializing restful calls. I wanted to answer how to deserialize restful calls using JSON.NET. I am using RestSharp version 104.4 for Silverlight. My server is using Web API 2.1 with BSON support turned on.
To accept a BSON response, create a BSON Deserializer for RestSharp like so
public class BsonDeserializer : IDeserializer
{
public string RootElement { get; set; }
public string Namespace { get; set; }
public string DateFormat { get; set; }
public T Deserialize<T>(IRestResponse response)
{
using (var memoryStream = new MemoryStream(response.RawBytes))
{
using (var bsonReader = new BsonReader(memoryStream))
{
var serializer = new JsonSerializer();
return serializer.Deserialize<T>(bsonReader);
}
}
}
}
Then, ensure your request accepts "application/bson"
var request = new RestRequest(apiUrl, verb);
request.AddHeader("Accept", "application/bson");
And add a handler for that media type
var client = new RestClient(url);
client.AddHandler("application/bson", new BsonDeserializer());

Request and Response Headers Override using Restler

I am new to restler and trying to do the following things, can't seem to get hold of it
I have this class and method exposed via Restler
class Account {
protected $Api_Version = array('version' => "1.0.2.1234", 'href' => "/");
// Returns the version of the service
// Content-Type: application/vnd.cust.version+json
function version() {
return json_encode($this->version);
}
// Accepts only Content Type: application/vnd.cust.account+json
function postCreate() {
}
}
1) I want to return my own Content-Type to client like in the 'version' method instead of default application/json. In my case its 'application/vnd.cust.version+json'
2) Method postCreate should only accept the request if the Contet-Type is set to 'application/vnd.cust.account+json'. How to check if that header is set in the request.
3) Also in the restler API Explorer, for methond name, how can I show only the method name instead of the 'version.json'. I want to show just 'version' like the method name
Thank you for your help.
Narsi
1) maybe Write your own format? Take a Look at
http://restler3.luracast.com/examples/_003_multiformat/readme.html
2) you could check the headers and throw Exception on wrong one.
Take a Look at this link
http://stackoverflow.com/questions/541430/how-do-i-read-any-request-header-in-php
3) have you tried to and the following line to your Index.php?
Resources::$useFormatAsExtension = false
Hope takes you further on :)
CU
Inge

WCF: (MTOM) is there any way to change the scheme used in xop:Content reference uris generated by WCF?

WCF uses http://tempuri/1/number for Content-ID uri references when handling streamed MTOM requests.
Is there any way how to force WCF to use a different Content-ID references for the xop:Include?
Background of the problem:
I am building a .NET client for MTOM enabled jax ws java web service that handles streamed large data uploads. I have hand crafted the service and data contacts (the WSDL generated contracts were not correct and did not allow streaming).
The problem is that the web service (jax ws) does not receive the request body containing the data.
It receives the data that is transferred in headers.
We have built a java client for the ws - this one works.
I have captured and compared the HTTP traffic when issuing requests from java and wcf, and the only difference is in how Content-ID reference is generated when posting the multipart data:
WCF uses http://tempuri/1/... Content-ID references which yield in encoded value, like href="cid:http%3A%2F%2Ftempuri.org%2F1%2F634019957020047928"
Java client uses "email-style" uris, like href="cid:3f3ec388-8cd9-47aa-a603-fb1bc17935b8#example.jaxws.sun.com"
These yield in the following xop-includes (Data is the only element in the soap body) (XOP includes specification)
//WCF:
<Data>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:http%3A%2F%2Ftempuri.org%2F1%2F634019957020047928" />
</Data>
//JAVA:
<Data>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:3f3ec388-8cd9-47aa-a603-fb1bc17935b8#example.jaxws.sun.com"/>
</Data>
later on, in the multipart data, the content is referred to by unencoded Content-ID:
--uuid:7e166bb7-042f-4ba3-b6ef-98fbbc21244b+id=1
Content-ID: <http://tempuri.org/1/634019957020047928>
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream
I guess there may be a bug in the jax web service framework and it is not recognizing WCF-generated+urlencoded Content-ID uri references.
Is there any way how to force WCF to use a different Content-ID references for the xop:Include?
EDIT: I have found the XmlMtomWriter which has the GenerateUriForMimePart method, this is used to generate Content-IDs.
public static string GenerateUriForMimePart(int index)
{
return string.Format(CultureInfo.InvariantCulture,
"http://tempuri.org/{0}/{1}", new object[] { index, DateTime.Now.Ticks });
}
It does not seem that the ID generation is in any way overridable.
A similar issue is described here, the answer provided does not help: http://social.msdn.microsoft.com/Forums/en/wcf/thread/f90affbd-f431-4602-a81d-cc66c049e351
Asnwering to myself after long investigation: Not possible without reimplementing the whole XmlMtomWriter and other related layers and concerns in WCF - almost everything involved in the mtom implementation is internal.
I know it is an old question. But I'm faced the same problem two days ago.
I found a way which works BUT it is a VERY VERY dirty hack (I know that. I thought about not publishing it here but perhaps it would help somebody.) Hopefully you will not blame me for that.
The ContentId is formatted with the use of CultureInfo.InvariantCulture. I didn't find an official way for replacing it with a custom CultureInfo. But with the help of reflection I got it running. The following implementation is only for .Net 4.0.
public class NoTempUriInvariantCultureInfo : CultureInfo, ICustomFormatter
{
private static CultureInfo originalCulture;
private static object originalCultureLock;
private static int enableCounter;
private NoTempUriInvariantCultureInfo(CultureInfo invariantCulture)
: base(invariantCulture.Name)
{
originalCulture = invariantCulture;
}
public static void Enable()
{
if(originalCultureLock == null)
originalCultureLock = new object();
lock (originalCultureLock)
{
if (enableCounter == 0)
{
var mInvCultField = typeof (CultureInfo).GetField("s_InvariantCultureInfo", BindingFlags.NonPublic | BindingFlags.Static);
mInvCultField.SetValue(null, new NoTempUriInvariantCultureInfo(CultureInfo.InvariantCulture));
}
enableCounter++;
}
}
public static void Disable()
{
lock (originalCulture)
{
if (enableCounter == 0)
return;
enableCounter--;
if (enableCounter == 0)
{
var mInvCultField = typeof (CultureInfo).GetField("s_InvariantCultureInfo", BindingFlags.NonPublic | BindingFlags.Static);
mInvCultField.SetValue(null, NoTempUriInvariantCultureInfo.originalCulture);
}
}
}
public override object GetFormat(Type formatType)
{
var result = originalCulture.GetFormat(formatType);
return result ?? this;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (format == null)
return System.Text.RegularExpressions.Regex.Replace(arg.ToString().Replace("http%3A%2F%2Ftempuri.org%2F1%2F", ""), "http[:][/][/]tempuri[.]org[/][0-9]+[/]*", "");
return String.Format("{0:" + format + "}", arg);
}
}
I enable my own "InvariantCulture" only before a WCF call.
NoTempUriInvariantCultureInfo.Enable();
try
{
// make your call
}
finally
{
NoTempUriInvariantCultureInfo.Disable();
}
CultureInfo.InvariantCulture is a global state object. Enabling my own InvariantCulture affects every other thread.
Again, it is a dirty hack. But it works.
Both of the XOP includes samples that you indicated are correct and acceptable according to the W3C. I refer to them as the URL format and the Email format respectively.
I am not a JAVA developer, but recall a similiar problem when interfacing with a particular JAVA web service. I recall there being a bug in a particular JAVA release and after they (the JAVA developers) upgraded to the next release version, this issue simply went away. I wish I could provide you more details, but at the time, there were enough problems for me to address from my end of the wire and I was just glad to have one less item on the defect log.
//WCF: using URL format
<Data>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:http%3A%2F%2Ftempuri.org%2F1%2F634019957020047928" />
</Data>
//JAVA: using EMAIL format
<Data>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:3f3ec388-8cd9-47aa-a603-fb1bc17935b8#example.jaxws.sun.com"/>
</Data>