Skip to main content

User Defined Functions

User Defined functions provide a lightweight way to implement custom behaviour outside of what Hypi offers. The functions are executed on the same server where the query gets processed.

Currently, you may use two programming language for the user defined functions.

  • Groovy: The entire Groovy syntax is available on the hypi platform. Groovy is a general programming language. Adding custom behaviour using Groovy becomes quick, easy and performant.

  • Velocity: The velocity template language is in place to output text. This text may execute dynamic behaviour. E.g., customizing the body of an email, substituting receiver’s name from a variable etc.

Let’s check how to implement user defined functions.

We will start with a simple schema as follows:

type Query {
inlineGroovyFunction(a: String, b: Int, c: Boolean)
:Json @tan(type:Groovy, inline: """
def map = new java.util.LinkedHashMap()
map.put("a", a)
map.put("b", b)
map.put("c", c)
return map
""")
inlineVelocityFunction(a: String, b: Int, c: Boolean):
String @tan(type:Velocity, inline: "$a,$b,$c")
}

The schema has two user defined functions. inlineGroovyFunction and inlineVelocityFunction You may define the user defined functions either inline or through a script. @tan directive is used to add the functions.

inlineGroovyFunction function puts value of variables a,b and c in a LinkedHashMap and the map is returned in the form of JSON data. inlineVelocityFunction uses velocity template to return the formatted string output of variables a,b, and c. You may execute the user defined function as any other GraphQL query.

{
inlineGroovyFunction(a:"val1", b:23, c: true)
}
OR
{
inlineVelocityFunction(a:"val1", b:23, c: true)
}

You may pass the variable parameters to the function as well. Just like this:

query inlineVelocityFunction($a: String, $b: Int, $c: Boolean){
inlineVelocityFunction(a: $a, b: $b, c: $c)
}

As you can see in inlineGroovyFunction, Java classes are also available to use inline. You may also use a simple Groovy like version as follows.

inlineGroovyFunction(a: String, b: Int, c: Boolean):Json @tan(type:Groovy, inline: """
return [
a: a,
b: b,
c: c
]
""")

User Defined Functions using APIs

It is easy to call CRUD APIs, Math APIs, Authentication functions from User Defined Functions. You may parse JSON and XML data inside User Defined Function as well.

Let's check how to implement these functions.

upsert

Look at the groovyUpsert user defined function below. It can be used to insert or update data in an object. Its behaviour is the same as the upsert function from CRUD APIs. groovyUpsert inserts data in the Author and Book objects.

You may use appropriate name for groovyUpsert as per its behaviour. But inline parameter of @tan directive must use the upsert function to insert or update data.

type Mutation {
groovyUpsert(a: String, b: Int, c: Int, d: String, e:String, f: Boolean):Json
@tan(type:Groovy, inline: """return upsert([
Author: [
[
name: a,
age: b,
count: c,
booklist:[
[
title: d,
publication: e,
available: f
]
]
]
]
]
)
"""
)
}

Note there is a slight change of syntax for the upsert function. Since it is written in Groovy, we have used square brackets instead of curly brackets.

Here is the execution of the function.

mutation{
groovyUpsert(a:"Dan Brown", b: 56, c:3, d: "Da Vinci Code", e: "abc", f: true)
}

To update an existing object, we will do a slight modification in the schema. Let's add a parameter to pass hypi.id to update an existing object.

type Mutation {
groovyUpsert(a: String, b: Int, c: Int, d: String, e:String, f: Boolean, g: ID):Json
@tan(type:Groovy, inline: """return upsert([
Author: [
[
hypi:[
id: g
],
name: a,
age: b,
count: c,
booklist:[
[
title: d,
publication: e,
available: f
]
]
]
]
]
)
"""
)
}

Here is the execution of function.

mutation{
groovyUpsert( d: "Inferno", e: "xyz", f: true, g: "01F9V5QJDFV5MJWSQ0KV13N5PN")
}

find

Below user defined function (groovyFind) can be used to retrieve all the data from an object. Its behaviour is the same as the find function from CRUD APIs.

Use appropriate name for groovyFind as per the requirements. But inline parameter of @tan directive must use the find function to retrieve data.

type Query {
groovyFind(type: String!, arcql: String!, first: Int, after: String, last: Int, before: String, includeTrashed: Boolean, page: Int):Json @tan(type:Groovy, inline: """return find(type, arcql, first, after, last, before, includeTrashed, page)""")
}

Let's retrieve the data inserted in the Author object.

{
groovyFind(type:"Author", arcql:"hypi.id = '01F9V5QJDFV5MJWSQ0KV13N5PN'")
}

get

Below groovyGet user defined function can be used to get data from a single object. Its behaviour is the same as the get function from CRUD APIs. You may choose another name for groovyGet. However, inline parameter of @tan directive must use the get function to get data from a single object.

type Query {
groovyGet( type: HypiMutationType! id: String! ): HypiRootAggregate
@tan(type:Groovy, inline: """return get(type, id)""")
}
{
groovyGet(type:Author, id:"01F9V5QJDFV5MJWSQ0KV13N5PN") {
... on Author {
hypi{
id
created
updated
}
name
booklist{
title
available
}
}
}
}

trash

Below groovyTrash user defined function can be used to put unwanted data in a trash. Its behaviour is the same as the trash function from CRUD APIs. You may choose another name for groovyTrash. But inline parameter of @tan directive must use the trash function to trash data.

type Mutation {
groovyTrash( type: HypiMutationType! arcql: String! ): Int!
@tan(type:Groovy, inline: """return trash(type, arcql)""")
}
mutation {
groovyTrash(type:Author,arcql: "hypi.id='01F9V5QJDFV5MJWSQ0KV13N5PN'")
}

untrash

Below user defined function (groovyUntrash) can be used to withdraw data marked as trash. Its behaviour is the same as the untrash function from CRUD APIs. You may use different name for groovyUntrash. But inline parameter of @tan directive must use the untrash function to untrash data.

type Mutation {
groovyUntrash( type: HypiMutationType! arcql: String! ): Int!
@tan(type:Groovy, inline: """return untrash(type, arcql)""")
}
mutation {
groovyUntrash(type:Author,arcql: "hypi.id='01F9V5QJDFV5MJWSQ0KV13N5PN'")
}

delete

Look at the groovyDelete user defined function below. It can be used to delete the unwanted data permanently Its behaviour is the same as the delete function from CRUD APIs. You may use another name for groovyDelete. Make sure to use delete function in the inline parameter of @tan directive to delete data.

type Mutation {
groovyDelete(type: HypiMutationType! arcql: String! clearArrayReferences: Boolean = false ): Int!
@tan(type:Groovy, inline: """return delete(type, arcql, clearArrayReferences)""")
}

mutation {
groovyDelete(type: Book, arcql: "hypi.id = '01F9V68331S0PVGH1Y8QK39NPC'",
clearArrayReferences:true)
}

deleteScalars

Data in a scalar array can be permanently deleted as shown in the groovyDeleteScalars user defined function below. Behaviour is the same as that of deleteScalars. You may choose appropriate name for groovyDeleteScalars Use deleteScalars function in the Groovy function of the inline parameter of the @tan directive.

type Mutation { 
groovyDeleteScalars( type: HypiMutationType! field: String! values: [String!]! arcql: String! ): Int!
@tan(type:Groovy, inline: """return deleteScalars(type, field, values, arcql)""")
}
mutation{
groovyDeleteScalars(
type:Question,
field:"answer",
values:["a","b"],
arcql:"hypi.id ='01F9VASEMCNFMR7F8N13R6GJXS'"
)
}

You can create a reference, using the link function. Two objects can be conneted using one-to-one or one-to-many references. groovyLink function below works similar to the link function. You may replace groovyLink with another function name. But inline parameter of @tan directive must use the link function to link objects.

type Mutation {
groovyLink( from: HypiMutationType! to: HypiMutationType! via: String! whereFromID: String! andToID: String! fromInstanceId: String toInstanceId: String ): Boolean!
@tan(type:Groovy, inline: """return link(from, to, via, whereFromID, andToID, fromInstanceId, toInstanceId)""")
}
mutation {
groovyLink(from:Author,to:Book, via:"booklist",whereFromID:"01F9V5QJDFV5MJWSQ0KV13N5PN",andToID:"01F9VB3EJMQWF27JW6QKPBEGM2")
}

You can remove a reference, using the unlink function. groovyUnlink user defined function below works similar to the unlink function. You may opt to have another name for groovyUnlink.But inline parameter of @tan directive must use the unlink function to unlink references.

type Mutation {
groovyUnlink(from: HypiMutationType! to: HypiMutationType! via: String! whereFromID: String! andToID: String! fromInstanceId: String toInstanceId: String ): Boolean!
@tan(type:Groovy, inline: """return unlink(from, to, via, whereFromID, andToID, fromInstanceId, toInstanceId)""")
}
mutation {  groovyUnlink(from:Author,to:Book,via:"booklist",whereFromID:"01F9V5QJDFV5MJWSQ0KV13N5PN",andToID:"01F9VB3EJMQWF27JW6QKPBEGM2")
}

createAccount

You may create a new user account using createAccount. The groovyCreateAccount user defined function works similar to the createAccount function. Choose any other name for groovyCreateAccount. But inline parameter of @tan directive must use the createAccount function to create an account.

type Mutation {
groovyCreateAccount( value: AccountInput! ): Hypi
@tan(type:Groovy, inline: """return createAccount(value)""")
}
mutation {
groovyCreateAccount(
value: {
username: "[email protected]"
password: { value: "[email protected]" }
emails: [{ value: "[email protected]" }]
}
) {
id
created
createdBy
}
}

login / loginByEmail

Users may login with login or loginByEmail function. groovyLogin /groovyLoginByEmail user defined functions below work similar to login / loginByEmail functions. You may choose another names for these functions. But inline parameter of @tan directive must use the login/loginByEmail function to login.

type Query {
groovyLoginByEmail(email: String! password: String! ): AccessToken
@tan(type:Groovy, inline: """return loginByEmail(email, password)""")

groovyLogin(username: String! password: String! ): AccessToken
@tan(type:Groovy, inline: """return login(username, password)""")
}
{
groovyLogin(username:"[email protected]",password:"[email protected]"){
sessionToken
sessionExpires
errorCode
errorMsg
}
}
OR
{
groovyLoginByEmail(email:"[email protected]",password:"[email protected]"){
sessionToken
sessionExpires
errorCode
errorMsg
}
}

Maths APIs

Maths APIs can be used to perform Mathematical operations. Behaviour of groovyMath user defined function is similar to Maths APIs.

Here is the schema. Please note the use of square brackets in the syntax. price is the float field on which Mathematical operations are carried out. You may have another name for groovyMath. But use math function in the Groovy function of the inline parameter of @tan directive.

type Mutation {
groovyMath(a: ID, b: Float, c: Float, d: Float, e:Float):Json
@tan(type:Groovy, inline: """return math([
Book: [
[
price: [
hypi: [
id: a
],
div: b,
times: c,
minus: d,
plus: e
]
]
]
]
)
"""
)
}
mutation{
groovyMath(a:"01F9VCE833595FYZR9X6MSKPMH",b:10,c:2,d:2,e:8)
}

parseJSON

You may parse a JSON value inside a user defined function using parseJSON function. groovyParseJSON function below does the same. You may have another name for groovyParseJSON. But use parseJSON function in the Groovy function of the inline parameter of @tan directive.

groovyParseJSON(value: String): Json
@tan(type:Groovy, inline: """return parseJSON(value)""")
{
groovyParseJSON(value:"{\"share\":{\"gpname\": \"scalars\",\"url\": \"[email protected]\"}}")
}

parseXML

You may parse an XML value inside a user defined function using parseXML function. groovyParseXML function below does the same.

You may have another name for groovyParseXML. But use parseXML function in the Groovy function of the inline parameter of @tan directive.

groovyParseXML(value: String): Json
@tan(type:Groovy, inline: """return parseXML(value)""")
{
groovyParseXML(value:"<note><to>abc</to><from>xyz</from><heading>Reminder</heading><body>Low code</body></note>")
}

Function ‘gql’

The function 'gql' is available to execute inside the user defined function. Using this function you may perform mutation or query operations inside the usergraphql defined function. The result depends upon the type of operation. If you perform an upsert mutation, it will return id, createdBy etc. (Just like the normal upsert in Hypi) If you perform a find query, it will return a list. groovyUpsert can be defined in the schema under Mutation type as follows.

Let’s check a few examples of how to use this function. Add the following schema in GraphQL.

type Mutation {
SetBookInfo(a: String, b: Float, c: Int):Json @tan(type:Groovy, inline: """
return [ gql(\"""
mutation {
upsert(
values: {
Book: [
{
title: "$a",
price: $b,
authorid: $c
}
]
}
) {
id
}
}\"""
)
]
""")
}
type Query {
GetAuthorInfo(a: String):Json @tan(type:Groovy, inline: """
return [ gql(\"""
query {
get(type: Author, id: "$a"){
... on Author {
hypi{
id
}
name
booklist{
title
price
authorid
}
}

}
}\"""
)
]
""")
}

SetBookInfo is a user defined function to set parameter values inside the table Book. Upon successful creation of the object, the hypi.id is returned.

mutation {
SetBookInfo(a:"Ikigai", b:6.99, c: 7)
}

Let's retrieve Author table data using GetAthorInfo. Pass the Author id as Author1 .It returns the author infomation with id 'Author1'

query {
GetAuthorInfo(a:"Author1")
}

Thegqlfunction accepts two parameters.

  • query: String - the GraphQL query to execute
  • values: Map - a map containing the set of variables used in query

In the above examples, we have just used GraphQL query string. Let's modify the SetBookInfo function with Map values.

type Mutation {
SetBookInfo(a: String, b: Float, c: Int):Json @tan(type:Groovy, inline: """
return gql('''
mutation CreateBook($p: String, $q: Float, $r: Int){
upsert(
values: {
Book: [
{
title: $p,
price: $q,
authorid: $r
}
]
}
) {
id
}
}''', ['p':a , 'q': b, 'r': c])
""")
}

Notice few changes here:

  • Replaced the \""" with just ''' . Using three ' in Groovy, we have the same effect as """. without string interpolation. It means that $a is not evaluated as a Groovy variable and remains as a part of the string.
  • Added GraphQL variables CreateBook($p: String, $q: Float, $r: Int)
  • Added variable values ['p':a, 'q': b, 'r': c] in the map. Now, the variables $p , $q and $r are GraphQL variables not Groovy variables.

With these changes, the result is the same as the previous example!