How to set response code while using json views with Grails 3 - json

I am now in process of switching to use json view in one of my apps built with Grails 3.3
It all looks pretty simple and here is one of my controllers:
def create(ProjectCommand command) {
if (command.validate()) {
// do something with user
Project project = projectService.create(command, springSecurityService.principal.id as Long)
if (project) {
[status: HttpStatus.CREATED, project: project]
} else {
badRequest("failed to create the project")
}
}
else {
badRequest(command.errors)
}
}
Here, I assumed that the status will be used as a response status code, but it does not.
Is there an easy way to set status code of the response without explicitly going through render?

Hmmm... that was easy.
Apparently, inside the view file itself, there is a way to almost anything.
For this particular case, it is enough to do:
response.status HttpStatus.CREATED
I hope it will be useful to someone

Related

how can I verify response against a predefined json schema in karate?

Currently for checking answer response IO use below method:
And match response ==
"""
{
"status":#number,
"message":#string
}
"""
Is there any way to do like below?
And match response == someJsonSchemaDefinedInKarateConfigFile
Yes, refer to the documentation on reading files.
And match response == read('my-schema.json')
(edit): There was a comment requesting how to initialize these in karate-config.js
karate-config.js is intended for 'global' config, I really don't recommend dumping schemas here unless you are sure it will be used by almost all of your tests. But if you are reading from a file, it might be ok as it won't be a time consuming operation, remember karate-config.js is re-loaded for every Scenario.
Within karate-config.js you can easily load a JSON or JS file by using karate.read(). This should answer your question:
function() {
var config = {
};
config.mySchema = karate.read('classpath:my-schema.json');
return config;
}

Best way to get users folder using as-user in new Box Java SDK

Per Box example easy way to get user's root folder using below code
http://opensource.box.com/box-java-sdk/
BoxAPIConnection api = new BoxAPIConnection("your-developer-token");
BoxFolder rootFolder = BoxFolder.getRootFolder(api);
for (BoxItem.Info itemInfo : rootFolder) {
System.out.format("[%d] %s\n", itemInfo.getID(), itemInfo.getName());
}
But if i need to access someone else info using As-user, I'm unable to use BOX SDK classes (BoxFolder, BoxFile, BoxUser...) and need to get the data only from JSON directly like below.
If i do so, i'm loosing the latest features added in the new SDK. Is it the best way? How about the performance? Is there any alternative way available?
url= new URL("https://api.box.com/2.0/folders/0");
BoxAPIRequest request = new BoxAPIRequest(api,url,"GET");
request.addHeader("As-User", "12345678");
BoxJSONResponse response = (BoxJSONResponse) request.send();
JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
Later get the folder properties using JsonObject / JsonArray. If i need the folder items, i need to loop the JsonArray like below
JsonArray entries = responseJSON.get("entries").asArray();
for (JsonValue entry : entries)
{ ....}
Unfortunately, the new Java SDK beta doesn't have built-in support for "As-User" functionality yet, which makes this kind of tricky. One workaround is to use a RequestInterceptor with your BoxAPIConnection to manually add the "As-User" header to every request.
api.setRequestInterceptor(new RequestInterceptor() {
#Override
public BoxAPIResponse onRequest(BoxAPIRequest request) {
request.addHeader("As-User", "user-id");
// Returning null means the request will be sent along with our new header.
return null;
}
}
This should let you use the rest of the SDK normally and not have to worry about doing the API requests manually. I also created an issue for adding "As-User" support.

Rails 4 API JSON: drying up the response object

I am creating an API that uses JSON to communicate back/forth with external view apps (Angular). In a lot of API actions, I return a JSON response that is 99% the same as the error one below:
# controller
def create
#record = Record.new(record_params)
if #record.save
#record
else
render json: {
error: {
type: "invalid_request",
message: "Could not create record. Params: #{record_params}",
errors: #record.errors.messages
}
}, status: 404
end
end
Is there a convenient way to DRY this up? I ask specifically because I know certain methods such as the render only work in controller classes because they are inherited.
I'm thinking about something like the following:
render json: API::ErrorObject.call(#record, record_params), status: 404
And in that class it would be:
class API::ErrorObject
self.call(object, params)
{
error: {
type: "invalid_request",
message: "Could not create record. Params: #{record_params}",
errors: object.errors.messages
}
}
end
end
I think that would work, but is there an even cleaner way to abstract away some of this behavior? The API is fairly large, so there are 30+ places where very similar code will reside. I know that someday someone will request an addition to the API responses, and having a single place to update this would be a lot better than 30...
The best solution I've found so far is to bring the V back into MVC by using a tool like Jbuilder.
Using this you can really DRY up your code similar to what you do using partials in ERB.

Play 2.0 routes file for different configurations

I have a Play 2.0 application with 3 different configurations (application.conf, test.conf and prod.conf)
Now I have a robots.txt file that should be delivered for only test.conf and for the rest environments it should give a 404 if someone tries to access it.
How can I configure my routes file to check if my application is using test.conf? Can I set some variable in test.conf that I can check in the routes file?
Something like this? (pseudo code)
#{if environment = "test"}
GET /robots.txt controllers.Assets.at(path="/public", file="robots.txt")
#{/if}
#{else}
GET /robots.txt controllers.Application.notFoundResult()
#{/else}
You can't add logic in the routes file.
I'd write a controller to serve the robots.txt file. Something like this:
In the routes file:
GET /robots.txt controllers.Application.robots
Then, in the controller, I'll test if I'm in a testing environment :
def robots = Action {
if (environment == "test") { // customize with your method
Redirect(routes.Assets.at("robots.txt"))
} else {
NotFound("")
}
}
I'm using Scala, but it can be easily translated to Java.
Edit - java sample
You can check if application is in one of three states: prod, dev or test, ie, simple method returning current state:
private static String getCurrentMode() {
if (play.Play.isTest()) return "test";
if (play.Play.isDev()) return "dev";
if (play.Play.isProd()) return "prod";
return "unknown";
}
you can use as:
play.Logger.debug("Current mode: "+ getCurrentMode());
of course in your case that's enough to use these condition directly:
public static Result robots() {
return (play.Play.isProd())
? notFound()
: ok("User-agent: *\nDisallow: /");
}

Node.js: Extend JSON object

I would like to extend the native JSON object of node.js. In my example, I was able to extend it to add a merge(json1, json2) function, which merges 2 JSONs:
JSON.merge = function(target) {
var sources = [].slice.call(arguments, 1);
sources.forEach(function (source) {
for (var prop in source) {
target[prop] = source[prop];
}
});
return target;
}
This script is larglely inspired from this question: Combine or merge JSON on node.js without jQuery
To have a cleaner code, I put this script in a library folder, and require it in my main script. This is what I did...
My lib/JSON.js file:
extends.merge = function(target){
// The same script as above.
}
and in my app.js file:
JSON = require('./lib/JSON')
It works well, but now, native JSON methods don't work (like .stringify). Then I digged a little bit, and tried to extend JSON using .prototype, like this (in lib/JSON.js):
extends.prototype.merge = ...
But get the following error:
TypeError: Cannot set property 'merge' of undefined
I really don't know what to do (and what I'm doing) since solving a problems causes another problem.
Please, need help.
Thanks.