‘Friend Requests’ use-case with User-Defined Functions of Hypi

User-Defined Functions are useful to build custom behavior outside of what Hypi’ APIs offer…

In this post, we are going to see how we can build a use case of friend requests on social media platforms with User-Defined Functions of Hypi. We are going to use Groovy to build these user-defined functions.

We all know how friend requests on social media platforms work. Person A sends a friend request to person B. Then person B may accept the friend request and an automated post gets published that person A and person B are now friends! Person B may reject the friend request as well if he doesn’t know person A.

Let’s look at the schema of this particular use case. We have three data types: Profile, FriendRequest, and Post.

type Profile {
  friends: [FriendRequest!]
  pendingFriendRequests: [FriendRequest!]
  me: Account!
}

type FriendRequest {
  from: Account
  to: Account
  dateOfFriendship: DateTime
  mutualFriends: [Account!]
  accepted: Boolean
}

type Post {
  postedby: Account 
  text: String
}

We have also defined User-defined mutations to update these data types and create objects from them. We use gql functions inside the User Defined Function to execute mutations and queries.

Let’s look at the mutations one by one!

Suppose person A with account ID a has sent a friend request to person B with account ID b. Then createFriendRequest user defined function will get called. The mutation happens and friend Request ‘from’ account a and friend requests ‘to’ account b will get updated. Since A has not accepted the friend request yet accepted field will be false.

createFriendRequest(a: ID, b: ID):Json @tan(type:Groovy, inline: """
    return gql(\"""
    mutation {
        upsert(
            values: { 
                FriendRequest: [
                    { 
                        from: {
                            hypi: {
                                id:"$a"
                            }
                        },
                        to: {
                            hypi: {
                                id:"$b"
                            }
                        },
                        accepted: false            
                    }
                ] 
            }
        ) {
            id
        }
    }\"""
    )    
  """)

Here friend request object will get created with a particular Hypi ID.

mutation{
  createFriendRequest(a:"01F8Q5VX1TPWEP568A582PAZ6H",b:"01F8Q5VD58HHJ1QSB1VQQ1N64S")
}

{
  "data": {
    "createFriendRequest": [
      {
        "data": {
          "upsert": [
            {
              "id": "01F8SPRX3ADYKM89BN15X1M44K"
            }
          ]
        }
      }
    ]
  }
}

Now we will update profiles of person A and person B with pendingFriendRequests object. We will call user-defined function updateProfile to implement this functionality.

updateProfile(a: ID, b:ID, c:ID):Json @tan(type:Groovy, inline: """
    def getProfileID(String accId) {
    def matches = gql(\""" {
        find(type: Profile, arcql: "me.hypi.id = '$accId'") {
          edges {
            node {
              ... on Profile {
                hypi {
                  id
                }
              }
            }
          }
       }
    }
    \""").data.find.edges
    return matches.isEmpty()? newId() : matches[0].node.hypi.id
    }
    return gql(\"""
    mutation {
        upsert(
            values: { 
                Profile: [
                    { 
                        hypi: {
                            id: "${getProfileID(a.toString())}"
                        }
                        me: {
                            hypi: {
                                id:"$a"
                            }
                        }
                        pendingFriendRequests: {
                            hypi: {
                                id: "$c"
                            }                          
                        }            
                    },
                     { 
                        hypi: {
                            id: "${getProfileID(b.toString())}"
                        }
                        me: {
                            hypi:{
                                id:"$b"
                            }
                        }
                        pendingFriendRequests: {
                        hypi: {
                               id: "$c"
                            }                          
                        }            
                    }
                ] 
            }
        ) {
            id
        }
    }\"""
    )
  """)

Account IDs of two persons, Hypi.id of the FriendRequest object created in the previous step are passed as parameters. getProfileID function retrieves the Hypi IDs of profile A and profile B using the account IDs passed as parameters. If the profile is non-existent then it will create a new Hypi.id for new profile generation.
The updateProfile function updates the profiles of the persons with the pending friend requests using the FriendRequest object passed as a parameter. It also sets the ‘me’ field with the account ids of the persons.

Do check the use of getProfileID how it stores the retrieved ID inside the Hypi ID. It converts the ID into a string and passes the string as a parameter to the function.

mutation{
updateProfile(a:"01F8Q5VX1TPWEP568A582PAZ6H",b:"01F8Q5VD58HHJ1QSB1VQQ1N64S",c:"01F8SPRX3ADYKM89BN15X1M44K")
}  

{
  "data": {
    "updateProfile": {
      "data": {
        "upsert": [
          {
            "id": "01F8SR8WQPWACCHZW0S52RTYFX"
          },
          {
            "id": "01F8SRAHYNSCF18H8YR7AD0D50"
          }
        ]
      }
    }
  }
}

{
  "data": {
    "find": {
      "edges": [
        {
          "node": {
            "hypi": {
              "id": "01F8SR8WQPWACCHZW0S52RTYFX"
            },
            "me": {
              "hypi": {
                "id": "01F8Q5VX1TPWEP568A582PAZ6H"
              }
            },
            "friends": null,
            "pendingFriendRequests": [
              {
                "from": {
                  "username": "test-hypi6@hypi.io"
                },
                "to": {
                  "username": "test-hypi5@hypi.io"
                }
              }
            ]
          },
          "cursor": "01F8SR8WQPWACCHZW0S52RTYFX"
        },
        {
          "node": {
            "hypi": {
              "id": "01F8SRAHYNSCF18H8YR7AD0D50"
            },
            "me": {
              "hypi": {
                "id": "01F8Q5VD58HHJ1QSB1VQQ1N64S"
              }
            },
            "friends": null,
            "pendingFriendRequests": [
              {
                "from": {
                  "username": "test-hypi6@hypi.io"
                },
                "to": {
                  "username": "test-hypi5@hypi.io"
                }
              }
            ]
          },
          "cursor": "01F8SRAHYNSCF18H8YR7AD0D50"
        }
    
      ]
    }
  }
}

Suppose person A accepts the friend request then we will use the acceptFriendRequest function to update the FriendRequest object with the dateOfFriendship being the current date. We have used the Java functions here to format the current date to ISO local.

acceptFriendRequest(a: ID, b: ID, c: ID):Json @tan(type:Groovy, inline: """
    import java.time.ZonedDateTime
    import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE
    return gql(\"""
    mutation {
        upsert(
            values: { 
                FriendRequest: [
                    { 
                        hypi:{
                            id:"$c"
                        },
                        accepted: true,   
                        dateOfFriendship: "${ZonedDateTime.now().format(ISO_LOCAL_DATE)}"                        
                    }
                ],
                Profile: [
                    {
                        hypi: {
                            id:"$a"
                        },
                        friends: {
                            hypi: {
                                id:"$c"
                            }
                        }
                    },
                    {
                        hypi: {
                            id:"$b"
                        },
                        friends: {
                            hypi: {
                                id:"$c"
                            }
                        }
                    }
                ]                
            }
        ) {
            id
        }
    }\"""
    )    
  """)

The accepted parameter becomes true and then profiles of A and B will get updated with the friends fields. A will now be the friend of B & B will be the friend of A. FriendRequest object will get inserted in the friends array of profiles A and B.

mutation{  acceptFriendRequest(a:"01F8SR8WQPWACCHZW0S52RTYFX",b:"01F8SRAHYNSCF18H8YR7AD0D50",c:"01F8SPRX3ADYKM89BN15X1M44K")
}

{
  "data": {
    "acceptFriendRequest": {
      "data": {
        "upsert": [
          {
            "id": "01F8SR8WQPWACCHZW0S52RTYFX"
          },
          {
            "id": "01F8SRAHYNSCF18H8YR7AD0D50"
          },
          {
            "id": "01F8SPRX3ADYKM89BN15X1M44K"
          }
        ]
      }
    }
  }
}

Since person A has responded to the friend request of person B. We do not need pending friend requests in profiles of A and B. So we’re using the user defined function removePendingRequest
function to do that.

removePendingRequest(a: ID, b: ID):Json @tan(type:Groovy, inline: """
    return gql(\"""
        mutation {
            unlink(from:Profile,to:FriendRequest,via:"pendingFriendRequests",whereFromID:"$a",andToID:"$b")
        }\"""
    )    
  """)

In this particular function, we use unlink function to remove the pending friend request objects from the profiles. Parameters of the functions are the account of A and friend request object and then again call the remove pending request function with the account B and the friend request object.

mutation{
  removePendingRequest(a:"01F8SRAHYNSCF18H8YR7AD0D50",b:"01F8SPRX3ADYKM89BN15X1M44K")
}

{
  "data": {
    "removePendingRequest": [
      {
        "data": {
          "unlink": true
        }
      }
    ]
  }
}

{
  "data": {
    "find": {
      "edges": [
        {
          "node": {
            "hypi": {
              "id": "01F8SR8WQPWACCHZW0S52RTYFX"
            },
            "me": {
              "username": "test-hypi6@hypi.io"
            },
            "friends": [
              {
                "hypi": {
                  "id": "01F8SPRX3ADYKM89BN15X1M44K"
                },
                "from": {
                  "username": "test-hypi6@hypi.io"
                },
                "to": {
                  "username": "test-hypi5@hypi.io"
                },
                "dateOfFriendship": "2021-06-22T00:00:00Z",
                "accepted": true
              }
            ],
            "pendingFriendRequests": null
          },
          "cursor": "01F8SR8WQPWACCHZW0S52RTYFX"
        },
        {
          "node": {
            "hypi": {
              "id": "01F8SRAHYNSCF18H8YR7AD0D50"
            },
            "me": {
              "username": "test-hypi5@hypi.io"
            },
            "friends": [
              {
                "hypi": {
                  "id": "01F8SPRX3ADYKM89BN15X1M44K"
                },
                "from": {
                  "username": "test-hypi6@hypi.io"
                },
                "to": {
                  "username": "test-hypi5@hypi.io"
                },
                "dateOfFriendship": "2021-06-22T00:00:00Z",
                "accepted": true
              }
            ],
            "pendingFriendRequests": null
          },
          "cursor": "01F8SRAHYNSCF18H8YR7AD0D50"
        }
      ]
    }
  }
}

Now A and B are friends! So, we will create an automated post to publish. We’re going to call createFriendsPost

createFriendsPost(a: ID, b: ID):Json @tan(type:Groovy, inline: """
    def acntA = gql(\"""{get(type: Account,id:"$a"){...on Account {username} }}\""").data.get
    def acntB = gql(\"""{get(type: Account,id:"$b"){...on Account {username} }}\""").data.get
    return gql(\"""
    mutation CreatePost(\$p: ID, \$q: ID){
        upsert(
            values: { 
                Post: [
                    { 
                        postedby: {
                            hypi:{
                                id: \$p
                            }
                        },
                        text: "$acntA.username is now friends with $acntB.username"            
                    },
                    { 
                        postedby: {
                            hypi:{
                                id: \$q
                            }
                        },
                        text: "$acntB.username is now friends with $acntA.username"            
                    }
                ]
            }
        ) {
            id
        }
    }\""", ['p':a , 'q': b])       
  """)
}

Function createFriendsPost accepts the parameters as account IDs of persons A and B. We use a GQL function to get the usernames of the account. So this particular piece of code gets the object of account A and store it in the variable acntA.

def acntA = gql(\"""{get(type: Account,id:"$a"){...on Account {username} }}\""").data.get

CreatePost mutation updates the postedby field of Post object with the account IDs of an account A and B. It sends the automatic text inserting the usernames from variables acntA and acntB.

mutation{
  createFriendsPost(a:"01F8Q5TD3DY85996JGA3KHK3GH",b:"01F8Q5TY5KH46VEP1ZS5JSVR4Z")
}

{
  "data": {
    "createFriendsPost": {
      "data": {
        "upsert": [
          {
            "id": "01F8ST7ZK373K4CA5GMMZ8F6VX"
          },
          {
            "id": "01F8ST7ZK72FX4WSYX16RH1RMP"
          }
        ]
      }
    }
  }
}

{
  "data": {
    "find": {
      "edges": [
        {
          "node": {
            "hypi": {
              "id": "01F8ST7ZK373K4CA5GMMZ8F6VX"
            },
            "postedby": {
              "username": "test-hypi3@hypi.io"
            },
            "text": "test-hypi3@hypi.io is now friends with test-hypi4@hypi.io"
          },
          "cursor": "01F8ST7ZK373K4CA5GMMZ8F6VX"
        },
        {
          "node": {
            "hypi": {
              "id": "01F8ST7ZK72FX4WSYX16RH1RMP"
            },
            "postedby": {
              "username": "test-hypi4@hypi.io"
            },
            "text": "test-hypi4@hypi.io is now friends with test-hypi3@hypi.io"
          },
          "cursor": "01F8ST7ZK72FX4WSYX16RH1RMP"
        }
      ]
    }
  }
}

Suppose person A doesn’t know person B and he wants to reject the friend request. We will call rejectFriendRequest function now. Pass on the FriendRequest object to this particular function and set the accepted field to false. Then remove the pendingFriendRequests objects from profiles A and B using the removePendingRequest function.

rejectFriendRequest(a: ID):Json @tan(type:Groovy, inline: """
    return [ gql(\"""
    mutation {
        upsert(
            values: { 
                FriendRequest: [
                    { 
                        hypi: {
                            id:"$a"
                        },
                        accepted: false                       
                    }
                ]                           
            }
        ) {
            id
        }
    },
    \"""
    )
  ]
  """)
mutation{
  rejectFriendRequest(a:"01F8SSCSKK1ZM3XY8N4YMYDYN3")
}

{
  "data": {
    "rejectFriendRequest": [
      {
        "data": {
          "upsert": [
            {
              "id": "01F8SSCSKK1ZM3XY8N4YMYDYN3"
            }
          ]
        }
      }
    ]
  }
}

{
  "data": {
    "find": {
      "edges": [
        {
          "node": {
            "hypi": {
              "id": "01F8SSDQD1RN0YE653W0MG085Q"
            },
            "me": {
              "username": "test-hypi3@hypi.io"
            },
            "friends": null,
            "pendingFriendRequests": null
          },
          "cursor": "01F8SSDQD1RN0YE653W0MG085Q"
        },
        {
          "node": {
            "hypi": {
              "id": "01F8SSDQD5F5NG3MYPQVS68TCC"
            },
            "me": {
              "username": "test-hypi4@hypi.io"
            },
            "friends": null,
            "pendingFriendRequests": null
          },
          "cursor": "01F8SSDQD5F5NG3MYPQVS68TCC"
         }
      ]
    }
  }
}

This way, we have built up the use-case of friend requests on social media platforms with the user defined functions of Hypi.

Do leave a comment if you like this post, have a suggestion to make it better, or have any questions!