Link

Joinpoint Expressions

Where the magic happens… Keep open your eyes!


Syntax

It’s important to keep in mind that Beyond provides a mechanism to intercept both functions and methods invocations.

Beyond interprets the provided expressions to decide which functions must be intercepted by the advices. The expressions have the following format.

<package>.<type>?.<function>(<params)<results>

<type>. is only required when the advice needs to intercept a method instead of a function.

A brief cheat sheet

The table contains some examples, that could help us to get a better understanding.

Expression Intercepted
*.*(...)... Any function invocation with 0 or N params and 0 or N results
*.*.*(...)... Any method invocation with 0 or N params and 0 or N results
*.*?.*(...)... Any function or method invocation with 0 or N params and 0 or N results
model.*(...)... Any function invocation, in package model, with 0 or N params and 0 or N results
handlers/employee.*(...)... Any function invocation, in package handlers/employee, with 0 or N params and 0 or N results
model.*.*(...)... Any method invocation for object Person (or any function invocation for non-object), in package model, with 0 or N params and 0 or N results
model.Person?.*(...)... Any method invocation, in package model, with 0 or N params and 0 or N results
model.person.*(...)... Any method invocation, for type person in package model, with 0 or N params and 0 or N results
database.*(string)... Any function in package database, with 1 param of type string and 0 or N results
database.*(string,*int32)... Any function in package database, with 2 params of types string and *int32, and 0 or N results
database.*(string,*)... Any function in package database, with 2 params of types string and the second param of any type, and 0 or N results
database.*(string,...)... Any function in package database, with 1 string param and 1 or more params of any type, and 0 or N results
database.*(string,...)func()string Any function in package database, with 1 string param and 1 or more params of any type, and 1 result whose type is func()string
database.set*(*model.Person) Any function whose name starts with set in package database, with 1 params of type *model.Person, and 0 results

Let’s practice

Let’s check that our environment is ready to follow the tutorial!

  • Install beyond tool & clone the beyond-examples repository
    >> go get github.com/wesovilabs/beyond
    >> git clone https://github.com/wesovilabs/beyond-examples.git
    >> cd joinpoints
    
  • The application provides a Rest API to interact with employee resources. A test purposed advice is registered in file cmd/main.go.

To test the joinpoints expreessions we need to launch the server with command beyond run cmd/main.go and then, run go test/main.go to make some requests to the server.

Server main is found in file cmd/main.go and client main in test/main.go

Intercepting function CreateEmployee

Any of these expressions would be valid to intercept the above function.

  • handler.CreateEmployee(...)...
  • *.CreateEmployee(...)...
  • handler.CreateEmployee(net/http.ResponseWriter,*net/http.Request,github.com/wesovilabs/beyond-examples/joinpoints/storage.Database)
  • handler.CreateEmployee(...,*net/http.Request,...)
  • handler.Create*(...,*net/http.Request,...)

To check the expressions we just need to modify the registered expression in cmd/main.go

func Beyond() *api.Beyond {
  return api.New().
  WithBefore(advice.NewSimpleTracingAdvice, `handler.Create*(...,*net/http.Request,...)`)
}

and then, run the server and the client.

>> beyond run main.go
Launching server on localhost:8000
handler.CreateEmployee
>> go run test/main.go

Intercepting any function in package handler

Any of these expressions would be valid to intercept the above function.

  • handler.*(...)...
  • handler.*(...,*net/http.Request,...)
  • *.*(net/http.ResponseWriter,*net/http.Request,...)

To check the expressions we just need to modify the registered expression in cmd/main.go

func Beyond() *api.Beyond {
  return api.New().
  WithBefore(advice.NewSimpleTracingAdvice, `handler.*(...,*net/http.Request,...)`)
}

and then, run the server and the client.

>> beyond run main.go
Launching server on localhost:8000
handler.CreateEmployee
handler.GetEmployee
handler.ListEmployees
handler.DeleteEmployee
>> go run test/main.go

Why don’t these other expressions print the same output?

  • *.*Employee(...)...
  • handler.*Employee(net/http.ResponseWriter,*net/http.Request,github.com/wesovilabs/beyond-examples/joinpoints/storage.Database)

Intercepting method SaveEmployee of type memDBClient

Any of these expressions would be valid to intercept the above function.

  • storage.*memDBClient.SaveEmployee(*github.com/wesovilabs/beyond-examples/joinpoints/model.Employee)error
  • storage.*memDBClient.Save*(*github.com/wesovilabs/beyond-examples/joinpoints/model.Employee)error
  • storage.*memDBClient.Save*(...)...
  • *.*.SaveEmployee(...)...
  • *.*memDBClient.Save*(...)...

To check the expressions we just need to modify the registered expression in cmd/joinpoints/main.go

func Beyond() *api.Beyond {
  return api.New().
  WithBefore(advice.NewSimpleTracingAdvice, `handler.*(...,*net/http.Request,...)`)
}

and then, run the server and the client.

>> beyond run main.go
Launching server on localhost:8000
storage.*storage.memDBClient.SaveEmployee
>> go run test/main.go

Intercepting function RespondWithJSON in package handler/internal

Any of these expressions would be valid to intercept the above function.

  • handler/internal.RespondWithJSON(net/http.ResponseWriter,int,interface{})
  • */internal.RespondWithJSON(net/http.ResponseWriter,int,interface{})
  • handler/*.RespondWithJSON(net/http.ResponseWriter,int,interface{})
  • *.RespondWithJSON(net/http.ResponseWriter,...)
  • *.RespondWithJSON(...,int,...)

To check the expressions we just need to modify the registered expression in cmd/main.go

func Beyond() *api.Beyond {
  return api.New().
  WithBefore(advice.NewSimpleTracingAdvice, `*.RespondWithJSON(...,int,...)`)
}

and then, run the server and the client.

>> beyond run main.go
Launching server on localhost:8000
internal.RespondWithJSON
internal.RespondWithJSON
internal.RespondWithJSON
internal.RespondWithJSON
>> go run test/main.go

Challenge

Find valid expressions that intercept the following:

  • All the invocations to memDBClient methods
  • Function RandomString in package helper
  • Function RespondWithError. To check it, you will need to force an error. You can do it in file test/main.go by making the below change.
    res,_ := api.CreateEmployee(&model.Employee{
     Email:    "",
     Fullname: "John Doe",
    })
    

If you found any problem to resolve this challenge, don’t hesitate to drop me an email at ivan.corrales.solera@gmail.com and I will be happy to give you some help.


If you enjoyed this article, I would really appreciate if you shared it with your networks