I am trying to write a function that will validate a Google id token.
The oauth2 package requires me to pass in the context when creating a new service, like this:
package services
import (
"context"
"google.golang.org/api/oauth2/v2"
)
func ValidateToken(ctx *context.Context, idToken string) {
// I need to pass context.Context in to the oauth2 library
oauth2Service, err := oauth2.NewService(*ctx)
tokenInfoCall := oauth2Service.Tokeninfo()
tokenInfoCall.IdToken(idToken)
tokenInfo, err := tokenInfoCall.Do()
In Beego this.Ctx is an instance of the Beego context module, so this code won't compile:
func (c *TokenController) Post(ctx *context.Context) {
requestParams := struct {
Google_id_token string
}{}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requestParams)
// Type mismatch
services.ValidateToken(c.Ctx, requestParams.Google_id_token)
How can I reach the context that I need to pass in to the OAuth2 library?
Edit: I'm working around it by passing in context.Background(), but I'm not sure that I fully understand the side effects of this. I'm pretty new to Golang and it feels like background context should only be used at "higher" levels?
func ValidateToken(idToken string) {
ctx := context.Background()
oauth2Service, err := oauth2.NewService(ctx)
try this : c.Ctx.Request.Context()
also don't use pointer in arg ctx in function ValidateToken because context.Context in stdlib is interface
Related
I've looked into various different tools that can be used for mock testing in golang, but I'm trying to accomplish this task using httptest. In particular, I have a function as such:
type contact struct {
username string
number int
}
func getResponse(c contact) string {
url := fmt.Sprintf("https://mywebsite/%s", c.username)
req, err := http.NewRequest(http.MethodGet, url, nil)
// error checking
resp, err := http.DefaultClient.Do(req)
// error checking
return response
}
A lot of the documentation I've read seems to require creating a client interface or a custom transport. Is there no way to mock a response in a test file without changing this main code at all? I want to keep my client, response, and all the related details within the getResponse function. I could have the wrong idea, but I'm trying to find a way to intercept the http.DefaultClient.Do(req) call and return a custom response, is that possible?
https://pkg.go.dev/net/http/httptest#example-Server is a good example for your use case with a small refactoring of your code.
You just have to change the getResponse() by getResponse(url string) to be able to give the server mock url.
I've read seems to require creating a client interface
without changing this main code at all
Keeping your code clean is a good practice and you'll finally get used to it, a testable code is cleaner and a cleaner code is more testable, so don't worry to change your code (using interfaces) so it can accept mock objects.
Your code in its simplest form can be like this:
package main
import (
"fmt"
"net/http"
)
type contact struct {
username string
number int
}
type Client interface {
Do(req *http.Request) (*http.Response, error)
}
func main() {
getResponse(http.DefaultClient, contact{})
}
func getResponse(client Client, c contact) string {
url := fmt.Sprintf("https://mywebsite/%s", c.username)
req, _ := http.NewRequest(http.MethodGet, url, nil)
// error checking
resp, _ := http.DefaultClient.Do(req)
// error checking and response processing
return response
}
And your test can be like this:
package main
import (
"net/http"
"testing"
)
type mockClient struct {
}
// Do function will cause mockClient to implement the Client interface
func (tc mockClient) Do(req *http.Request) (*http.Response, error) {
return &http.Response{}, nil
}
func TestGetResponse(t *testing.T) {
client := new(mockClient)
getResponse(client, contact{})
}
But if you prefer to use httptest:
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
)
type contact struct {
username string
number int
}
func main() {
fmt.Println(getResponse(contact{}))
}
func getResponse(c contact) string {
// Make a test server
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "your response")
}))
defer ts.Close()
// You should still set your base url
base_url := ts.URL
url := fmt.Sprintf("%s/%s", base_url, c.username)
req, _ := http.NewRequest(http.MethodGet, url, nil)
// Use ts.Client() instead of http.DefaultClient in your tests.
resp, _ := ts.Client().Do(req)
// Processing the response
response, _ := io.ReadAll(resp.Body)
resp.Body.Close()
return string(response)
}
So I'm testing the waters with Go. I need to manually make a REST call to an Azure AD protected endpoint. I'm using the Azure Identity package, but still I am not able to get the token.
package main
import (
"context"
"fmt"
azi "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
)
func main() {
cred, err := azi.NewInteractiveBrowserCredential(nil)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println("No error 😎")
var ctx = context.Context()
fmt.Println(cred.GetToken(ctx))
}
This then yields the following error response
# command-line-arguments
.\main.go:19:27: missing argument to conversion to context.Context: context.Context()
Can someone please point me in the right direction of what I am doing wrong?
context.Context is an interface, not a method (https://pkg.go.dev/context#Context) that is why you're getting the error, you're attempting to convert nothing to that type.
Calls to the GetToken method require something that implements context.Context.
Try replacing var ctx = context.Context() with var ctx = context.Background()
Read more about context.Context here https://pkg.go.dev/context
I've recently shifted from python to golang. I had been using python to work with GCP.
I used to pass in the scopes and mention the discovery client I wanted to create like this :
def get_client(scopes, api, version="v1"):
service_account_json = os.environ.get("SERVICE_ACCOUNT_KEY_JSON", None)
if service_account_json is None:
sys.exit("Exiting !!! No SSH_KEY_SERVICE_ACCOUNT env var found.")
credentials = service_account.Credentials.from_service_account_info(
json.loads(b64decode(service_account_json)), scopes=scopes
)
return discovery.build(api, version, credentials=credentials, cache_discovery=False)
And this would create my desired discovery client, whether it be compute engine service or sqladmin
However in go I don't seem to find this.
I found this : https://pkg.go.dev/google.golang.org/api/discovery/v1
For any client that I want to create I would've to import that and then create that, like this :
https://cloud.google.com/resource-manager/reference/rest/v1/projects/list#examples
package main
import (
"fmt"
"log"
"golang.org/x/net/context"
"golang.org/x/oauth2/google"
"google.golang.org/api/cloudresourcemanager/v1"
)
func main() {
ctx := context.Background()
c, err := google.DefaultClient(ctx, cloudresourcemanager.CloudPlatformScope)
if err != nil {
log.Fatal(err)
}
cloudresourcemanagerService, err := cloudresourcemanager.New(c)
if err != nil {
log.Fatal(err)
}
req := cloudresourcemanagerService.Projects.List()
if err := req.Pages(ctx, func(page *cloudresourcemanager.ListProjectsResponse) error {
for _, project := range page.Projects {
// TODO: Change code below to process each `project` resource:
fmt.Printf("%#v\n", project)
}
return nil
}); err != nil {
log.Fatal(err)
}
}
So I've to import each client library to get the client for that.
"google.golang.org/api/cloudresourcemanager/v1"
There's no dynamic creation of it.
Is it even possible, cause go is strict type checking 🤔
Thanks.
No, this is not possible with the Golang Google Cloud library.
You've nailed the point on the strict type checking, as it would definitely defeat the benefits of compile time type checking. It would also be a bad Golang practice to return different objects with different signatures, as we don't do duck typing and instead we rely on interface contracts.
Golang is boring and verbose, and it's like that by design :)
func getPrivateVlan(env string) (string, error) {
// 1. Create a session
sess := session.New(user, pass)
// 2. Get a service
accountService := services.GetAccountService(sess)
// 3. Invoke a method:
vlans, err := accountService.GetNetworkVlans()
vlan := vlans[0]
log.Println(*vlan.Name) //works
log.Println(*vlan.PrimaryRouter) //Doesn't work
}
The object returned is an array of vlans of type SoftLayer_Network_Vlan, https://sldn.softlayer.com/reference/datatypes/softlayer_network_vlan.
I am able to access the properties in column "Local Properties" (ie Name) but am unable to access the properties in column "Relational & Count Properties" (ie PrimaryRouter).
How can I add an object mask to my call in order to see these properties?
The reason is that in order to obtain relational data from an object in the API you must declare an object mask in your API call, as far as I can see, it is not declared in your code, it should be something like this:
func getPrivateVlan(env string) (string, error) {
// 1. Create a session
sess := session.New(user, pass)
// 2. Get a service
accountService := services.GetAccountService(sess)
//declare Mask
object_mask := "id;name;primaryRouter"
// 3. Invoke a method including the Object mask:
vlans, err := accountService.Mask(object_mask).GetNetworkVlans()
vlan := vlans[0]
log.Println(*vlan.Name) //works
log.Println(*vlan.PrimaryRouter) //
}
Try the following code for example:
package main
import (
"fmt"
"github.com/softlayer/softlayer-go/services"
"github.com/softlayer/softlayer-go/session"
"encoding/json"
)
func main() {
// SoftLayer API username and key
username := "set-me"
apikey := "set-me"
// Create a session
sess := session.New(username, apikey)
// Get SoftLayer_Account service
service := services.GetAccountService(sess)
// Object-Mask to get specific Vlan's information
mask := "id;name;primaryRouter"
// Call to getNetworkVlans in order to retrieve vlans according to filter.
result, err := service.Mask(mask).GetNetworkVlans()
if err != nil {
fmt.Printf("\n Unable to retrieve vlans:\n - %s\n", err)
return
}
// Following helps to print the result in json format.
jsonFormat, jsonErr := json.MarshalIndent(result,""," ")
if jsonErr != nil {
fmt.Println(jsonErr)
return
}
fmt.Println(string(jsonFormat))
}
For more information please see below:
https://sldn.softlayer.com/article/object-masks#Property_Set
source: Unable to get itemCategory info from call GetConfiguration when called from golang
F.Ojeda:
The default endpoint is REST but in your environment you are using xmlrpc, which is probably due to the existence of the ~ / .softlayer file and in this it is configured as an XMLRPC enpoint.
For more information you can see the following documentation: https://github.com/softlayer/softlayer-go
This issue happens for the XMLRPC enpoint and you can report it in the go GitHub. https://github.com/softlayer/softlayer-go/issues/
Try in your code with the REST endpoint, like this example:
endpoint := "https://api.softlayer.com/rest/v3"
// Create a session
sess := session.New(username, apikey, endpoint)
I'm using Gin, https://gin-gonic.github.io/gin/, to build a simple RESTful JSON API with Golang.
The routes are setup with something like this:
func testRouteHandler(c *gin.Context) {
// do smth
}
func main() {
router := gin.Default()
router.GET("/test", testRouteHandler)
router.Run(":8080")
}
My question is how can I pass down an argument to the testRouteHandler function? For example a common database connection could be something that one wants to reuse among routes.
Is the best way to have this in a global variable? Or is there some way in Go to pass along an extra variable to the testRouteHandler function? Are there optional arguments for functions in Go?
PS. I'm just getting started in learning Go, so could be something obvious that I'm missing :)
I would avoid stuffing 'application scoped' dependencies (e.g. a DB connection pool) into a request context. Your two 'easiest' options are:
Make it a global. This is OK for smaller projects, and *sql.DB is thread-safe.
Pass it explicitly in a closure so that the return type satisfies gin.HandlerFunc
e.g.
// SomeHandler returns a `func(*gin.Context)` to satisfy Gin's router methods
// db could turn into an 'Env' struct that encapsulates all of your
// app dependencies - e.g. DB, logger, env vars, etc.
func SomeHandler(db *sql.DB) gin.HandlerFunc {
fn := func(c *gin.Context) {
// Your handler code goes in here - e.g.
rows, err := db.Query(...)
c.String(200, results)
}
return gin.HandlerFunc(fn)
}
func main() {
db, err := sql.Open(...)
// handle the error
router := gin.Default()
router.GET("/test", SomeHandler(db))
router.Run(":8080")
}
Using the link i posted on comments, I have created a simple example.
package main
import (
"log"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3"
)
// ApiMiddleware will add the db connection to the context
func ApiMiddleware(db gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("databaseConn", db)
c.Next()
}
}
func main() {
r := gin.New()
// In this example, I'll open the db connection here...
// In your code you would probably do it somewhere else
db, err := gorm.Open("sqlite3", "./example.db")
if err != nil {
log.Fatal(err)
}
r.Use(ApiMiddleware(db))
r.GET("/api", func(c *gin.Context) {
// Don't forget type assertion when getting the connection from context.
dbConn, ok := c.MustGet("databaseConn").(gorm.DB)
if !ok {
// handle error here...
}
// do your thing here...
})
r.Run(":8080")
}
This is just a simple POC. But i believe it's a start.
Hope it helps.
Late to the party, so far here is my proposal. Encapsulate methods into the object with private/public vars in it:
package main
import (
"log"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3"
)
type HandlerA struct {
Db gorm.DB
}
func (this *HandlerA) Get(c *gin.Context) {
log.Info("[%#f]", this.Db)
// do your thing here...
}
func main() {
r := gin.New()
// Init, should be separate, but it's ok for this sample:
db, err := gorm.Open("sqlite3", "./example.db")
if err != nil {
log.Fatal(err)
}
Obj := new(HandlerA)
Obj.Db = db // Or init inside Object
r := gin.New()
Group := r.Group("api/v1/")
{
Group.GET("/storage", Obj.Get)
}
r.Run(":8080")
}
Handler closures are a good option, but that works best when the argument is used in that handler alone.
If you have route groups, or long handler chains, where the same argument is needed in multiple places, you should set values into the Gin context.
You can use function literals, or named functions that return gin.HandlerFunc to do that in a clean way.
Example injecting configs into a router group:
Middleware package:
func Configs(conf APIV1Config) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("configKey", conf) // key could be an unexported struct to ensure uniqueness
}
}
Router:
conf := APIV1Config{/* some api configs */}
// makes conf available to all routes in this group
g := r.Group("/api/v1", middleware.Configs(conf))
{
// ... routes that all need API V1 configs
}
This is also easily unit-testable. Assuming that you test the single handlers, you can set the necessary values into the mock context:
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("configKey", /* mock configs */)
apiV1FooHandler(c)
Now in the case of application-scoped dependencies (db connections, remote clients, ...), I agree that setting these directly into the Gin context is a poor solution.
What you should do then, is to inject providers into the Gin context, using the pattern outlined above:
Middleware package:
// provider could be an interface for easy mocking
func DBProvider(provider database.Provider) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("providerKey", provider)
}
}
Router:
dbProvider := /* init provider with db connection */
r.Use(DBProvider(dbProvider)) // global middleware
// or
g := r.Group("/users", DBProvider(dbProvider)) // users group only
Handler (you can greatly reduce the boilerplate code by putting these context getters in some helper function):
// helper function
func GetDB(c *gin.Context) *sql.DB {
provider := c.MustGet("providerKey").(database.Provider)
return provider.GetConn()
}
func createUserHandler(c *gin.Context) {
db := GetDB(c) // same in all other handlers
// ...
}
I like wildneuro's example but would do a one liner to setup the handler
package main
import (
"log"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3"
)
type HandlerA struct {
Db gorm.DB
}
func (this *HandlerA) Get(c *gin.Context) {
log.Info("[%#f]", this.Db)
// do your thing here...
}
func main() {
r := gin.New()
// Init, should be separate, but it's ok for this sample:
db, err := gorm.Open("sqlite3", "./example.db")
if err != nil {
log.Fatal(err)
}
r := gin.New()
Group := r.Group("api/v1/")
{
Group.GET("/storage", (&HandlerA{Db: db}).Get)
}
r.Run(":8080")
}
Let me try to explain in detail so that you won't get confused.
Depending on the incoming route, you want to call a controller function. Lets say your incoming route is /books and your controller is BooksController
Your BooksController will try to fetch the books from the database and returns a response.
Now, you want this a handler within your BooksController so that you can access database.
I would do something like this. Let's assume that you are using dynamoDB and the aws sdk provides *dynamodb.DynamoDB. Depending on your db, change this variable.
Create a struct as below.
type serviceConnection struct {
db *dynamoDB.DynamoDB
// You can have all services declared here
// which you want to use it in your controller
}
In your main function, get the db connection information. Let's say you already have a function initDatabaseConnection which returns a handler to db, something like below.
db := initDatabaseConnection() -> returns *dynamodb.DynamoDB
Set db to a struct variable.
conn := new(serviceConnection)
conn.db = db
Call the gin request method with a receiver handler as below.
r := gin.Default()
r.GET("/books", conn.BooksController)
As you see, the gin handler is a controller method which has your struct instance as a receiver.
Now, create a controller method with serviceConnection struct receiver.
func (conn *serviceConnection) BooksController(c *gin.Context) {
books := getBooks(conn.db)
}
As you see here, you have access to all the serviceConnection struct variables and you can use them in your controller.
Alright, I have given you a simple example. It should work. You can extend it as per your need
func main() {
router := gin.Default()
router.GET("/test/:id/:name", testRouteHandler)
router.Run(":8080")
}
func testRouteHandler(c *gin.Context) {
id := c.Params.ByName("id")
name := c.Params.ByName("name")
}
Now you will have to call your handler as below
http://localhost:8080/test/1/myname