What is the best way to handle nested futures? - fluent

I have users and groups in my system. Users belong to groups using a sibling relationship. I need to transform the Users/Groups into an Array of users/groups to return to the client. When I lookup a user I get a Future, if I then map the use and attempt to access its groups I get a Future.
What is the best way of handling nested futures so I can get to the actual data and munge it?
struct ReturnUserStruct {
var userId: Int
var primaryGroupName: String
}
let futureUsers = Users.query(on: request).all()
futureUsers.map { users in
var returnUsersArray<returnUserStruct> = []
users.ForEach { user in
var returnUserStruct: ReturnUserStruct
returnUserStruct.userId = user.id
// This a sibling relationship that returns a Future
primaryGroupFuture = user.primaryGroup
primaryGroupFuture.map { group in
returnUserStruct.primaryGroupName = group.name
}
returnUsersArray.append(returnUserStruct)
}
return returnUsersArray
This code doesn't work. The map call are obviously scheduled asynchronously and returnUsersArray ends up empty. I can rearrange this and get the user ids into the return array but haven't figured out a good way to get the groupName.

Good to hear about Fluent4 and Vapor4. Here is the solution I've settled on.
return Users.query(on: request)
.all()
.and(Group.find(user.primaryGroup).first())
.map { (users, groupOptional) -> [ReturnUserStruct]
guard let group = groupOptional else {
Abort(.notFound)
}
var returnArray: Array<ReturnUserStruct>
users.forEach { user in
var returnUserStruct: ReturnUserStruct
returnUserStruct.userId = user.id
returnUserStruct.primaryGroupName = group.name
returnArray.append(returnUserStruct)
}
}
It seems to work, here's hope .and sticks around.
Thanks

Related

How to delete all in json server

I am using this json server in my Angular app, to create, fetch, and delete posts.
In the following method, I delete a post with a specified id:
deleteConsumer(post: Post): Observable<Post> {
const url = `${this.apiUrl}/${post.id}`;
return this.httpClient.delete<Post>(url);
}
I looked at the .delete code and searched for something like a .deleteall but could not find it. Is there really no such method that would delete everything?
If there really isn't, then my attempt at doing it myself is not paying off, because what I have done is not working:
deleteConsumers(): Observable<Post> {
let i: number = 0;
this.httpClient.get<Post[]>(this.apiUrl).forEach(
() => {
++i;
const url = `${this.apiUrl}/${i}`;
return this.httpClient.delete<Post>(url);
}
);
}
Obviously, this is wrong in terms of return type, but I cannot figure out what to do... How can I modify the first method, so it would go through all the json objects in my db.json file; meaning iterate through all the existing posts and delete them all?
I did encounter this when using json-server with Vue.js and I realized that there was no special function to delete all at once. I had to work around it.
So, for example in your case, I would first map the posts array to get a new array with only the post ids:
const postsIdsArray = this.posts.map((post) => post.id)
Then, assuming you already have a function to delete one post given the id, I would then execute the function for each of the ids in the array:
postsIdsArray.forEach((id) => this.deletePost(id))
Just combine the two lines in one JavaScript function (in this case I used Vue.js):
deleteAllPosts(){
const postsIdsArray = this.posts.map((post) => post.id)
postsIdsArray.forEach((id) => this.deletePost(id))
}

Combine two Json files exported from wordpress

I have two Json files that I exported from wordpress that have corresponding ID's I want to combine them into one Json file so I can bring it into website I am building with Gatsby JS. One of the files is the posts.json and the other is postsMeta.json. The post_id in postsMeta corresponds with the ID in Posts
How would I best go about merging the two? Can I run some sort of for loop in js and how would I so? I am on windows is there a json explorer of some sorts that could help me do this.
lastly I would also like to trim out some of the unnecasry fiels such as post_parent in the posts json and something like the meta_key in the postsMeta json.
Ok hopefully this is clear enough, thanks in advance.
Here is an example of the first object corresponding pairs in the two files
posts.json
{"ID":"19","post_author":"2","post_date":"2010-12-31 23:02:04","post_date_gmt":"2010-12-31 23:02:04","post_content":"Harry Potter was not available for the first sitting of the Halloween Picture. I hope everyone had a safe and fun Halloween. Tomorrow is picture retake day, please send back your previous prints if you want retakes. It is also hot lunch. See You tomorrow!","post_title":"Happy Halloween","post_excerpt":"","post_status":"publish","comment_status":"open","ping_status":"open","post_password":"","post_name":"happy-halloween","to_ping":"","pinged":"","post_modified":"2011-01-03 05:26:11","post_modified_gmt":"2011-01-03 05:26:11","post_content_filtered":"","post_parent":"0","guid":"http:\/\/localhost\/mrskitson.ca_wordpress\/?p=19","menu_order":"0","post_type":"post","post_mime_type":"","comment_count":"1"},
postsMeta.json
{"meta_id":"27","post_id":"19","meta_key":"large_preview","meta_value":"http:\/\/www.mrskitson.ca\/wp-content\/uploads\/2010\/12\/halloween.jpg"},
Update:
this is an attempt to solve this problem with the current answer, you can edit the code there.
How would I best go about merging the two?
Is it mandatory for you combine the two JSON files/data?
Because you could just require or load the JSON data from within your script (or even put them in the HTML) and then to get the meta value of a specific meta field/key, this function could do that:
// `single` has no effect if `meta_key` is empty.
function getPostMeta( post_id, meta_key, single ) {
let id = String( post_id ),
pm = [];
postsMeta.map( m => {
let a = ( ! meta_key ) ||
( meta_key === m.meta_key );
if ( a && id === m.post_id ) {
pm.push( m );
}
});
let meta = {},
mk = {};
pm.map( m => {
let k = m.meta_key, v;
if ( undefined === meta[ k ] ) {
meta[ k ] = m.meta_value;
} else {
v = meta[ k ];
if ( undefined === mk[ k ] ) {
meta[ k ] = [ v ];
mk[ k ] = 1;
}
meta[ k ].push( m.meta_value );
m[ k ]++;
}
});
pm = null;
mk = meta_key ? mk[ meta_key ] : null;
if ( mk ) {
return single ?
meta[ meta_key ][0] : // Returns a single meta value.
meta[ meta_key ]; // Returns all the meta values.
}
return meta_key ?
meta[ meta_key ] : // Returns the value of the `meta_key`.
meta; // Or returns all the post's meta data.
}
The data I used for testing: (take note of the postsMeta in the above/getPostMeta() function)
// Array of `post` objects.
const posts = [{"ID":"19","post_author":"2","post_date":"2010-12-31 23:02:04","post_date_gmt":"2010-12-31 23:02:04","post_content":"Harry Potter was not available for the first sitting of the Halloween Picture. I hope everyone had a safe and fun Halloween. Tomorrow is picture retake day, please send back your previous prints if you want retakes. It is also hot lunch. See You tomorrow!","post_title":"Happy Halloween","post_excerpt":"","post_status":"publish","comment_status":"open","ping_status":"open","post_password":"","post_name":"happy-halloween","to_ping":"","pinged":"","post_modified":"2011-01-03 05:26:11","post_modified_gmt":"2011-01-03 05:26:11","post_content_filtered":"","post_parent":"0","guid":"http:\/\/localhost\/mrskitson.ca_wordpress\/?p=19","menu_order":"0","post_type":"post","post_mime_type":"","comment_count":"1"}];
// Array of `meta` objects.
const postsMeta = [{"meta_id":"27","post_id":"19","meta_key":"large_preview","meta_value":"http:\/\/www.mrskitson.ca\/wp-content\/uploads\/2010\/12\/halloween.jpg"},{"meta_id":"28","post_id":"19","meta_key":"many_values","meta_value":"http:\/\/facebook.com"},{"meta_id":"29","post_id":"19","meta_key":"many_values","meta_value":"http:\/\/twitter.com"},{"meta_id":"30","post_id":"19","meta_key":"many_values","meta_value":"http:\/\/linkedin.com"}];
Examples: (see this Fiddle for demo)
// In these examples, we are retrieving the meta value for the post #19 (i.e. ID is 19).
// Retrieve a single value.
// Returns mixed; string, number, etc.
let url = getPostMeta( 19, 'large_preview', true );
console.log( url );
// Retrieve all meta values.
// Always returns an array of values.
let ms = getPostMeta( 19, 'many_values' );
console.log( ms, ms[0] );
// Retrieve all meta data.
// Always returns an object with meta_key => meta_value pairs. I.e. { key => value, ... }
let ma = getPostMeta( 19 );
console.log( ma, ma.large_preview, ma.many_values[0] );
But if you really must combine the JSON data, you can do: (again, see demo on the same Fiddle)
// Here we modify the original `posts` object.
posts.map( p => {
// Add all the post's meta data.
p.meta = getPostMeta( p.ID );
// Delete items you don't want..
delete p.post_parent;
delete p.menu_order;
// delete ...;
});
console.log( JSON.stringify( posts[0].meta ) ); // posts[0].meta = object
console.log( posts[0].post_parent, posts[0].menu_order ); // both are undefined
And then if you want to copy-paste the new/merged JSON data:
JSON.stringify( posts );
But if you actually just want to do something with the post's meta, you can loop through the posts object and do the thing; e.g.:
// Here the original `posts` object is not modified, and that we don't
// (though you can) repeatedly call `getPostMeta()` for the same post.
posts.map( p => {
// Get all the post's meta data.
let meta = getPostMeta( p.ID );
// Do something with `meta`.
console.log( meta.large_preview );
});
console.log( JSON.stringify( posts[0].meta ) ); // posts[0].meta = undefined
console.log( posts[0].post_parent, posts[0].menu_order ); // both still defined
// posts[0].meta wouldn't be undefined if of course posts[0] had a `meta` item,
// which was set in/via WordPress...
If you can do this in js, there's a pretty easy approach using Array#map. If you simplify your question, you're really asking how to add this meta data under each entry in posts, and get only the fields you want.
I'm assuming the posts.json is actually an array (e.g. [{"ID":"19"....).
// Load these server-side, fetch them remotely, copy-paste, etc.
// I'll require them here for simplicity
const posts = require('./posts.json');
const postsMeta = require('./postsMeta.json');
// Build a Map so we can quickly look up the metas by post_id
// Extract what we need by destructuring the args
const metaByPost = postsMeta.reduce((a, {
post_id: id,
meta_value: value,
}) => a.set(id, {
value,
/* anything else you want in here */,
}), new Map());
const mergedPosts = posts.map(post => ({
// Spread in the post
...post,
// Spread in the meta content
...metaByPost.get(post.ID),
// Undefine the props we don't want
post_parent: undefined,
}));
I don't love manually setting stuff to undefined -- I think it's nicer to explicitly say what props you're going to include, instead of loading everything and undefining certain props.
Try this snippet directly in the Chrome DevTools console:
(function(
postsUrl='https://cdn.glitch.com/61300ea6-6cc4-4cb6-a62f-31adc62ea5cc%2Fposts.json?1525386749382',
metaUrl='https://cdn.glitch.com/61300ea6-6cc4-4cb6-a62f-31adc62ea5cc%2Fpostmeta.json?1525386742630'
) {
Promise.all([
fetch(postsUrl).then(r => r.json()),
fetch(metaUrl).then(r => r.json()),
]).then(([postsResponse, metaResponse]) => {
// Inspected the actual JSON response to come up with the data structure
const posts = postsResponse[2].data;
const meta = metaResponse[2].data;
const metaByPostId = meta.reduce((accum, el) => {
accum[el.post_id] = el;
return accum;
}, {});
const transformedPosts = posts.map(post => {
const merged = {
...post,
...(metaByPostId[post.ID] || {}),
};
delete merged.post_parent;
// delete any other fields not wanted in the result
return merged;
});
console.log(transformedPosts);
});
})();
replace URLs accordingly, I used the ones from Glitch example here
as commented, actual data is buried in response[2].data. Use Network tab / Parsed view to see structure
replace console.log with copy, if you want the result copied to clipboard, instead of logged to console
Blunt to the point for your question. We want to:
merge var a = {/*some json*/} into var b = {/*another json*/}
trim fields in var exclusions = ["post_parent","meta_key"]
Merge the JSONS
First, we need to populate a and b.
Your JSONs are parsable into Javascript objects with JSON.parse():
let a = JSON.parse(/*JSON here*/);
let b = JSON.parse(/*JSON here*/);
Because how properties are defined in Javascript, if you define a property again, the second definition will overwrite the first. Your JSONS contain only strings as keys and strings as values, so a shallow copy will suffice. Object.assign() will copy all the properties (field and values) into the first argument and return the final Object. Therefore this will merge a into b, assuming they have different keys, else the values in b will overwrite values in a:
a = Object.assign(a,b);
Otherwise, if they are not disjoint, you have to define some policy on how to join, for example may prioritize one. Below, we keep the values in a instead:
a = Object.assign(b,a);
Since you mentionmed a for loop, the line below does the same as two code lines above and will also allow show you an example on how to write your own custom lambda expression:
Object.keys(a).forEach(k=>b[k]=b[k]?b[k]:a[k]);
Do not wish to touch a and b? Create a third object c.
let c = Object.assign({},a,b)
Lastly (wait until the trim step below is accomplished) JSON.stringify() will convert your merged object back into JSON.
Trim exclusions
Following the third example, we have c merged with all the fields.
First a little hack taken from here:
Object.filter = (obj, predicate) => Object.keys(obj)
.filter( key => predicate(obj[key]))
.reduce( (res, key) => (res[key] = obj[key], res), {} );
Now Objects, just like arrays have a filter prototype, having extended Object prototype. It is not really best practice since this will extend every Object but this function works quite well with respects to the semantics of Javascript and this example serves as an opportunity to keep elegant Javascript styles code:
c = Object.filter(c, key=> !exclusions.includes(key) );
Voit-lá, done.
As for defined Object.filter() it uses Array.filter() and Array.reduce() . Click for reference, for your convenience.

Sailsjs MVC map params from external API to multiple models

I need to create a database of shopify orders so I can run advanced queries and sales reports that you can't do in the shopify admin area. I'm building in Sails .12 and mysql. Shopify lets you register a webhook so that every time an order is placed, it creates a POST to the specified URL with the order data in the body as JSON. The products ordered are an array of JSON objects as one of the values in the POST:
{
"id": 123456,
"email": "jon#doe.ca",
"created_at": "2017-01-10T14:26:25-05:00",
...//many more entires
"line_items": [
{
"id": 24361829895,
"variant_id": 12345,
"title": "T-Shirt",
"quantity": 1,
"price": "140.00",
},
{
"id": 44361829895,
"variant_id": 42345,
"title": "Hat",
"quantity": 1,
"price": "40.00",
},
]
}
I need to save the order into an Orders table, and the products ordered into a line_items table that is a one to many relation; one order can have many line_items (products ordered). There are over 100 key-value pairs sent by the webhook, and I'm saving all of it. I've created my two models where I define the data type, so now i have very long Order.js and Line_item.js files, and I'm using the
line_items: {
collection: 'line_item',
via: 'order_id'
},
in my Order.js, and
order_id: {
model: 'order'
},
in my Line_item.js models to relate them. Is this the correct way to denfine my two tables? Also, where would I put the code that maps the JSON to the model parameters? If I put that code in the controllers, would I have to type another 100+ lines of code to map each json value to its correct parameter. The how would I save to the two different models/tables? Eg:
var newOrder = {};
newOrder.id =req.param('id');
newOrder.email = req.param('email');
newOrder.name = req.param('name');
...//over 100 lines more, then Order.create(newOrder, ...)
var newLine_items = req.params('line_items'); //an array
_.forEach(newLine_items, function(line_item){
var newLine_item = {};
newLine_item.id = line_item.id;
newLine_item.order_id = newOrder.id;
newLine_item.title = line_item.title;
//etc for over 20 more lines, then Line_item.create(newLine_item, ...)
});
I need to save the order into an Orders table, and the products ordered into a line_items table that is a one to many relation; one order can have many line_items (products ordered).
That sounds completely reasonable, well, besides the use of the Oxford comma :)
There are over 100 key-value pairs sent by the webhook
I'm not sure that I understand exactly what this is or what it is used for within this process.
That being said, it might help to have a single attribute in your model for this which has a JSON value, then retrieve and work with it as JSON instead of trying to manually account for each attribute if that is what you're doing over there?
It really depends on your use case and how you'll use the data though but I figure if the format changes you might have a problem, not so if it's just being stored and parsed as a JSON object?
Also, where would I put the code that maps the JSON to the model parameters
In v0.12.x take a look at Services.
In v1, Services will still work but moving this logic into Helpers might be a good option but then, it seems that a custom model method would be a better one.
Here is a shorter version of your code:
var newOrder = req.allParams();
newLine_items = {};
_.forEach(newOrder.line_items, function(line_item) {
newLine_items.push(line_item);
});
Here is what your logic might look like:
var newOrder = req.allParams();
// Store the order
Order
.create(newOrders)
.exec(function (err, result) {
if (err) // handle the error
var newLine_items = {};
_.forEach(newOrder.line_items, function(line_item) {
// Add the order id for association
line_item.order_id = result.id;
// Add the new line item with the orders id
newLine_items.push(line_item);
});
// Store the orders line items
LineItems
.create(newLine_items)
.exec(function (err, result) {
if (err) // handle the error
// Handle success
});
});
And the lifecycle callback in the Order model:
beforeCreate: function (values, cb) {
delete(values.line_items);
cb();
}
But you really should look into bluebird promises as the model methods in version one of sails have opt in support for them and it helps to negate the pyramid of doom that is starting in my example and is also something that you want to avoid :P

WEB API return all objects instead of one

I want to expose a list of services from my DB or just return one service detail via Web API with my EF DBmodel . I used VS2012 Web API scaffolding, quite easy so far and it works and return the list of services in JSON when I hit the URL(.../api/Services). The problem is that when I want to obtain just one service URL(.../api/Services/1), I still obtain the full list of all services although when I trace it seems to return only a count of 1 object.
What happening here?
Here are the 2 controller actions.
ps: I also tried using a .Where() instead of .Find() but the result is the same in both cases.
// GET api/Services
public IEnumerable<service> Getservices()
{
var services = db.services.Include(s => s.Category).Include(s => s.Country).Include(s => s.StateProvince).Include(s => s.Territory);
return services.AsEnumerable();
}
// GET api/Services/5
public service Getservice(int id)
{
service service = db.services.Find(id);
if (service == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return service;
}
Try handling it as: var service = db.services.Single(s => s.Id == id)
First, check if your database has a single item for your query or not.
If you are querying by primary key then
db.services.SingleOrDefault(s => s.Id == id) should do.
You will need to handle the exception if you are querying on some field which may give back more than one result.
The variant of filtering (Single, SingleOrDefault, First, FirstOrDefault) that you use will depend upon the exact semantics of the code.

Play Scala Anorm One to Many relationship

Coming from play anorm, create a model from json without passing anorm PK value in the json I'm trying to add Seq[Address] to case class like
case class User(
id: Pk[Long] = NotAssigned,
name: String = "",
email: String = "",
addresses: Seq[Address])
Address is a simple object/class with three Strings. An user can have more than 1 address, how do I get all the addresses along with the User in findAll.
def findAll(): Seq[User] = {
Logger.info("select * from pt_users")
DB.withConnection { implicit connection =>
SQL(
"""
select * from pt_users where name like {query} limit {size} offset {offset}
""").as(User.list *)
}
}
A side note about something I have found useful: if you're not sure you will always want to fetch the addresses for a user, you can avoid adding that relation as a field and instead use tuples or other data structures for representing them. This would allow you to have a method like this:
def allUsersWithAddresses(): Map[User, Seq[Address])] = ...
But still have methods that return only users without having the joined data.
To read a join (or subselect) you will have to parse the combined output with a parser, something like this:
.as(User ~ Address *).groupBy(_._1)
If you really want to put the addresses inside of user, you'd have to make the address list empty from the user parser and then map each distinct user into one with the addresses:
.as(User ~ Address *).groupBy(_._1).map {
case (user, addresses) => user.copy(addresses = addresses)
}
Note, the examples are just pointers to an approximate solution, not copy-paste-and-compile ready code.
Hope it helped!
This will work
/** Parses a `Blog` paired with an optional `Post` that can be later be collapsed into one object. */
val parser: RowParser[(Blog, Option[Post])] = {
simple ~ Post.parser.? map {
case blog~post => (blog, post)
}
}
def list: List[Blog] = {
DB.withConnection { implicit c =>
SQL("""
SELECT * FROM blogs b
LEFT OUTER JOIN posts p ON(p.blog_id = b.id)
""").as(parser *)
.groupBy(_._1)
.mapValues(_.map(_._2).flatten)
.toList
.map{ case (blog, posts) => blog.copy(posts = posts) }
}
}
Copied from https://index.scala-lang.org/mhzajac/anorm-relational/anorm-relational/0.3.0?target=_2.12