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
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)
}
I am trying over the Bud full-stack framework and I wanted to implement a simple RSS parser to get data at each request.
I have been following pretty closely the tutorial made by Bud's creator, Matt Mueller https://www.youtube.com/watch?v=LoypcRqn-xA&t=601s and then I modified some of the code of the client he uses in the video to get hackernews json. I just want to be able to call for the data at each request and print it to the screen. However, While it compiles, I encounter an error at runtime
conjure: generate "bud/.app/controller/controller.go" > controller: unable to load definition for field Extensions in sto
ries > parser: unable to find declaration for "Extensions" in "ext"
|
I have tried using feed instead of stories as an object name, creating a struct with a pointer to gofeed.Feed but nothing worked. I hypothize this has something to do with the way I use gofeed parser but it seems I can't manage to find the issue. Do you have any suggestion on what I am doing wrong?
This is my client.go file
// Package feedparser is a simple RSS reader client that leverages gofeed
package feedparser
import (
"context"
"fmt"
"github.com/mmcdole/gofeed"
)
const baseURL = `https://ec.europa.eu/info/news/feed_en`
// Feed Client, it uses gofeed parser to open a connection with the rss/atom/json data feed
func New() *Client {
feedParser := gofeed.NewParser()
return &Client{feedParser}
}
// Feed Client
type Client struct {
*gofeed.Parser
}
// FrontPage gets the feeds and prints them as a list
func (c *Client) FrontPage(ctx context.Context) (*gofeed.Feed, error) {
feedData := `<rss version="2.0">
<channel>
<webMaster>example#site.com (Example Name)</webMaster>
</channel>
</rss>`
fd, err := c.RSSTranslator.Translate(feedData)
if err != nil {
return fd, err
}
fmt.Println(fd)
return fd, nil
}
This is my controller.go file
package controller
import (
context "context"
"github.com/AlessioNar/eunews/feedparser"
"github.com/mmcdole/gofeed"
)
type Controller struct {
// Dependencies...
FP *feedparser.Client
}
// Index of stories
// GET /
func (c *Controller) Index(ctx context.Context) (stories *gofeed.Feed, err error) {
return c.FP.FrontPage(ctx)
}
This is the original repository holding the hackernews client
https://github.com/matthewmueller/hackernews
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
I am new in programming and have no idea about using the the token generate client api function in the source code from my client side golang program. Looking for some advice. Thank you so much.
Source code package: https://pkg.go.dev/github.com/gravitational/teleport/api/client#Client.UpsertToken
Function Source Code:
func (c *Client) UpsertToken(ctx context.Context, token types.ProvisionToken) error {
tokenV2, ok := token.(*types.ProvisionTokenV2)
if !ok {
return trace.BadParameter("invalid type %T", token)
}
_, err := c.grpc.UpsertToken(ctx, tokenV2, c.callOpts...)
return trail.FromGRPC(err)
}
My code:
package main
import (
"context"
"crypto/tls"
"fmt"
"log"
"os"
"strings"
"time"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
"google.golang.org/grpc"
)
// Client is a gRPC Client that connects to a Teleport Auth server either
// locally or over ssh through a Teleport web proxy or tunnel proxy.
//
// This client can be used to cover a variety of Teleport use cases,
// such as programmatically handling access requests, integrating
// with external tools, or dynamically configuring Teleport.
type Client struct {
// c contains configuration values for the client.
//c Config
// tlsConfig is the *tls.Config for a successfully connected client.
tlsConfig *tls.Config
// dialer is the ContextDialer for a successfully connected client.
//dialer ContextDialer
// conn is a grpc connection to the auth server.
conn *grpc.ClientConn
// grpc is the gRPC client specification for the auth server.
grpc proto.AuthServiceClient
// closedFlag is set to indicate that the connnection is closed.
// It's a pointer to allow the Client struct to be copied.
closedFlag *int32
// callOpts configure calls made by this client.
callOpts []grpc.CallOption
}
/*
type ProvisionToken interface {
Resource
// SetMetadata sets resource metatada
SetMetadata(meta Metadata)
// GetRoles returns a list of teleport roles
// that will be granted to the user of the token
// in the crendentials
GetRoles() SystemRoles
// SetRoles sets teleport roles
SetRoles(SystemRoles)
// GetAllowRules returns the list of allow rules
GetAllowRules() []*TokenRule
// GetAWSIIDTTL returns the TTL of EC2 IIDs
GetAWSIIDTTL() Duration
// V1 returns V1 version of the resource
V2() *ProvisionTokenSpecV2
// String returns user friendly representation of the resource
String() string
}
type ProvisionTokenSpecV2 struct {
// Roles is a list of roles associated with the token,
// that will be converted to metadata in the SSH and X509
// certificates issued to the user of the token
Roles []SystemRole `protobuf:"bytes,1,rep,name=Roles,proto3,casttype=SystemRole" json:"roles"`
Allow []*TokenRule `protobuf:"bytes,2,rep,name=allow,proto3" json:"allow,omitempty"`
AWSIIDTTL Duration `protobuf:"varint,3,opt,name=AWSIIDTTL,proto3,casttype=Duration" json:"aws_iid_ttl,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
*/
func main() {
ctx := context.Background()
args := os.Args[1:]
nodeType := ""
if len(args) > 0 {
nodeType = args[0]
}
proxyAddress := os.Getenv("TELEPORT_PROXY")
if len(proxyAddress) <= 0 {
proxyAddress = "proxy.teleport.example.local:443"
}
clt, err := client.New(ctx, client.Config{
Addrs: []string{
"proxy.teleport.example.local:443",
"proxy.teleport.example.local:3025",
"proxy.teleport.example.local:3024",
"proxy.teleport.example.local:3080",
},
Credentials: []client.Credentials{
client.LoadProfile("", ""),
},
})
if err != nil {
log.Fatalf("failed to create client: %v", err)
}
defer clt.Close()
ctx, err, token, err2 := clt.UpsertToken(ctx, token)
if err || err2 != nil {
log.Fatalf("failed to get tokens: %v", err)
}
now := time.Now()
t := 0
fmt.Printf("{\"tokens\": [")
for a, b := range token {
if strings.Contains(b.GetRoles(), b.Allow().String(), b.GetAWSIIDTTL(), nodeType) {
if t >= 1 {
fmt.Printf(",")
} else {
panic(err)
}
expiry := "never" //time.Now().Add(time.Hour * 8).Unix()
_ = expiry
if b.Expiry().Unix() > 0 {
exptime := b.Expiry().Format(time.RFC822)
expdur := b.Expiry().Sub(now).Round(time.Second)
expiry = fmt.Sprintf("%s (%s)", exptime, expdur.String())
}
fmt.Printf("\"count\": \"%1d\",", a)
fmt.Printf(b.Roles(), b.GetAllowRules(), b.GetAWSIIDTTL(), b.GetMetadata().Labels)
}
}
}
Output:
Syntax error instead of creating a token
It's seems your code have many mistake. And, It's very obvious you are getting syntax error. I am sure you would have got the line number in the console where actually these syntax error has occurred.
Please understand the syntax of Golang and also how to call the functions and how many parameter should i pass to those functions.
There are few mistakes i would like to point out after reviewing your code.
//It shouldn't be like this
ctx, err, token, err2 := clt.UpsertToken(ctx, token)
//Instead it should be like this
err := clt.UpsertToken(ctx, token)
//The return type of UpsertToken() method is error, you should use only one variable to receive this error.
strings.Contains() function takes two argument but you are passing four.
Refer this document for string.Contains()
You are assigning t := 0 and checking it with if condition inside for loop and never incremented.
Refer this document for fmt.Printf()
Refer this for function
Remove all the syntax error then only your code will run also cross check your logic.
If you want to see the example of syntax error then check here : https://go.dev/play/p/Hhu48UqlPRF
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 :)