-
Notifications
You must be signed in to change notification settings - Fork 0
Iter5 #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
shilin-anton
wants to merge
16
commits into
main
Choose a base branch
from
iter5
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Iter5 #5
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
6d6a53e
Add Get and Post handlers with simple storage
shilin-anton 99b61eb
Update responses
shilin-anton dd4b6c8
Fix WriteReader order
shilin-anton 0cddb61
Change project structure and add tests
shilin-anton 7b0b190
Close body in a test
shilin-anton 491b1e7
Move body.close before Result
shilin-anton c146465
Add defer on body closing
shilin-anton e4bd004
Add chi usage, Change tests
shilin-anton 86ff933
Remove unnecessary storage creation
shilin-anton d6a19a0
Add config to handle flags
shilin-anton 8fcb776
FIx run address
shilin-anton e595f4c
Fix host
shilin-anton f4296de
Add prefix on save short link
shilin-anton 06c9355
FIx
shilin-anton 53def49
Add ENV params
shilin-anton 809a583
Fixes after review
shilin-anton File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,3 +21,4 @@ vendor/ | |
| # IDEs directories | ||
| .idea | ||
| .vscode | ||
| cmd/.DS_Store | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,15 @@ | ||
| package main | ||
|
|
||
| func main() {} | ||
| import ( | ||
| "github.com/shilin-anton/urlreducer/internal/app/config" | ||
| "github.com/shilin-anton/urlreducer/internal/app/server" | ||
| "github.com/shilin-anton/urlreducer/internal/app/storage" | ||
| ) | ||
|
|
||
| func main() { | ||
| config.ParseConfig() | ||
|
|
||
| myStorage := storage.New() | ||
| myServer := server.New(myStorage) | ||
| myServer.Start() | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| module github.com/shilin-anton/urlreducer | ||
|
|
||
| go 1.21.3 | ||
|
|
||
| require github.com/go-chi/chi/v5 v5.0.11 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= | ||
| github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package config | ||
|
|
||
| import ( | ||
| "flag" | ||
| "os" | ||
| ) | ||
|
|
||
| var RunAddr string | ||
| var BaseAddr string | ||
|
|
||
| const defaultRunURL = "localhost:8080" | ||
| const defaultBaseURL = "http://localhost:8080" | ||
|
|
||
| func ParseConfig() { | ||
| flag.StringVar(&RunAddr, "a", defaultRunURL, "address and port to run server") | ||
| flag.StringVar(&BaseAddr, "b", defaultBaseURL, "base URL before short link") | ||
| flag.Parse() | ||
|
|
||
| if envRunAddr := os.Getenv("SERVER_ADDRESS"); envRunAddr != "" { | ||
| RunAddr = envRunAddr | ||
| } | ||
| if envBaseAddr := os.Getenv("BASE_URL"); envBaseAddr != "" { | ||
| BaseAddr = envBaseAddr | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package handlers | ||
|
|
||
| import ( | ||
| "crypto/md5" | ||
| "encoding/hex" | ||
| "github.com/go-chi/chi/v5" | ||
| "github.com/shilin-anton/urlreducer/internal/app/config" | ||
| "github.com/shilin-anton/urlreducer/internal/app/storage" | ||
| "io" | ||
| "net/http" | ||
| ) | ||
|
|
||
| type Storage interface { | ||
| Add(short string, url string) | ||
| Get(short string) (string, bool) | ||
| } | ||
|
|
||
| type Server struct { | ||
| data Storage | ||
| handler http.Handler | ||
| } | ||
|
|
||
| func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
| s.handler.ServeHTTP(w, r) | ||
| } | ||
|
|
||
| func New() *Server { | ||
| r := chi.NewRouter() | ||
|
|
||
| s := &Server{ | ||
| data: make(storage.Storage), | ||
| handler: r, | ||
| } | ||
| r.Get("/{short}", s.GetHandler) | ||
| r.Post("/", s.PostHandler) | ||
|
|
||
| return s | ||
| } | ||
|
|
||
| func shortenURL(url string) string { | ||
| // Решил использовать хэширование и первые символы результата, как короткую форму URL | ||
| hash := md5.Sum([]byte(url)) | ||
| hashString := hex.EncodeToString(hash[:]) | ||
| shortURL := hashString[:8] | ||
| return shortURL | ||
| } | ||
|
|
||
| func (s Server) PostHandler(res http.ResponseWriter, req *http.Request) { | ||
| body, err := io.ReadAll(req.Body) | ||
| if err != nil { | ||
| http.Error(res, "Error reading request body", http.StatusInternalServerError) | ||
| return | ||
| } | ||
| defer req.Body.Close() | ||
|
|
||
| url := string(body) | ||
| short := shortenURL(url) | ||
|
|
||
| s.data.Add(short, url) | ||
|
|
||
| res.Header().Set("Content-Type", "text/plain") | ||
| res.WriteHeader(http.StatusCreated) | ||
| res.Write([]byte(config.BaseAddr + "/" + short)) | ||
| } | ||
|
|
||
| func (s Server) GetHandler(res http.ResponseWriter, req *http.Request) { | ||
| short := chi.URLParam(req, "short") | ||
|
|
||
| url, ok := s.data.Get(short) | ||
| if !ok { | ||
| http.NotFound(res, req) | ||
| return | ||
| } | ||
| res.Header().Set("Location", url) | ||
| res.WriteHeader(http.StatusTemporaryRedirect) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| package handlers | ||
|
|
||
| import ( | ||
| "net/http" | ||
| "net/http/httptest" | ||
| "strings" | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestPostHandler(t *testing.T) { | ||
| srv := New() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| method string | ||
| url string | ||
| requestBody string | ||
| wantStatusCode int | ||
| }{ | ||
| { | ||
| name: "Valid POST request", | ||
| method: http.MethodPost, | ||
| url: "/", | ||
| requestBody: "http://example.com", | ||
| wantStatusCode: http.StatusCreated, | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| t.Run(test.name, func(t *testing.T) { | ||
| req := httptest.NewRequest(test.method, test.url, strings.NewReader(test.requestBody)) | ||
| w := httptest.NewRecorder() | ||
|
|
||
| srv.handler.ServeHTTP(w, req) | ||
|
|
||
| resp := w.Result() | ||
| defer resp.Body.Close() | ||
|
|
||
| if resp.StatusCode != test.wantStatusCode { | ||
| t.Errorf("unexpected status code: got %d, want %d", resp.StatusCode, test.wantStatusCode) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestGetHandler(t *testing.T) { | ||
| srv := New() | ||
| srv.data.Add("test_short", "https://smth.ru") | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| method string | ||
| url string | ||
| wantStatusCode int | ||
| wantLocationHeader string | ||
| }{ | ||
| { | ||
| name: "Valid GET request with existing short link", | ||
| method: http.MethodGet, | ||
| url: "/test_short", | ||
| wantStatusCode: http.StatusTemporaryRedirect, | ||
| wantLocationHeader: "https://smth.ru", | ||
| }, | ||
| { | ||
| name: "Invalid GET request with non-existing short link", | ||
| method: http.MethodGet, | ||
| url: "/non_existing_short_link", | ||
| wantStatusCode: http.StatusNotFound, | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| t.Run(test.name, func(t *testing.T) { | ||
| req := httptest.NewRequest(test.method, test.url, nil) | ||
| w := httptest.NewRecorder() | ||
|
|
||
| srv.handler.ServeHTTP(w, req) | ||
|
|
||
| resp := w.Result() | ||
| defer resp.Body.Close() | ||
|
|
||
| if resp.StatusCode != test.wantStatusCode { | ||
| t.Errorf("unexpected status code: got %d, want %d", resp.StatusCode, test.wantStatusCode) | ||
| } | ||
|
|
||
| if test.wantLocationHeader != "" { | ||
| location := resp.Header.Get("Location") | ||
| if location != test.wantLocationHeader { | ||
| t.Errorf("unexpected Location header: got %s, want %s", location, test.wantLocationHeader) | ||
| } | ||
| } | ||
| }) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| package server | ||
|
|
||
| import ( | ||
| "github.com/shilin-anton/urlreducer/internal/app/config" | ||
| "github.com/shilin-anton/urlreducer/internal/app/handlers" | ||
| "log" | ||
| "net/http" | ||
| ) | ||
|
|
||
| type Storage interface { | ||
| Add(short string, url string) | ||
| Get(short string) (string, bool) | ||
| } | ||
|
|
||
| type server struct { | ||
| handler http.Handler | ||
| storage Storage | ||
| } | ||
|
|
||
| func New(storage Storage) *server { | ||
| handler := handlers.New() | ||
| S := &server{ | ||
| handler: handler, | ||
| storage: storage, | ||
| } | ||
| return S | ||
| } | ||
|
|
||
| func (s server) Start() { | ||
| log.Fatal(http.ListenAndServe(config.RunAddr, s.handler)) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package storage | ||
|
|
||
| type Storage map[string]string | ||
|
|
||
| func (s Storage) Add(short string, url string) { | ||
| s[short] = url | ||
| } | ||
|
|
||
| func (s Storage) Get(short string) (string, bool) { | ||
| url, ok := s[short] | ||
| return url, ok | ||
| } | ||
|
|
||
| func New() Storage { | ||
| storage := make(map[string]string) | ||
| return storage | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Мапа при конкурентном доступе к ней выдает панику, учти это на будущее
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
можно ли решить эту проблему, переделав Storage в структуру, в которой будет мапа и мьютекс?
и перед каждым обращением делать лок, с последующим анлоком?