When we create our apps, we usually add our own routes.
So using the baucis.rest i added some custom routes like example:
var controller = baucis.rest( {
singular: 'User'
} );
controller.put('/myroute/:id', function(req,res,done){
//doing something
})
My app runs and using the swagger ui i can see the operations about users.
GET /Users/{id} description
PUT /Users/{id} description
......
I would like to add my "/myroute/:id' to the generated swagger api.
PUT /Users/myroute/{id} description
Does anyone know how to do about this?
As of v0.9.0 , you can modify the swagger definitions directly. It's purely cosmetic, only altering the swagger documentation, not any other functionality.
For example:
var controller = baucis.rest('User');
controller.swagger.apis.push({
'path': '/Users/myroute/{id}',
'description': 'Myroute custom description.',
'operations': [
{
'httpMethod': 'PUT',
'nickname': 'putSomethingCustom',
'responseClass': 'User',
'summary': 'Something custom.'
}
]
})
controller.swagger.models is also exposed.
Related
Im working with Strapi v3.4.0 for a project. I want to allow the authenticated user to update his personal informations when hitting the following endpoint:
PUT /users/me
for now I'm just trying to console.log a message when i hit the endpoint, I have followed the step from this video: https://www.youtube.com/watch?v=ITk-pYtOCnQ but I keep getting 403 error "Forbidden". Am I doing something wrong?
I understand this update me problem in Strapi is really painful. I have also followed the tutorial on the link you put above to no avail since Strapi just updated to the new version. I tried a workaround for this solution. So basically, I tried to create an additional method on the model that shows on the api folder (Previously, I am trying to add a new method on user controller but again I got this 403 error as well). As for my approach, I created a model called User Profile and then add the custom method for updating the current user data shown below.
api/user-profile/config/routes.json
...
{
"method": "PUT",
"path": "/updateMe",
"handler": "user-profile.updateMe",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/user-profiles/:id",
"handler": "user-profile.update",
"config": {
"policies": []
}
}
...
and on the controller, I added the following snippets
api/user-profile/controllers/user-profile.js
'use strict';
const _ = require('lodash');
const { sanitizeEntity } = require('strapi-utils');
const sanitizeUser = user =>
sanitizeEntity(user, {
model: strapi.query('user', 'users-permissions').model,
});
const formatError = error => [
{ messages: [{ id: error.id, message: error.message, field: error.field }] },
];
module.exports = {
async updateMe(ctx) {
const { id } = ctx.state.user;
let updateData = {
...ctx.request.body,
};
const data = await strapi.plugins['users-permissions'].services.user.edit({ id }, updateData);
ctx.send(sanitizeUser(data));
},
};
After that, enable the updateMe for the public use (for now, you do not have to worry since the ctx.state.user line will check if the token is valid or not, { at least until the developer officially create this updateMe function })
That is all. You just have to pass the attributes to the body and the auth header and you are good to go. You can pretty much put the function anywhere you want
PS: Take a note that you have to put the updateMe route above the update route. Else, it will give you the same 403 error
PPS: This is just a workaround until the developer officially includes the updateMe function, which we cannot expect in short time
Greeting everyone, I have a datatable in my html page that I populated using REST API. I can create new row and also update or delete by selecting a row and clicking the edit or delete button.
But currently I am unable to delete update or delete multiple row at once due to url error,
e.g : PUT http://127.0.0.1:8000/dashboard/content_detail/5,7,9/ 404 (Not Found)
how can I split this this into several separate url with respective id when I update or delete.
e.g :
/dashboard/content_detail/5
/dashboard/content_detail/7
/dashboard/content_detail/9
Below is my code, any help is much appreciated thank you.
idSrc: 'id',
ajax: {
create: {
type: 'POST',
url: content_path,
data: function (content_data) {
var create_data = {};
$.each(content_data.data, function (id, value) {
create_data['name'] = value['name'];
create_data['description'] = value['description'];
create_data['category'] = value['category'];
});
return create_data;
},
success: function () {
content_table.api().ajax.reload();
}
},
edit: {
type: 'PUT',
url: '/dashboard/content_detail/_id_/',
data: function (content_data) {
var updated_data = {};
$.each(content_data.data, function (id, value) {
updated_data['description'] = value['description'];
updated_data['category'] = value['category'];
updated_data['name'] = value['name'];
});
return updated_data;
},
success: function () {
content_table.api().ajax.reload();
}
},
remove: {
type: 'DELETE',
url: '/dashboard/content_detail/_id_/',
data: function (content_data) {
var deleted_data = {};
$.each(content_data.data, function (id, value) {
deleted_data['id'] = id;
});
return deleted_data;
},
success: function () {
content_table.api().ajax.reload();
}
}
},
If you're going to allow the update of a large number of items at once, then PATCH might be your friend:
Looking at the RFC 6902 (which defines the Patch standard), from the client's perspective the API could be called like
PATCH /authors/{authorId}/book
[
{ "op": "replace", "path": "/dashboard/content_detail/5", "value": "test"},
{ "op": "remove", "path": "/dashboard/content_detail", "value": [ "7", "9" ]}
]
From a design perspective you don't want several ids in your url.
I would prefer single calls for each change, thinking in resources you only manipulate one at a time.
In case this is a perfomance issue, I recommend a special url marked with action or something simliar, to make clear this ist not REST.
In HTTP it is not required for information to only exist on a single resource. It is possible to have multiple resources that represent the same underlying data.
It's therefore not out of the question to create a resource that 'represents' a set of other resources that you wish to DELETE or PUT to.
I do agree that it might not be the most desirable. I think we tend to prefer having information only exist in a single part of tree, and I think we like to avoid situations where updating a resource effects a secondary resource's state. However, if you are looking for a strictly RESTful solution to solve this problem, I think it's the right way.
Therefore a url design such as:
/dashboard/content_detail/5,7,9/
Is not necessarily non-RESTful or goes against the HTTP protocol. The fact that you're getting a 404 on that URL currently has to do with your application framework, not the protocol (HTTP) or architecture (REST) of your API.
However, for cases such as these I feel I would personally be inclined to sometimes create a separate POST endpoint that, acting outside of REST like an RPC endpoint. Specifically for these types of batch requests.
So, I'm trying to show how Restivus API works and Meteor is nice to my colleagues. :)
I've made a simple blog app at http://askar-blog.meteor.com/ (thanks to DiscoverMeteor book).
My repo https://github.com/tenzan/blog
(I'm reading https://github.com/kahmali/meteor-restivus#restivus)
I have three collections:
users
posts
comments
So, post has many comments. Usually, we used to have comments as a nested documents inside of a post, but from the Meteor's nature these two attributes are split up into different collections.
I want to implement a REST API, so that I can access (including CRUD operations) posts and collections in the way:
http://example.com/api/posts - all posts
http://example.com/api/posts/post_id - a specific post
http://example.com/api/posts/post_id/comments - all comments that belongs to a given post
http://example.com/api/posts/post_id/comments/comment_id - a specific comment that belongs to a given post
If you have a look at my repo, you will see there're posts.js and comments.js under lib/collections.
As I understood, to enable REST API, I will need the following snippet in the posts.js:
if (Meteor.isServer) {
// Global API configuration
var Api = new Restivus({
useDefaultAuth: true,
prettyJson: true
});
// Generates: GET, POST on /api/post and GET, PUT, DELETE on
// /api/items/:id for the Posts collection
Api.addCollection(Posts);
// Generates: POST on /api/users and GET, DELETE /api/users/:id for
// Meteor.users collection
Api.addCollection(Meteor.users, {
excludedEndpoints: ['getAll', 'put'],
routeOptions: {
authRequired: true
},
endpoints: {
post: {
authRequired: false
},
delete: {
roleRequired: 'admin'
}
}
});
As you see, I've added Api.addCollection(Posts); and I've confirmed I can access all posts or a specific one.
My questions:
1- How can I setup API to access comments for their parent post?
2 - Will I have to have to following code to access posts ? I'm asking because, I'm already able to access them as I have Api.addCollection(Posts); :
Maps to: /api/posts/:id
Api.addRoute('posts/:id', {authRequired: true}, {
get: function () {
return Posts.findOne(this.urlParams.id);
},
delete: {
roleRequired: ['author', 'admin'],
action: function () {
if (Articles.remove(this.urlParams.id)) {
return {status: 'success', data: {message: 'Post removed'}};
}
return {
statusCode: 404,
body: {status: 'fail', message: 'Post not found'}
};
}
}
});
I apologise, I got confused myself trying to figure out the correct way of making a REST API.
Please feel free to add anything important on this regard I have missed here.
I've discussed with the author of the package
https://github.com/kahmali/meteor-restivus/issues/128
The feature is being developed
https://github.com/kahmali/meteor-restivus/issues/70
In order to create a many-to-many association between models, I use the blueprints to access something like:
/api/item/1/tags/2
How can I protect this action using policies?
This action doesn't seem to fit any of the find/create/update/destroy policies.
There's no need for custom routing here; the blueprint you're referring to is called populate, so it can be protected in your config/policies.js with:
ItemController: {
populate: 'somePolicy'
}
check this:
module.exports.routes = {
//Set blueprints
'GET /findAllUsers': {model: 'user', blueprint: 'find'},
'GET /user/findAll': {blueprint: 'find'}
'GET /user/findAll': {blueprint: 'find', model: 'pet'}
// Set policies in routes
'/foo': {policy: 'myPolicy'}
// Mix of blueprints and policies
'GET /mix-of-both': [
{policy: 'isLoggued'},
{blueprint: 'find', model: 'tag'}
]
}
Check the official docs: http://sailsjs.org/#/documentation/concepts/Routes/RouteTargetSyntax.html
I hope it helps!
Is there a way in Ember to configure a custom REST url for a specific Model?
Like with this model:
App.Post = DS.Model.extend({
title: DS.attr('string'),
comments: DS.hasMany('App.Comment')
});
App.Comment = DS.Model.extend({
content: DS.attr('string'),
post: DS.belongsTo('App.Post')
});
And this Store:
app.Store = DS.Store.extend({
revision : 11,
adapter : DS.RESTAdapter.extend({
namespace : 'rest'
})
});
I want that the comments are retrieved via /rest/post/{id}/comments instead of /rest/comments which is the default behaviour.
Is it possible to configure a rest-url for one specific Model?
You can register an additional adapter and 'scope' it to your model.
App.Store.registerAdapter('App.Post', DS.RESTAdapter.extend({
url: "/rest/post/"
}));
Is this for literally just for one model across your entire app or is this the default "hasMany" uri that your REST backend uses? I ask because my api (django rest framework) uses this exact uri and it required a full blown pull request on the ember-data project because to build the URL the adapter needs the related "parent" or "owner" (something rails devs never needed so it didn't exist).
I would write your own adapter (just subclass the base adapter so you only override the single hasMany that is different). The method I wrote for my adapter is below and here is my full blown adapter for reference.
This is ember-data revision 11 friendly btw (have not upgraded to 12 yet)
https://github.com/toranb/ember-data-django-rest-adapter/blob/master/tests/adapter.js
findMany: function(store, type, ids, parent) {
var json = {}
, root = this.rootForType(type)
, plural = this.pluralize(root)
, ids = this.serializeIds(ids)
, url = this.buildFindManyUrlWithParent(store, type, ids, parent);
this.ajax(url, "GET", {
success: function(pre_json) {
json[plural] = pre_json;
Ember.run(this, function(){
this.didFindMany(store, type, json);
});
}
});
},