I try to generate totp code using this library same like authy.com desktop application. this is my current code :
package main
import (
"time"
"github.com/pquerna/otp/totp"
"bufio"
"fmt"
"os"
)
func promptForPasscode() string {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter Passcode: ")
text, _ := reader.ReadString('\n')
return text
}
func main() {
keySecret := "NK4KFDHUGRGMFKFEWRJ5EEOV6FT2IAKE"
coded, _ := totp.GenerateCode(keySecret, time.Now().UTC())
fmt.Println("code :", coded)
fmt.Println("Validating TOTP...")
// Now Validate that the user's successfully added the passcode.
passcode := promptForPasscode()
valid := totp.Validate(passcode, keySecret)
if valid {
println("Valid passcode!")
os.Exit(0)
} else {
println("Invalid passocde!")
os.Exit(1)
}
}
The code is working, my problem is, the code generated by golang application is not the same like authy desktop application, what is wrong ?
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)
}
My tg bot shows a data about selling car to user. This data is sсraped from a web site. User can set a town from he wants to see cars. I have a problem, that if one user set a town, it changes on all users. I tried to use database to solve this promleb. Now i can insert in db data about user id and town, that he set, but i don't understand how it can help me. I think i need to use a gorutine to detect several connections from users, but i don't know how it use.
package main
import (
"encoding/json"
"fmt"
_ "github.com/go-sql-driver/mysql"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
"github.com/gocolly/colly"
"io/ioutil"
"log"
"os"
"strings"
"time"
)
var (
currentFile string = "currentCars.json"
newFileName string = "newCars.json"
numericKeyboard = tgbotapi.NewReplyKeyboard(
tgbotapi.NewKeyboardButtonRow(
tgbotapi.NewKeyboardButton("Next Car " + "\u27A1"),
),
)
)
func main() {
bot, err := tgbotapi.NewBotAPI("TOKEN")
if err != nil {
log.Panic(err)
}
var url_town string = "https://auto.drom.ru/region50/all/"
go webScraper(&url_town) //func to scrape data from site
bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName)
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
i := 0
updates := bot.GetUpdatesChan(u)
for update := range updates {
//mysql_db.InsertUserInfo(int(update.Message.Chat.ID), url_town)
if update.Message != nil { // If we got a message
switch update.Message.Text {
case "Next car \u27A1":
// Do something to show a card
case "/select_area":
// Do something to change url_town
default:
reply := "I don't know this command"
msg := tgbotapi.NewMessage(update.Message.Chat.ID, reply)
msg.ReplyMarkup = numericKeyboard
bot.Send(msg)
}
}
}
}
So I currently have a function that will take in a string APIKey to check it against my Mongo collection. If nothing is found (not authenticated), it returns false - if a user is found, it returns true. My problem, however, is I'm unsure how to integrate this with a Martini POST route. Here is my code:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/go-martini/martini"
_ "github.com/joho/godotenv/autoload"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
Name string
APIKey string
}
func validateAPIKey(users *mongo.Collection, APIKey string) bool {
var user User
filter := bson.D{{"APIKey", APIKey}}
if err := users.FindOne(context.TODO(), filter).Decode(&user); err != nil {
fmt.Printf("Found 0 results for API Key: %s\n", APIKey)
return false
}
fmt.Printf("Found: %s\n", user.Name)
return true
}
func uploadHandler() {
}
func main() {
mongoURI := os.Getenv("MONGO_URI")
mongoOptions := options.Client().ApplyURI(mongoURI)
client, _ := mongo.Connect(context.TODO(), mongoOptions)
defer client.Disconnect(context.TODO())
if err := client.Ping(context.TODO(), nil); err != nil {
log.Fatal(err, "Unable to access MongoDB server, exiting...")
}
// users := client.Database("sharex_api").Collection("authorized_users") // commented out when testing to ignore unused warnings
m := martini.Classic()
m.Post("/api/v1/upload", uploadHandler)
m.RunOnAddr(":8085")
}
The validateAPIKey function works exactly as intended if tested alone, I am just unsure how I would run this function for a specific endpoint (in this case, /api/v1/upload).
I am wondering what would be the most efficient way to retrieve the latest comment from a github issue using Go.
I actually know how to do this already but I am not satisfied with the performance so I would love to get some suggestions
package main
import (
"context"
"fmt"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
"net/url"
"os"
)
func main() {
owner, repo := "owner", "repo"
token := oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}
ts := oauth2.StaticTokenSource(&token)
ctx := context.Background()
tc := oauth2.NewClient(ctx, ts)
gc := github.NewClient(tc)
gc.BaseURL, _ = url.Parse("https://api.github.com/")
opts := github.IssueListByRepoOptions{}
issues, _, _ := gc.Issues.ListByRepo(ctx, owner, repo, &opts)
// Implement Here: get latest comment for issues[0]
return
}
Thanks in advance :)
You can use Rest API v3 or GraphQL v4. If you plan to loop through a lot of issues, graphQL definitly worth it
Using Rest API v3
Using go-github as you suggested, you can use :
ListComments(ctx context.Context, owner string, repo string, number int, opts *IssueListCommentsOptions)
For example from this test
For example to get the last comment for the last 20 opened issues (from your code).
package main
import (
"context"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
"net/url"
"os"
"log"
)
func main() {
owner, repo := "google", "gson"
token := oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}
ts := oauth2.StaticTokenSource(&token)
ctx := context.Background()
tc := oauth2.NewClient(ctx, ts)
gc := github.NewClient(tc)
gc.BaseURL, _ = url.Parse("https://api.github.com/")
opts := github.IssueListByRepoOptions{}
issues, _, _ := gc.Issues.ListByRepo(ctx, owner, repo, &opts)
for i := 0; i < len(issues); i++ {
opt := &github.IssueListCommentsOptions{}
comments, _, err := gc.Issues.ListComments(ctx, owner, repo, *issues[i].Number, opt)
if err != nil {
log.Println(err)
} else if len(comments) > 0 {
log.Println(*comments[0].Body)
} else {
log.Println("no comment for this issue")
}
}
}
It will perform :
one request to get the last 20 opened issues
one request for each issue to get the last comments
So a total of 21 requests
Using GraphQL v4
You can use githubv4 library to use Github GraphQL v4.
The same as previous example in GraphQL would be :
package main
import (
"context"
"github.com/shurcooL/githubv4"
"golang.org/x/oauth2"
"os"
"encoding/json"
"log"
)
func main() {
owner, repo := "google", "gson"
token := oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}
ts := oauth2.StaticTokenSource(&token)
httpClient := oauth2.NewClient(context.Background(), ts)
client := githubv4.NewClient(httpClient)
{
var q struct {
Repository struct {
Issues struct {
Nodes []struct {
Number int
Comments struct {
Nodes []struct {
Body githubv4.String
}
} `graphql:"comments(last:$commentsLast)"`
}
PageInfo struct {
EndCursor githubv4.String
HasNextPage githubv4.Boolean
}
} `graphql:"issues(last:$issuesLast,states:OPEN)"`
} `graphql:"repository(owner:$repositoryOwner,name:$repositoryName)"`
}
variables := map[string]interface{}{
"repositoryOwner": githubv4.String(owner),
"repositoryName": githubv4.String(repo),
"issuesLast": githubv4.NewInt(20),
"commentsLast": githubv4.NewInt(1),
}
err := client.Query(context.Background(), &q, variables)
if err != nil {
log.Println(err)
return
}
printJSON(q)
}
}
func printJSON(v interface{}) {
w := json.NewEncoder(os.Stdout)
w.SetIndent("", "\t")
err := w.Encode(v)
if err != nil {
panic(err)
}
}
This is modification of the example from the github repo
The code above will perform exactly 1 request
I am trying to send 'hello world' to the telnet server from go client. In the documentation I have found example:
var caller telnet.Caller = telnet.StandardCaller
telnet.DialToAndCall("localhost:5555", caller)
What is the next step to send 'helloworld' now?
Example of programmatic connection using go-telnet
func SetTest() {
conn, _ := telnet.DialTo("localhost:5555")
conn.Write([]byte("hello world"))
conn.Write([]byte("\n"))
}
In the example below you can see that the CallTELNET uses stdin and stdout to allow the user of the program to communicate through telnet. You can send "hello world" by running the program and typing the desired text you wish to send followed by the enter key.
package main
import (
"bufio"
"fmt"
"log"
"os"
"github.com/reiver/go-oi"
"github.com/reiver/go-telnet"
)
type caller struct{}
func (c caller) CallTELNET(ctx telnet.Context, w telnet.Writer, r telnet.Reader) {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
oi.LongWrite(w, scanner.Bytes())
oi.LongWrite(w, []byte("\n"))
}
}
func main() {
fmt.Printf("Dial to %s:%d\n", "localhost", 8080)
err := telnet.DialToAndCall(fmt.Sprintf("%s:%d", "localhost", 8080), caller{})
if err != nil {
log.Fatal(err)
}
}
Examples found here and here
The telnet library implements the 'Writer' type. The Writer Type has a Write method.