Implementing 3 way relationship in Prisma - prisma

I also asked this question on Prisma forum.
** EDIT **: The forum has since been locked as read-only just 1 day after my question. It's sure getting scary because there is no official announcement of whether they'll stop developing Prisma 1 after implementing promised features or not. Surely they could have said something. And TBH, the unstable Prisma site does add to my shaking confidence in Prisma despite the number of stars in their repo.
I'm new to Prisma. So I have a 3-way relationship between User, Event and Role I would like to define. For each association of a User and an Event, there exists a Role for that association. If I were to design a database table for another ORM, I would have created a event_user table with user_id, event_id and role_id as columns to relate them.
Practically, a row of these 3 columns must be unique. Obviously, it would be good if Prisma can do the safeguarding of these constraints, but the obvious solution I see might not even come to Prisma 1.
My current design consists of the following:
type User {
// ...
eventUsers: [EventUser!]!
}
type Event {
// ...
eventUsers: [EventUser!]!
}
type EventUser {
role: Role!
event: Event!
user: User!
}
This design will make render all xxWhereUniquexx unusable, which is definitely a hassle to maintain relationships. upserts will certainly be unusable for maintaining the relationships.
How would one create a relationship like this in Prisma?
For some context, each user would have a list of global roles as well, so there would already be an association between User and Role. The question concerns the "local roles" for each event.

If each user already have a value for their respective roles, there would be no need for a third table (unless there is more information you'd like to be stored in your modal layer in which case the role type should be in the Role table).
Relationships are set with the #relation directive. You can apply it to either one of two tables in a relation but for clarity I'm apply them to the Event table. The example assumes users can be part of several events.
Tables:
enum ROLE_TYPE {
TYPE_ONE
TYPE_TWO
}
User {
user_id: ID! #id
events: [Event!]!
}
Event {
event_id: ID! #id
users: [User!]! #relation(link: INLINE)
}
Role {
role_id: ID! #id
type: ROLE_TYPE
event: Event! #relation(link: INLINE)
user: User! #relation(link: INLINE)
}
You can read more about relations on the Prisma site

Related

Is there a way to setup a field-level authorisation on FaunaDB + GraphQL?

I'm having troubles finding a way to hide user emails from everyone, except the owner (user has access to only his email). Is there a way to hide a certain document field, for a certain roles?
Here is an example I found that creates a role with dynamic access to the whole User collection:
CreateRole({
name: "tier1_role",
membership: {
resource: Collection("User"),
predicate: Query(
Lambda("userRef",
// User attribute based rule:
// It grants access only if the User has TIER1 role.
// If so, further rules specified in the privileges
// section are applied next.
Equals(Select(["data", "role"], Get(Var("userRef"))), "TIER1")
)
)
},
privileges: [
{
// Note: 'allUsers' Index is used to retrieve the
// documents from the File collection. Therefore,
// read access to the Index is required here as well.
resource: Index("allUsers"),
actions: { read: true }
}
]
})
I tried to change it a bit, but I wasn't able to set up field-level access.
Let's say I'd set up FaunaDB with GraphQL schema below.
enum UserRole {
TIER1
}
type User {
email: String! #unique
username: String! #unique
role: UserRole!
}
type Query {
allUsers: [User!]
}
type Mutation {
addUsers(new_users: [UserInput]): [User]
#resolver(name: "add_users", paginated: false)
}
How do create a FaunaDB role in such a way that all of the users (except the current one) in resulting array from allUsers query, will not have email field?
I could break User collection into two: one is public, the other is accessible to a document owner, but this sounds wrong.
I'm new to the noSQL concept, so maybe I'm looking at this problem from the wrong perspective?
it's a request that came up a few times. You probably want to do this straight in FaunaDB's ABAC role system but although it provides row-level security, hiding a specific field is currently not provided yet. The feedback has been logged though, we will look into it.
The current way to do this is to split out Users from Accounts and fetch Users instead of Accounts. It would be useful to have something like hidden fields though in the future.
If you think of it, in this case, it does make sense to split authentication information from User information. You never know that you might offer another way to authentication in the future. I still recall from the Phoenix Framework book that they do it there was well and considered it a good practice.
You could also make a thin wrapper using Apollo in a serverless function and filter out these fields when you pass through the results. There is a guide that explains how to build such a thin Apollo middleware that just delegates to FaunaDB https://www.gatlin.io/blog/post/social-login-with-faunadb-and-auth0

The Mongo connector currently does not support Cascading Deletes

I'm working with Prisma and I want to support CASCADE delete but although I have done everything mentioned in the docs I'm still not getting it to work
this is the error I'm getting when I try to Prisma deploy
Errors:
Restaurant
✖ The Mongo connector currently does not support Cascading Deletes, but the field `foods` defines cascade behavior. Please remove the onDelete argument.}
Here is the code
type Restaurant {
id: ID! #id
name: String!
foods: [Food!]! #relation(onDelete: CASCADE, name: "FoodToRestaurant", link: INLINE)
}
type Food {
id: ID! #id
name: String!
desc: String
price: Float!
category: Category!
restaurant: Restaurant! #relation(name: "FoodToRestaurant", onDelete: SET_NULL)
}
I expect when the restaurant gets deleted all its foods should also be deleted
I have CASCADE deleted with Prisma PostgreSQL but now I want to use MongoDB for this app
As it's not possible, you should manage it manually.
Means you should delete all of related entities recursively.
For example if it's your db schema:
Question -> Comment -> Rate
If you want to delete a question, you should delete all of comments related to that question, and if you want to delete a comment, you should delete all of rates assigned to that comment. So you need some recursive functions for deleting these entities.
function deleteQuestion(questionId) {
for each comment related to questionID
deleteComment(commentId)
delete(questionId)
}
function deleteComment(commentId) {
for each rate related to commentId
deleteRate(rateId)
delete(commentId)
}
function deleteRate(rateId) {
delete(rateId)
}
currently mongodb prisma not support cascading delete. if you have it in your schema please remove it.

Understanding Mongoose Schema better

I am relatively new to the MongoDb world, coming from a MS Sql / Entity framework environment.
I am excited about Mongo, because of:
MongoDb's ability to dynamically change the shape of the class/table/collection at run time.
Entity framework does not offer me that.
Why is that so important?
Because I would like to create a generic inventory app and have the product class/collection/table be dynamic for clients to add fields pertinent to their business that cannot be used by everyone, eg. Vin Number, ISBN number, etc.
Now I have come to learn about Mongoose and how it offers a schema, which to me detracts from the flexibility of MongoDb described above.
I have read in a few sections that there is such an animal as mixed-schema, but that appears to be dynamic relative to the data type and not the collection of properties for the given class/collection/table.
So this is my question:
If I am looking at developing a generic class/collection /table that affords clients to shape it to include whatever fields/properties they want that pertain to their business, dynamically, should I abandon the whole notion of mongoose?
I found a benefit today as to where a Schema may be warranted:
Allow me to preface though and say I still thoroughly am excited about the whole idea that Mongo allows a collection to be reshaped at run time in circumstances where I may need ti to be. As mentioned above, a perfect example would be an Inventory app where I would want each client to add respective fields that pertain to their business as opposed to other clients, such as a Car dealership needing a VIN Number field, or a Book store needing a ISBN Number field.
Mongo will allow me to create one generic table and let the client shape it according to his own wishes for his own database at run time - SWEET!
But I discovered today where a schema would be appropo:
If in another table that will not be 're-shapeable', say a user table, I can create a Schema for pre-determined fields and make them required, as such:
var dbUserSchema = mongoose.Schema({
title: {type:String, required:'{PATH} is required!'},
FullName: {
FirstName: {type: String, required: '{PATH} is required!'},
LastName: {type: String, required: '{PATH} is required!'}
}
});
By having the respective first-name and last-name required from the schema, the database will not add any records for a user if they are not both included in the insert.
So, I guess one gets the best of both worlds: Tables that can be re-shaped and thru a schema, tables that can be rigid.

How to clear many to many relationship without loading from database in EF?

I have the following entities (pocos using Entity Framework 5):
Company: Id, Name, Applications, etc.
Application: Id, Name, etc.
There's a many-to-many relationship between companies and applications.
Having a company (without the apllications relationship loaded from the database) and a collection of application ids, I would like to clear the applications from the company and add the applications with ids specified in the collection.
I can attach applications using their ids, without loading them from the database, like this:
foreach (int id in selectedApplications)
{
Application application = new Application() { Id = id };
_context.Applications.Attach(application);
company.Applications.Add(application);
}
_context.SaveChanges();
But I need to clear the applications relationship first. Is there any way to clear a many-to-many relationship without loading it from the database first?
This is only way. company.Applications must be loaded when you add or remove items from it, otherwise the change tracker will never be aware of any changes.
Also, the junction table is not an entity in the conceptual model, so there is no way to set primitive foreign key properties as is possible in foreign key associations. You have to access the associations by the object collections.

In Entity Framework, how do I specify a condition on an association?

I have a relational model with an associative table. But in addition to the related keys, this table also has a flag. I would like to define two associations: one where the flag is true and another where it is false. The EF designer can add a condition to an entity, but not to an association.
The associative table looks like this:
UserPrivilege
-------------
UserId int (FK1)
PrivilegeId int (FK2)
IsGranted bit
I would like to create two associations between the User entity and the Privilege entity: PrivilegesGranted and PrivilegesDenied.
You can't do this directly through the designer.
But this is possible in the XML using DefiningQuery and Create and Delete sprocs. See this old post on my blog for more: Associations with Payloads.
The only thing that is a bit interesting is I assume the PK is just UserId and PrivilegeId, which means a user can't be granted and denied for a particular privilege at the same time.
So if you write code like this:
Privilege p = user.Granted.First();
user.Granted.Remove(p);
user.Denied.Add(p);
ctx.SaveChanges();
Update ordering is important. because you are using a DefiningQuery for both associations, the EF doesn't know they are related, and that it needs to do the delete before it can do the update.
So you might end up with PK violations.
A way to address this issue is in the Insert and Delete sprocs for each association, you can essentially make them change the current row for the User and Privilege pair, if it exists update it with the correct IsGranted value, if not create it, i.e. make it an upsert.
Let me know how you go here
Alex