RKPaginator Params - restkit

how can I use the "RKPaginator" with a custom "page paramter? My API doesn't provide a "page" param, it uses a pattern like this for pagination:
"http://my.domain.com/api/lists/?limit=5&offset=10"
"offset" is the param for the next page.
The JSON return form my API looks like this:
"meta": {
"next": "/api/lists/?limit=5&offset=15",
"total_count": 22,
"previous": "/api/lists/?limit=5&offset=5",
"limit": 5,
"offset": 10
},
Or would it be easier to customize tastypie to handle requests like this?
"http://my.domain.com/api/lists/?page=1"
Which methods I've to override?
Thanks a lot.

Override the RKPaginator::loadPage method with a objective-c category and calculate the offset from the page number and limit as shown in the following lines of code:
#import "RKPaginator+Tastypie.h"
#import <objc/runtime.h>
#implementation RKPaginator (Tastypie)
- (void)TastypieLoadPage:(NSUInteger)pageNumber
{
[self TastypieLoadPage:(pageNumber-1) * self.perPage];
}
+ (void)load {
method_exchangeImplementations(class_getInstanceMethod(self, #selector(loadPage:)), class_getInstanceMethod(self, #selector(TastypieLoadPage:)));
}
#end
And here comes the construction of the RKPaginator object:
RKPaginator *paginator = [[RKObjectManager sharedManager] paginatorWithPathPattern:#"yourResource/?limit=:perPage&offset=:currentPage"];

Related

badRequest: Invalid requests[0]: No request set. (Google::Apis::ClientError)

I can't solve this problem. Can someone help me?
Issue: badRequest: Invalid requests[0]: No request set. (Google::Apis::ClientError)
I just need to include formulas in a cell sequence.
Here is the code:
requests: [
{
repeatCell: {
range: {
sheetId: sheet_id,
startRowIndex: 0,
endRowIndex: 10,
startColumnIndex: 1,
endColumnIndex: 6
},
cell: {
userEnteredValue: {
formulaValue: "=FLOOR(A1*PI())"
}
},
fields: "*"
}
}
]
}
response = service.batch_update_spreadsheet(spreadsheet_id, request) ```
Unfortunately, although I cannot see your whole script in your question, it is found that in your request body, you use the camel case. I think that this is the reason of your issue. In the case of Ruby, please use the snake case as follows.
Modified script:
request = {
requests: [{repeat_cell: {
range: {
sheet_id: 0,
start_row_index: 0,
end_row_index: 10,
start_column_index: 1,
end_column_index: 6
},
cell: {
user_entered_value: {
formula_value: "=FLOOR(A1*PI())"
}
},
fields: "*"
}}]
}
response = service.batch_update_spreadsheet(spreadsheet_id, request, {})
Note:
In this modification, it supposes that your service can be used for using the batchUpdate method of Sheets API to the Google Spreadsheet of spreadsheet_id. Please be careful this.

Type of return data in API platform

I have a problem of changing types of returned data in API platform:
I have an entity:
final class ModelClass
{
/**
* #var float
*/
public $total;
}
And a configuration:
ModelClass:
properties:
total:
attributes:
swagger_context:
type: float
And Controller:
public function __invoke(CustomRequest $request): Paginator
{
return $this->service->getTotals($request);
}
The return of this is Paginator, which holds custom doctrine query, result of which looks like this:
{
"#type": "hydra:Collection",
"hydra:member": [
{
"id": 1,
"total": "120.00",
},
]
}
As you see, total is a string (because in the result of query it is a string). What i want it to be: a float: "total": 120.00. And what i would also like to be able to do, is to format it differently, for example separator sign ',' instead '.'
I didnt find in documentation how to do it. Is it a missing documentation or missing feature?
I would expect that API platform reads DocBlock to understand the type of Model. And i think that there is some intercept mechanism after query is executed, but before response is sent back to client, so i could change format/type.
Thank you.

Setter not called

I am building a simple location tracker in NativeScript. The current location and the past ones are all stored in a view model called scope and constantly updated. When new location data is available, the setter addLocation is called.
var scope = new observableModule.fromObject({
"locations": [],
"lastLocation": {
"latitude": 0,
"longitude": 0,
"altitude": 0,
"horizontalAccuracy": 0,
"verticalAccuracy": 0,
"direction": 0,
"timestamp": "No location yet",
"speed": 0
},
set addLocation(loc) {
console.log('Setter called');
this.lastLocation = loc;
this.locations.push(loc);
}
});
This is imported as view model to the page. The setter is called simply like this:
scope.addLocation = loc;
where loc is the output of watchLocation() from the GeoLocation plugin.
It seems that the setter is not triggered though. The console message never appears. I tried adding other setters, but they also don't work. Getters are working. What is going on?

How do I return only selected certain fields in Strapi?

Pretty straightforward (I hope). I'd like to be able to use the API endpoint and have it only return specified fields. I.E. something like this
http://localhost:1337/api/reference?select=["name"]
Would ideally return something of the form
[{"name": "Ref1"}]
Unfortunately that is not the case, and in actuality it returns the following.
[
{
"contributors": [
{
"username": "aduensing",
"email": "standin#gmail.com",
"lang": "en_US",
"template": "default",
"id_ref": "1",
"provider": "local",
"id": 1,
"createdAt": "2016-07-28T19:39:09.349Z",
"updatedAt": "2016-07-28T19:39:09.360Z"
}
],
"createdBy": {
"username": "aduensing",
"email": "standin#gmail.com",
"lang": "en_US",
"template": "default",
"id_ref": "1",
"provider": "local",
"id": 1,
"createdAt": "2016-07-28T19:39:09.349Z",
"updatedAt": "2016-07-28T19:39:09.360Z"
},
"updatedBy": {
"username": "aduensing",
"email": "standin#gmail.com",
"lang": "en_US",
"template": "default",
"id_ref": "1",
"provider": "local",
"id": 1,
"createdAt": "2016-07-28T19:39:09.349Z",
"updatedAt": "2016-07-28T19:39:09.360Z"
},
"question": {
"createdBy": 1,
"createdAt": "2016-07-28T19:41:33.152Z",
"template": "default",
"lang": "en_US",
"name": "My Question",
"content": "Cool stuff, huh?",
"updatedBy": 1,
"updatedAt": "2016-07-28T19:45:02.893Z",
"id": "579a5ff83af4445c179bd8a9"
},
"createdAt": "2016-07-28T19:44:31.516Z",
"template": "default",
"lang": "en_US",
"name": "Ref1",
"link": "Google",
"priority": 1,
"updatedAt": "2016-07-28T19:45:02.952Z",
"id": "579a60ab5c8592c01f946cb5"
}
]
This immediately becomes problematic in any real world context if I decide to load 10, 20, 30, or more records at once, I and end up loading 50 times the data I needed. More bandwidth is used up, slower load times, etc.
How I solved this:
Create custom controller action (for example, 'findPaths')
in contributor/controllers/contributor.js
module.exports = {
findPaths: async ctx => {
const result = await strapi
.query('contributor')
.model.fetchAll({ columns: ['slug'] }) // here we wait for one column only
ctx.send(result);
}
}
Add custom route (for example 'paths')
in contributor/config/routes.json
{
"method": "GET",
"path": "/contributors/paths",
"handler": "contributor.findPaths",
"config": {
"policies": []
}
},
Add permission in admin panel for Contributor entity, path action
That's it. Now it shows only slug field from all contributor's records.
http://your-host:1337/contributors/paths
Here is how you can return specific fields and also exclude the relations to optimize the response.
async list (ctx) {
const result = await strapi.query('article').model.query(qb => {
qb.select('id', 'title', 'link', 'content');
}).fetchAll({
withRelated: []
}).catch(e => {
console.error(e)
});
if(result) {
ctx.send(result);
} else {
ctx.send({"statusCode": 404, "error": "Not Found", "message": "Not Found"});
}
}
I know this is old thread but I just run into exactly same problem and I could not find any solution. Nothing in the docs or anywhere else.
After a few minutes of console logging and playing with service I was able to filter my fields using following piece of code:
const q = Post
.find()
.sort(filters.sort)
.skip(filters.start)
.limit(filters.limit)
.populate(populate);
return filterFields(q, ['title', 'content']);
where filterFields is following function:
function filterFields(q, fields) {
q._fields = fields;
return q;
}
It is kinda dirty solution and I haven't figured out how to apply this to included relation entites yet but I hope it could help somebody looking for solution of this problem.
I'm not sure why strapi does not support this since it is clearly capable of filtering the fields when they are explicitly set. it would be nice to use it like this:
return Post
.find()
.fields(['title', 'content'])
.sort(filters.sort)
.skip(filters.start)
.limit(filters.limit)
.populate(populate);
It would be better to have the query select the fields rather than relying on node to remove content. However, I have found this to be useful in some situations and thought I would share. The strapi sanitizeEntity function can include extra options, one of which allows you only include fields you need. Similar to what manually deleting the fields but a more reusable function to do so.
const { sanitizeEntity } = require('strapi-utils');
let entities = await strapi.query('posts').find({ parent: parent.id })
return entities.map(entity => {
return sanitizeEntity(entity, {
model: strapi.models['posts'],
includeFields: ['id', 'name', 'title', 'type', 'parent', 'userType']
});
});
This feature is not implemented in Strapi yet. To compensate, the best option for you is probably to use GraphQL (http://strapi.io/documentation/graphql).
Feel free to create an issue or to submit a pull request: https://github.com/wistityhq/strapi
You can use the select function if you are using MongoDB Database:
await strapi.query('game-category').model.find().select(["Code"])
As you can see, I have a model called game-category and I just need the "Code" field so I used the Select function.
In the current strapi version (3.x, not sure about previous ones) this can be achieved using the select method in custom queries, regardless of which ORM is being used.
SQL example:
const restaurant = await strapi
.query('restaurant')
.model.query((qb) => {
qb.where('id', 1);
qb.select('name');
})
.fetch();
not very beautiful,but you can delete it before return.
ref here:
https://strapi.io/documentation/developer-docs/latest/guides/custom-data-response.html#apply-our-changes
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
async find(ctx) {
let entities;
if (ctx.query._q) {
entities = await strapi.services.restaurant.search(ctx.query);
} else {
entities = await strapi.services.restaurant.find(ctx.query);
}
return entities.map(entity => {
const restaurant = sanitizeEntity(entity, {
model: strapi.models.restaurant,
});
if (restaurant.chef && restaurant.chef.email) {
**delete restaurant.chef.email;**
}
return restaurant;
});
},
};
yeah,I remember another way.
you can use the attribute in xx.settings.json file.
ref:
model-options
{
"options": {
"timestamps": true,
"privateAttributes": ["id", "created_at"], <-this is fields you dont want to return
"populateCreatorFields": true <- this is the system fields,set false to not return
}
}
You can override the default strapi entity response of:-
entity = await strapi.services.weeklyplans.create(add_plan);
return sanitizeEntity(entity, { model: strapi.models.weeklyplans });
By using:-
ctx.response.body = {
status: "your API status",
message: "Your own message"
}
Using ctx object, we can choose the fields we wanted to display as object.
And no need to return anything. Place the ctx.response.body where the response has to be sent when the condition fulfilled.
It is now 2023, and for a little while it has been possible to do this using the fields parameter:
http://localhost:1337/api/reference?fields[0]=name&fields[1]=something

JqGrid custom formatter with custom parameter

I have a question about custom formatters.
What I try to achieve is a currencyFormatter just for the amount with Locale sent by the server, when locale is not define or supported fall back to British English.
Something like this:
function currencyFmatter(cellvalue, options, rowdata) {
return new Intl.NumberFormat([locale, "en-GB"], {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(cellvalue);
}
My problem is how to pass my variable locale to the formatter, I’m pretty sure it has to be a way to do it but right now I don’t see it.
Thanks
It's an interesting question! There are many ways to implement your requirements.
1) you can extend your input data returned from the server with additional information which specify the locale of data. For example you can returns "de-DE:10.000,04" instead of "10.000,04" which represent 1000.04 formatted in German locale (where , will be used as the decimal separator and . used as the thousands separator). It allows you to use cellvalue.split(":") to get array ["de-DE", "10.000,04"] with the locale of the number and the number itself
function currencyFmatter(cellvalue, options, rowdata) {
var data;
if (typeof cellvalue === "string") {
data = cellvalue.cellvalue.split(":");
if (data.length === 2) {
return new Intl.NumberFormat([data[0], "en-GB"], {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(data[1]);
}
}
return cellvalue;
}
Alternatively you can place the information about locale of the number in separate field (for example numLocale) of the input data and use something like rowdata.numLocale (or rowdata[12] depend on the input format of the JSON data) to access the locale.
2) It could be that all the data returned from the server will be in the same format. In the case it would be not the best way to prepend all numbers with the same prefix "de-DE:". What you can do for example is to extend the data returned from the server with additional field. For example you can use
{
"total": "12",
"page": "1",
"records": "12345",
"localOfNumbers": "de-DE",
"rows" : [
...
]
}
You can access the custom localOfNumbers field inside of beforeProcessing callback. It's very practical callback. It allows you to pre-process the data returned from the server before the data will be processed by jqGrid. I recommend you to read the answer and this one for more code example. What you can do for example is to save localOfNumbers value in some new option of jqGrid (see the answer for more details). Let us you want to have an option gridLocale for the goal. Then you can do something like the following:
beforeProcessing: function (data) {
if (typeof data.localOfNumbers === "string") {
$(this).jqGrid("setGridParam", {gridLocale: data.localOfNumbers});
}
}
To access the new gridLocale option you can use
function currencyFmatter(cellvalue, options, rowdata) {
var locale = $(this).jqGrid("getGridParam", "gridLocale"); // this.p.gridLocale
}
3) You can consider to save the information about the locale as column property instead of usage one common gridLocale grid option. To do this you can define the column in colModel like below
{ name: 'col2', width: 200, formatoptions: { colLocale: "en-IN" },
formatter: function (cellvalue, options, rowdata) {
// options.colModel.formatoptions.colLocale get you the value
}
One can set the property of formatoptions.colLocale inside of beforeProcessing too. You can use
{
"total": "12",
"page": "1",
"records": "12345",
"localOfColumns": {
"col2": "de-DE",
"col5": "en-IN"
},
"rows" : [
...
]
}
and
beforeProcessing: function (data) {
if ($.isPlainObject(data.localOfColumns)) {
if (typeof data.localOfColumns.col2 === "string") {
$(this).jqGrid("setColProp", "col2", {
formatoptions: { colLocale: data.localOfColumns.col2 }
});
}
if (typeof data.localOfColumns.col5 === "string") {
$(this).jqGrid("setColProp", "col5", {
formatoptions: { colLocale: data.localOfColumns.col5 }
});
}
}
}
I'm sure that one can suggest even more ways to implement your requirements.

Resources