Require unique username when Users container has userId as partition key - azure-cosmosdb

I am using this article as an example
https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-model-partition-example with a Users container with userId and username and the partition key as userId.
{
"id": "54c7da13-f4b8-4668-90dc-7c1aa968a73e",
"userId": "54c7da13-f4b8-4668-90dc-7c1aa968a73e",
"type": "user",
"username": "jeffw"
}
In my create user page I want to make sure the username is unique before adding a new user. I tried a pre-trigger but found that "You can't run stored procedures or triggers across multiple logical partitions." How do I make sure that when a user is created that they have selected a unique username? I think I could change the partition key to username but why does the article use userId instead?
SOLUTION
See answer from #mark-brown.
Create a unique key on the Users container and /username:
await database.Database.DefineContainer(name: "Users", partitionKeyPath: "/userId")
.WithUniqueKey().Path("/username").Attach()
.CreateIfNotExistsAsync();
Then try to create a new User with userId as "unique_username" and the new username that is attempting to be created:
{
"id": "06af2937-4677-4d27-a167-5517aa6d0ffd",
"userId": "unique_username",
"type": "unique_username",
"username": "jeffw"
}
await _usersContainer.CreateItemAsync(uniqueUser, new PartitionKey("unique_username"));
This will return a Conflict status if the username already exists. Example is here https://github.com/jwidmer/AzureCosmosDbBlogExample/blob/master/BlogWebApp/Services/BlogCosmosDbService.cs

Changing the partition key to username won't help because you can have multiples of that value in your container. One way you could do this is to have a new partition where you store a unique instance for every user name and use a unique index on the container (unique indexes are unique within a logical partition).
Create a new type = "unique_user" and a userId = "unique_user". Then add a new record of that type with the new username as they register. This should get you millions of users without going over the 20GB limit. Then when creating a new user do an insert on the container with the "unique_user" type and id with the new username. If you get a 201 then do another insert with type= "user" and the rest of the user data.
hope that helps.

You can set up an index policy for unique values.

Related

Hasura object permission based authorization

I am trying to set a "Row Select" permissions on Hasura. I have a (simplified for brevity) Data Model like below
User
id: UserID
App
id: AppID
App Permissions
user_id: User ID
app_id: App ID
permissions: [ ENUM: Admin, View, Owner ]
Feed
app_id: AppID
feed_data: Some Feed Data
Now, I wish to query all Feed for an authenticated user. The query can be of the form
GET all apps, for which the authenticated user has view permissions
query MyQuery {
feed(limit: 10) {
app_id
feed_data
}
}
GET apps with app_id in the query filter for which the authenticated user has view permissions
query MyQuery {
feed(limit: 10, where: {app_id: {_in: [1, 2]}}) {
app_id
feed_data
}
}
Since feed table does not have user_id information directly in it, I can not use X-Hasura-User-Id attribute directly against feed table. I also tried to use _exists relation against the app_permission table, but I am unable to put app_id filter in the permission clause.
{
"_exists": {
"_where": {
"user_id": {
"_eq": "X-Hasura-User-Id"
}
},
"_table": {
"schema": "public",
"name": "app_permission"
}
}
}
I am not really sure how to proceed with such data modelling with Hasura. Any help is appreciated. Thanks.
Since you dont have a direct relationship, I think you can query via appPermissions Table instead of directly querying feeds table.
When you create a feeds table with appId as foreign key relationship, Hasura lets you track this relationship as shown below
This way you can make nested graphQL queries to appPerms table as shown below
query GetUserFeeds {
test_appPerms {
id
userId
feeds(limit: 10) {
app_id
id
feed_data
}
}
}
Another thing I'd like to suggest is that you could try is by using a session variable like x-hasura-app-id along side a x-hasura-role and build your permissions around that.
https://hasura.io/docs/latest/graphql/core/auth/authorization/roles-variables.html

Azure Cosmos Db document partition key having duplicate, but find duplicate document with combination of other columns

I have below document JSON (pasted partial JSON, actual JSON will be complex and embedded). The JSON has Code as ParitionKey, I am trying to build No SQL database documents by migrating my sql tables, and I will have Code, Type making Unique row, as you can see below Code = 4 is duplicated with different Type and id I just generated GUID (not sure on id field so generated GUID and assigned to it).
we only have two values for Type filed, it's either RI or NRI for entire data, and Code is duplicated like below sample data Code:4, but combination of Type & Code fields make it unique.
Example JSON:
{
"id" : "88725628-2a9a-4fc7-90ed-29c5ffbd45fa"
"Code": "4",
"Type": "RI",
"Description": "MAC/CHEESE ",
},
{
"id" : "88725628-9a3b-4fc7-90ed-29c5ffbd34sk"
"Code": "8",
"Type": "RI",
"Description": "Cereals",
},
{
"id" : "88725628-6d9f-4fc7-90ed-29c4ffbd87de"
"Code": "4",
"Type": "NRI",
"Description": "Christmas Deal",
}
In NoSQL cosmos document db, I couldn't use two columns as partition key, so I have only code as Partition key, but when I am trying to insert into Cosmos Db how do I check if not exists then only insert or else I would end up creating duplicate documents:
CreateItemAsync --> I need a way to check if the document already exists if not then create
I have below code to check and if not found create Item
try
{
// Read the item to see if it exists.
ItemResponse<Item> itemResponse = await this.container.ReadItemAsync<Item>(itm.Id, new PartitionKey(itm.Code));
}
catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
// Create an item in the container representing the Andersen family. Note we provide the value of the partition key for this item, which is "Andersen"
ItemResponse<Item> itemResponse = await this.container.CreateItemAsync<Item>(itm, new PartitionKey(itm.Code));
}
But from above code in ReadItemAsync parameters, how do I know id parameter as it is a GUID randomly generated on every insert, is there a better way to utilize id property before insert into Cosmos DB, so it can be utilized while ReadItemAsync ?
second parameter is paritionKey, If I give code as partition key, it wouldn't work as expected as Code can be duplicated with different "Type" values and it's valid, but Code & Type together makes it unique and we shouldn't allow another document to be inserted if code and type are same.
How do I do it in Cosmos db insert ? I have below questions:
id field --> can I generate GUID and save document or id filed has any purpose which can be utilized during reads ?
Is it ok to pick a partition key which can potentially have duplicates like Code field.
How do I check document exists before insert with above qualifiers as Code filed can be duplicated but only With Type it makes it unique ?
Any suggestions ?
If code and type make a unique row then you should use the value of type for id as well rather than generating a GUID because in Cosmos DB the combination of your partition key and id must be unique.
Then when you do an insert, if the data is already there it will throw an exception which you can catch. For reads, if you know the value for code and type, you can use these to perform a point read to get a single row of data, rather than using a query. This is the most efficient way to fetch data in Cosmos DB.
It is fine to have duplicates for partition key values. You only need to make sure that you have less than 20GB of data for each partition key value.

Get user from identityId

I saved one element in dynamodb using the id with AWS.config.credentials.identityId and restricting with ${cognito-identity.amazonaws.com:sub}
This generates a us-east-1:14b37fe3-xxxx-xxxx-xxxx-xxxxxxxxxxxx record in dynamodb for a user with sub in cognito user pool: 20a3902b-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Now, I need to create an trigger in dynamodb and retrieve some user attributes like name and email to generate a billing, how I can retrieve this info?
I have tried using this function:
response = boto3.client('cognito-idp').list_users(
UserPoolId='us-east-1_rYoyNTF4V',
AttributesToGet=[
'email', 'name', 'middle_name'
],
Filter='sub = "{sub}"'.format(sub=sub)
)
But it's only works for the 20a3902b-xxxx-xxxx-xxxx-xxxxxxxxxxxx sub.

Don't store documents with duplicated key in Firestore

I want to avoid duplicated keys in different documents.
For example, in the document 1 I have this:
{
"user": "myuser",
"email: "a#a.com"
}
In the document 2 I have:
{
"user": "myuser2",
"email: "b#b.com"
}
And in document 3 I have:
{
"user": "myuser",
"email: "c#c.com"
}
I don't want to be possible to create document 3, as the user key has the same value. Is possible to do this with database rules?
Thanks.
add the document with user as a unique ID like that
db.collection("Your Collection").document(user).set(Your Object)
"user" is the user field of your object
one solution could be using email address as an unique id for your document, because email addresses are always unique:
db.collection("collection/" + userObject.email).set(userObject)
or
db.collection("collection").doc(email).set(user object)

Angularfire generate new unique ID for non-email object

I'm making a web app using angularfire. I have a url for users at 'url.firebaseio.com/users'. I want to make another url to store chat-room type things at 'url.firebaseio.com/rooms'. When I create a new user using the $createUser() method, it stores the user's information under a unique ID created by $createUser() in the '/users' url. I want to do this with the new '/rooms' url, but I can't find a way to generate unique IDs in the same way without $createUser(). I can't use $createUser() because it requires an email address argument, and I just want to take in a name for the room and a password, all in an object under the unique ID for the room.
I can't think of any code to provide, so here's what a user looks like:
users: {
uniqueUserId: {
email: email#email.com,
name: name
}
}
And here's what I'd like a 'room' to look like:
rooms: {
uniqueRoomId: {
roomName: something
}
}
Is there a built-in way to do this? If not would it be best to generate IDs on my own?

Resources