Apollo: Using same objectID with different depth of query - apollo

Let's say I have 2 pages. User and UserDetail.
User is wrapped by just
user (id: 1){
id
name
email
}
whereas UserDetail is wrapped by more detail query
user (id: 1) {
id
name
email
bankAccount
...
}
I setup dataIdFromObject as typename + id so in this case, both of them use the key of User-1
When I switch from page User to UserDetail, willUser-1 in apollo cache be overwritten to have bankAccount data?
When I switch from UserDetail to User, will bankAccount be removed?
Is it common approach for apollo dataId to be used this way (duplicately) across different pages with different depth of data?

Related

DynamoDB table design for social network

I got a thinking-problem in DynamoDB.
My structure is looking as following:
primary key = "id"
sort key = "sort"
I have posts, users and "user A following user B" relationships.
Users:
id=1234
sort="USER_USER_1234"
name="max" (for example)
-
id=3245
sort="USER_USER_3245"
name="tom"
Post:
id=9874
sort="POST_POST_1234 (because its created by user id 1234)
createdAt=1560371687
Following:
id=1234
sort="USER_FOLLOW_3245"
--> tom follows max (but max not tom)
How could I design a query to get all posts by the people which tom(id=3245) is following? So in my case the post id 9874?
My approach was to put a GSI where sort is the primary key and id is the sort key (that i can query all people which user A is following), than get all the posts from the users (with help of the same GSI) and sort the result after a second index where createdAt is the sort key. The problem is that this needs much much querys (imagine user A would follow 10000 people and they all make posts). Is there a technique or design thinking approach which you could recommend for this situation? My second approach was to index the whole application table to elastic search and do a nested query. Would this make more sense? Or would you recommend using another type of database like AWS neptune?
There's a hands-on lab on aws about a similar problem - "a mobile application that includes a social network": https://aws.amazon.com/getting-started/hands-on/design-a-database-for-a-mobile-app-with-dynamodb/4/
Brief description:
Users will upload photos through your application
users will want to find and follow friends
By following a friend, a user will receive notifications of the friend’s new photos
user will be able to message their friends
friends can view their photos
users can react to a photo with one of four emojis — a heart, a smiley face, a thumbs up, or a pair of sunglasses.
When looking at a photo, users should be able to see the number of each type of reaction a photo has received
The model has the following entities: User, Photo, Reaction, Friendship.
A User can have many Photos, and a Photo can have many Reactions. Finally, the Friendship entity represents a many-to-many relationship between Users, as a User can follow multiple Users and be followed by multiple other Users.
Access patterns
Based on the business requirements, these are the access patterns identified:
User
Create user profile (Write)
Update user profile (Write)
Get user profile (Read)
Photo
Upload photo for user (Write)
View recent photos for user (Read)
React to a photo (Write)
View photo and reactions (Read)
Friendship
Users can follow friends, view updates on their friends’ activities, and receive recommendations on other friends they may want to follow.
A friendship is a one-way relationship, like Twitter. One user can choose to follow another user, and that user may choose to follow the user back. For our application, we will call the users that follow a user “followers”, and we will call the users that a user is following the “followed”.
Based on this information, we have the following access patterns:
Follow user (Write)
View followers for user (Read)
View followed for user (Read)
On the Friendship entity, we have an access pattern that needs to find all users that follow a particular user as well as an access pattern to find all of the users that a given user follows.
Table Design
Because of this, we’ll use a composite primary key with both a PK and SK value. The composite primary key will give us the Query ability on the PK to satisfy one of the query patterns we need:
Entity PK SK
User USER#<USERNAME> #METADATA#<USERNAME>
Photo USER#<USERNAME>. PHOTO#<USERNAME>#<TIMESTAMP>
Reaction REACTION#<USERNAME>#<TYPE> PHOTO#<USERNAME>#<TIMESTAMP>
Friendship USER#<USERNAME> #FRIEND#<FRIEND_USERNAME>
The Friendship entity uses the same PK as the User entity. This will allow you to fetch both the metadata for a user plus all of the user’s followers in a single query:
KeyConditionExpression="PK = :pk AND SK BETWEEN :metadata AND :photos",
ExpressionAttributeValues={
":pk": { "S": "USER#{}".format(username) },
":metadata": { "S": "#METADATA#{}".format(username) },
":photos": { "S": "PHOTO$" },
},
A secondary (inverted) index is useful to query the “other” side of a many-to-many relationship. This is the case for your Friendship entity. With your primary key structure, you can query all followers for a particular user with a query against the table’s primary key. When you add an inverted index, you will be able to find the users that a user is following (the “followed”) by querying the inverted index:
KeyConditionExpression="SK = :sk",
ExpressionAttributeValues={
":sk": { "S": "#FRIEND#{}".format(username) }
},
Extensions
What would be interesting is to tweak the design to support mega-popular users (having millions of followers).
Another interesting access pattern not mentioned here is the user feed - see all the photos that their friends have recently posted. This could be done with another table to contain this stream of data which gets updated whenever a friend posts something (find his followers, update their feeds...).
In Amazon Neptune, this would be something as simple as:
g.V(3245).E('post')
The above query would return an iterator, to all the vertices connected by the Edge label "post", starting from the vertex with ID "3245". You can firther tighten it up by either projecting specific properties (.property('name')) from those vertices or materializing the whole vertex (.valueMap()). This is just Gremlin syntax, and you can easily do the same using SPARQL as well, and Amazon Neptune supports both of them.
A bigger question for you is to evaluate all the types of queries you wish to perform on your data, and see if modeling it in a graph database makes sense. If it does, then you're better off using Neptune as opposed to something custom using a mix of other products. Querying/Traversing highly connected data, navigating through relationships etc are some of the classic usecases to use a graph data model.

Bot Framework User Identification

How does bot framework identify the user so it knows to go and grab correct state data? It identifies user correctly when same channel is used, even on different machines. Does it user IP address or something similar?
It is based on the Id property of the user + the channel Id. This user Id depends on the channel: each channel has a specific format of user Id, hence those 2 fields.
Examples:
Webchat: by default is userid but can be changed: user: { id: 'userid' },
Emulator: user ID is always set to default-user
Facebook Messenger: the user ID is a PSID (Page Scoped ID) of the user
Slack: the user ID is made of the concatenation of several parameters: Slack's Team ID, Slack's channel ID, and Slack's User ID
SMS: it's the phone number
Email: it's the email address
etc

Store UserTokens generated by ASP.Net Core identity for external login provider

I am using Facebook as external login of ASP.Net Core identity.
I would like, even if the user logged in with Facebook, the user to fill his profile on the website.
For that I use the ExternalLoginCallback method, from which I would like to get data from Facebook such as date of birth, location (country), ...
One issue is if the user unchecked some of the permissions, the default call to Facebook fails:
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
return RedirectToAction(nameof(Login));
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
I would also need to do some additional checks on user data, which would require calling directly the Graph API.
My blocking points:
- In the ExternalLoginCallback method, I would need to separate the 'country' and 'birthday' to avoid the Facebook API to return an error in case of the user didn't grant the permission
- For that I would need the the user access_token (and for additional calls in the method), I don't see how to get it even if it is used by the Facebook Identity framework
- Once the profile created, I would like to get access to this access_token, which should be stored in the UserTokens table (I guess?), but I can't find it there, the table is empty. (my DbContext is a class extending IdentityDbContext<AppUser, AppRole, long>, don't know if it has an impact)
I have found this answer https://stackoverflow.com/a/42670559/4881677 which may help, but not sufficient.
Any help? :)
In order to store the user Facebook token, it requires to specify it in the options (not stored by default).
var fo = new FacebookOptions();
fo.SaveTokens = true;
From there we can call the graph method permissions to get the available permissions: https://graph.facebook.com/me/permissions?access_token={token}
Then it can be read with something like this:
foreach (var perm in data)
{
perms.Add((string)perm["permission"], (string)perm["status"]);
}

Accessing Current Recipient in Sitecore 8 Email Experience Manager

How do you get the raw contact id from the ec_contact_id on the URL created by EXM?
We are using Sitecore's EXM to send emails containing links for surveys to recipients. When the recipient takes the survey we want to tie the response back to the recipient. Since EXM puts a unique Id, ec_contact_id, for the contact (encrypted) we want to use that to determine the recipient versus adding our own custom id.
We found this article, https://briancaos.wordpress.com/2015/04/27/sitecore-8-exm-get-the-email-recipient-from-a-sublayout/, and tried implementing it in the Sitecore controller that gets called when the recipient clicks on the link but the resulting recipient name comes back as empty. We don't have a "sc_item_id" value so we tried "_id" and "ec_message_id" in its place but neither value produced a valid contact Id or recipient name. We also tried looking in MongoDB with the decrypted contactId but couldn't find a match.
You could try somthing like this:
//get value of the ec_contact_id parameter for current request
string queryString = WebUtil.GetQueryString( Sitecore.Modules.EmailCampaign.GlobalSettings.AnalyticsContactIdQueryKey);
var shortID = ShortID.TryParse(queryString, out shortID);
System.Guid contactId;
// where initializationVector is System.Guid of your email message item.
using (var cryptoServiceProvider = new GuidCryptoServiceProvider(System.Text.Encoding.UTF8.GetBytes(GlobalSettings.PrivateKey), initializationVector.ToByteArray()))
{
contactId = cryptoServiceProvider.Decrypt(shortID.Guid);
}
When you create a new user in an email list, Sitecore creates a record in the xDB Mongo database. You should be able to get the email address from the users profile.
Tracker.Current.Contact.GetFacet<IContactEmailAddresses>("Emails").Entries[Tracker.Current.Contact.GetFacet<IContactEmailAddresses>("Emails").Preferred]
Tracker.Current.Contact.GetFacet<IContactEmailAddresses>("Emails").Entries["work_email"]

Rest Replay Attacks / Security

let me describe a simple scenario:
There is a rest resource represented by this URI: /api/messages/{userid}. After the user is logged in, a request is dispatched passing to this URI "userid" (logged user). This way, as soon as the user logs in, he gets his own messages based on his ID.
If user is not logged yet, the URI is not visible (there is a authentication filter).
The problem is: if a already logged user discover this uri, he can submit a request passing another ID what will lead to a security problem because he will be able to get messages from another user (simply passing any random ID).
Can you propose any security model to prevent this security flaw ? (what I believe its more likely a cross-cutting concern).
Thanks, in advance!
Just off the top of my head, have the endpoint either (a) only return those messages visible by the logged-in user, or (b) only return messages if the logged-in user is the same as the userId in the URI. Which one depends on your business rules.
In the endpoint you can check for the user and then see if that user id matches the pathparam
finding the userid from the user name is as easy as selecting the id using the user name from a database
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;
import ... blablabla;
#GET
#Path("messages/{userid}")
public Response getMessages( #PathParam("userid") int userId, #Context SecurityContext context ) {
String username = context.getUserPrincipal().getName();
int id = getIdFromName(username); // define this yourself
if(userId==id) {
// output the message list
List<Message> msgs = getDemMessagesGURL(); // define this yourself
return Response.ok(new GenericEntity<List<T>>(msgs) {}).build();
} else {
// output Forbidden(403)
return Response.status(403).build();
}
}
and with oauth you can use a servlet filter to set the user principal based on the oauth signiture