├── .gitignore ├── LICENSE ├── README.md ├── session01-An-inspirational-introduction-to-the-Go-Programming-language └── Session01 - An inspirational introduction to the Go Programming language.pdf ├── session02-How-to-start-writing-Go-code-structure-modules-introduction └── How to start writing Go code. Code structure and modules introduction_.pdf ├── session03_Lexical_elements_Literals_primitives_variables_constants_program_init_flow └── Lexical elements, literals, primitives, variables, constants, declarations, and their scope_.pdf ├── session04_Expressions_and_Statements └── Expressions and Statements in Go.pdf ├── session05_Error_handling_and_best_practices_panic_and_recovery ├── Error handling and best practices, panic and recovery.pdf └── code │ ├── deferexample_02 │ └── defer_test.go │ ├── errorexample_01 │ ├── errors_check_test.go │ ├── errors_wrapping_test.go │ └── reduce_number_of_checks_test.go │ ├── go.mod │ ├── go.sum │ ├── panicexample_03 │ └── panic_protection_test.go │ └── recoveryexample_04 │ ├── recovery_example_test.go │ └── recovery_gotcha_test.go ├── session06_Type_system_in_Go_overview ├── Type_system_in_Go_overview.pdf └── code │ ├── deferexample_01 │ └── anonymous_fn_with_defer_test.go │ ├── go.mod │ └── typedefinitions_02 │ └── type_definitions_test.go ├── session07_An_in-depth_look_at_Slices_and_Maps ├── An_in-depth_look_at_Slices_and_Maps.pdf └── code │ ├── binary_representation_test.go │ ├── go.mod │ ├── mapsexample │ ├── maps_concurrent_access_03_test.go │ ├── maps_iteration_order_01_test.go │ ├── maps_literals_02_test.go │ └── sets_04_test.go │ └── sliceexample │ ├── multidimentional_slices_01_test.go │ ├── slices_append_03_test.go │ ├── slices_copy_04_test.go │ └── slices_iteration_02_test.go ├── session08_Slice_gotchas_and_Struct_types_in_Go ├── Slice_Gotchas_and_Struct_types.pdf └── code │ ├── go.mod │ ├── slicegotchas │ ├── append_to_inner_resliced_slice_04_test.go │ ├── append_with_new_allocated_array_03_test.go │ ├── capacity_is_not_reached_02_test.go │ ├── pass_slice_to_function_01_test.go │ └── unexpected_memory_usage_05_test.go │ ├── structexample │ ├── struct_embedding_not_subclassing_02_test.go │ ├── struct_initialization_01_test.go │ └── struct_tags_03_test.go │ └── userdefinedtype │ ├── converting_slices_02_test.go │ └── sample_type_definitions_01_test.go ├── session09_Interfaces_and_Functions_in_Go ├── Funcitons_and_Methods_in_Go_Introduction_to_Interfaces.pdf └── code │ ├── funcitonexample │ ├── 01_function_named_return_variables_test.go │ ├── 02_closure_generator_test.go │ └── 03_closure_goroutine_pitfall_test.go │ ├── go.mod │ ├── interfacesexample │ └── 01_interface_general_info_test.go │ └── methodexample │ ├── 01_method_set_mutator_test.go │ └── 02_method_are_just_funcitons_test.go ├── session10_Interfaces_in_Go ├── Session_10_Interfaces_in_Go.pdf └── code │ ├── go.mod │ └── interfaceecample │ ├── 01_system_without_interfaces_test.go │ ├── 02_system_with_interfaces_test.go │ ├── 03_empty_interface_test.go │ ├── 04_type_assert_test.go │ ├── 05_nil_receiver_in_methods_test.go │ ├── 06_wrong_errors_check_test.go │ └── 07_failed_type_assertion_test.go ├── session11_Testing_in_Go_and_Memory_allocations_and_alignment ├── Session11-Memory_allocations_and_alignment_Testing_in_Go-Part_1.pdf └── code │ ├── go.mod │ ├── interfacepollution │ └── 01_interface_pollution_test.go │ ├── memorylayout │ └── 01_stacktrace.go │ └── testspart1 │ ├── greeter.go │ └── greeter_test.go ├── session12_Testing_Part_II ├── Session12_Testing-Part2.pdf └── code │ ├── benchexample │ ├── 01_fib_test.go │ ├── 02_fib_test.go │ ├── 03_fib_test.go │ ├── 04_fib_test.go │ └── fib.go │ ├── go.mod │ ├── go.sum │ └── testexample │ ├── 01_greeter.go │ ├── 01_greeter_test.go │ ├── 02_greeter_test.go │ └── 03_greeter_test.go ├── session13_Packages_in_Go ├── Session13_Benchmarks_and_Packages_in_Go.pdf └── code │ ├── benchexample │ ├── 01_fib_test.go │ ├── 02_fib_test.go │ ├── 03_fib_test.go │ ├── 04_fib_test.go │ └── fib.go │ └── go.mod ├── session14_Modules_in_Go ├── Session14_Modules_in_Go.pdf └── code │ └── graterm-example │ ├── go.mod │ ├── go.sum │ └── main.go ├── session15_Goroutines_and_Channels_in_Go ├── Session15_Goroutines_and_Channels_in_Go.pdf └── code │ ├── go.mod │ └── goroutinesexample │ ├── 01_goroutines_test.go │ ├── 02_bestpractice_test.go │ └── 03_channel_test.go ├── session16_The_most_useful_package_sync ├── Channels-Part2_The_most_Useful_package_in_Go_ sync.pdf └── code │ ├── 01_chanexample │ ├── 01_chan_iteration_test.go │ └── 02_select_statement_test.go │ ├── 02_advancedexample │ ├── 01_generate_data_test.go │ ├── 02_merge_two_channels_test.go │ └── 03_merge_many_channels_test.go │ ├── go.mod │ └── go.sum ├── session17_HTTP_servers_and_routers ├── Package_context_HTTP_server_and_routers_in_Go.pdf └── code │ ├── 01_contextexample │ └── 01_context_test.go │ ├── 02_httpserverexample │ ├── 01_simple │ │ ├── main.go │ │ └── req.http │ └── 02_httpclient │ │ └── 01_httpclient_test.go │ ├── 03_jsonexample │ └── 01_jsonexample_test.go │ ├── 04_httptestexample │ └── 01_httptesting_test.go │ ├── go.mod │ └── go.sum ├── session18_Generics_in_Go ├── Go_generics_concepts.pdf ├── code │ ├── generic-constraint │ │ ├── generic-constraint.go │ │ └── go.mod │ ├── generic-type-assertion │ │ ├── generictypeassertion.go │ │ └── go.mod │ └── stack │ │ ├── go.mod │ │ ├── go.sum │ │ ├── node-stack.go │ │ ├── node-stack_test.go │ │ ├── slice-stack.go │ │ ├── slice-stack_test.go │ │ └── stack-interface.go └── session18_Generics_in_Go.pdf └── session19_Possible_ways_to_design_flexible_APIs_in_Go ├── code └── graterm-example │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── rest.http └── session19_Possible_ways_to_design_flexible_APIs_in_Go.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | *.test 9 | 10 | *.out 11 | .DS_Store 12 | 13 | vendor/ 14 | .idea/ 15 | .vscode/ 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Sergio Kovtunenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exadel Go course 2022 2 | 3 | External registration [form](https://events.exadel.com/event/go-course/) 4 | 5 | # Exadel Video playlist 6 | 7 | - Youtube session 1: ["An inspirational introduction to the Go Programming language"](https://www.youtube.com/watch?v=VM1rYOMoLmY&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=1) 8 | - Youtube session 2: ["How to start writing Go code, structure, modules introduction"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=2) 9 | - Youtube session 3: ["Lexical elements, literals, primitives, variables, constants, etc."](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=3) 10 | - Youtube session 4: ["Expressions and Statements"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=4) 11 | - Youtube session 5: ["Error handling and best practices, panic, and recovery"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=5) 12 | - Youtube session 6: ["Type system in Go: overview"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=6) 13 | - Youtube session 7: ["An in-depth look at Slices and Maps"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=7) 14 | - Youtube session 8: ["Slice Gotchas. Struct types in Go"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=8) 15 | - Youtube session 9: ["Functions and Methods in Go. Introduction to Interfaces - Part 1/2"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=9) 16 | - Youtube session 10: ["Interfaces in Go - Part 2/2"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=10) 17 | - Youtube session 11: ["Memory allocations and alignment. Testing in Go - Part 1/2"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=11) 18 | - Youtube session 12: ["Testing - Part 2/2"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=12) 19 | - Youtube session 13: ["Benchmarks in Go. Packages in Go"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=13) 20 | - Youtube session 14: ["Modules in Go"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=14) 21 | - Youtube session 15: ["Goroutines, channels"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=15) 22 | - Youtube session 16: ["Most useful packages: context, sync"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=16) 23 | - Youtube session 17: ["HTTP servers and routers"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=17) 24 | - Youtube session 18: ["Generics in Go"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=18) 25 | - Youtube session 19: ["Possible ways to design flexible APIs in Go"](https://www.youtube.com/watch?v=i-GlSbRxSY8&list=PLU3Rl8A6vmKcm439krer7nf_EM8eI8fAQ&index=19) 26 | -------------------------------------------------------------------------------- /session01-An-inspirational-introduction-to-the-Go-Programming-language/Session01 - An inspirational introduction to the Go Programming language.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session01-An-inspirational-introduction-to-the-Go-Programming-language/Session01 - An inspirational introduction to the Go Programming language.pdf -------------------------------------------------------------------------------- /session02-How-to-start-writing-Go-code-structure-modules-introduction/How to start writing Go code. Code structure and modules introduction_.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session02-How-to-start-writing-Go-code-structure-modules-introduction/How to start writing Go code. Code structure and modules introduction_.pdf -------------------------------------------------------------------------------- /session03_Lexical_elements_Literals_primitives_variables_constants_program_init_flow/Lexical elements, literals, primitives, variables, constants, declarations, and their scope_.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session03_Lexical_elements_Literals_primitives_variables_constants_program_init_flow/Lexical elements, literals, primitives, variables, constants, declarations, and their scope_.pdf -------------------------------------------------------------------------------- /session04_Expressions_and_Statements/Expressions and Statements in Go.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session04_Expressions_and_Statements/Expressions and Statements in Go.pdf -------------------------------------------------------------------------------- /session05_Error_handling_and_best_practices_panic_and_recovery/Error handling and best practices, panic and recovery.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session05_Error_handling_and_best_practices_panic_and_recovery/Error handling and best practices, panic and recovery.pdf -------------------------------------------------------------------------------- /session05_Error_handling_and_best_practices_panic_and_recovery/code/deferexample_02/defer_test.go: -------------------------------------------------------------------------------- 1 | package deferexample_02 2 | 3 | import "fmt" 4 | 5 | func ExampleDefer() { 6 | for i := 0; i < 4; i++ { 7 | defer fmt.Print(i) 8 | } 9 | 10 | // OUTPUT: 11 | // 3210 12 | } 13 | 14 | func ExampleHeaderPrinting() { 15 | printHeader() 16 | defer printFooter() 17 | 18 | for i := 0; i < 3; i++ { 19 | fmt.Printf("%d...", i+1) 20 | } 21 | fmt.Println() 22 | 23 | // OUTPUT: 24 | // <--- THIS IS HEADER ---> 25 | // 1...2...3... 26 | // <--- THIS IS FOOTER ---> 27 | } 28 | 29 | func printFooter() { 30 | fmt.Println("<--- THIS IS FOOTER --->") 31 | } 32 | 33 | func printHeader() { 34 | fmt.Println("<--- THIS IS HEADER --->") 35 | } 36 | -------------------------------------------------------------------------------- /session05_Error_handling_and_best_practices_panic_and_recovery/code/errorexample_01/errors_check_test.go: -------------------------------------------------------------------------------- 1 | package errorexample_01 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | type MyError struct { 10 | } 11 | 12 | func (m MyError) Error() string { 13 | return "my error" 14 | } 15 | 16 | var ErrBad = &MyError{} 17 | 18 | func incorrectlyReturnsError() error { 19 | var p *MyError = nil 20 | if false { 21 | p = ErrBad 22 | } 23 | return p // Will always return a non-nil error. 24 | } 25 | 26 | func correctlyReturnsError() error { 27 | if true { 28 | return ErrBad 29 | } 30 | return nil 31 | } 32 | 33 | func TestReturnError(t *testing.T) { 34 | gotErr := incorrectlyReturnsError() 35 | require.NoError(t, gotErr) // this will fail 36 | 37 | gotErr = correctlyReturnsError() 38 | require.Error(t, gotErr) 39 | } 40 | -------------------------------------------------------------------------------- /session05_Error_handling_and_best_practices_panic_and_recovery/code/errorexample_01/errors_wrapping_test.go: -------------------------------------------------------------------------------- 1 | package errorexample_01 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/pkg/errors" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func A() error { 11 | return errors.New("cause of the error") 12 | } 13 | 14 | func B() error { 15 | if err := A(); err != nil { 16 | return errors.WithMessage(err, "unable to do B()") 17 | } 18 | return nil 19 | } 20 | 21 | func C() error { 22 | if err := B(); err != nil { 23 | return errors.WithMessage(err, "unable to do C()") 24 | } 25 | return nil 26 | } 27 | 28 | func TestError_wrapping(t *testing.T) { 29 | gotErr := C() 30 | require.Error(t, gotErr) // change to require.NoError() 31 | } 32 | -------------------------------------------------------------------------------- /session05_Error_handling_and_best_practices_panic_and_recovery/code/errorexample_01/reduce_number_of_checks_test.go: -------------------------------------------------------------------------------- 1 | package errorexample_01 2 | 3 | import ( 4 | "bufio" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | // Analyze bufio.Scanner type. Definition: 10 | 11 | // type Scanner struct { 12 | // r io.Reader // The reader provided by the client. 13 | // split SplitFunc // The function to split the tokens. 14 | // maxTokenSize int // Maximum size of a token; modified by tests. 15 | // token []byte // Last token returned by split. 16 | // buf []byte // Buffer used as argument to split. 17 | // start int // First non-processed byte in buf. 18 | // end int // End of data in buf. 19 | // err error // Sticky error. <-------------- take a look at this! 20 | // empties int // Count of successive empty tokens. 21 | // scanCalled bool // Scan has been called; buffer is in use. 22 | // done bool // Scan has finished. 23 | // } 24 | 25 | func TestReduceNumberOfErrorChecks(t *testing.T) { 26 | // Analyze bufio.NewScanner type 27 | 28 | input := strings.NewReader("some\nmultiline\ntest\ninput") 29 | 30 | scanner := bufio.NewScanner(input) 31 | 32 | // perform some operations: 33 | for scanner.Scan() { 34 | token := scanner.Text() 35 | // process token.... 36 | t.Log(token) 37 | } 38 | 39 | // Check for error only once after: 40 | if err := scanner.Err(); err != nil { 41 | // process the error 42 | t.Log("there was an error :(") 43 | } 44 | } 45 | 46 | // NOTE: 47 | 48 | // Sure, there is a nil check for an error, but it appears and executes only once. 49 | // ⁃ With the real API, the client's code therefore feels more natural: loop until done, then worry about errors. 50 | // ⁃ In this case error handling does not obscure the flow of control. 51 | -------------------------------------------------------------------------------- /session05_Error_handling_and_best_practices_panic_and_recovery/code/go.mod: -------------------------------------------------------------------------------- 1 | module session05 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/pkg/errors v0.9.1 7 | github.com/stretchr/testify v1.8.0 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.1 // indirect 12 | github.com/pmezard/go-difflib v1.0.0 // indirect 13 | gopkg.in/yaml.v3 v3.0.1 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /session05_Error_handling_and_best_practices_panic_and_recovery/code/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 5 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 9 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 10 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 11 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 12 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 16 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 17 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | -------------------------------------------------------------------------------- /session05_Error_handling_and_best_practices_panic_and_recovery/code/panicexample_03/panic_protection_test.go: -------------------------------------------------------------------------------- 1 | package panicexample_03 2 | 3 | import ( 4 | "log" 5 | "runtime/debug" 6 | "testing" 7 | ) 8 | 9 | func TestPanicProtection(t *testing.T) { 10 | // Case #1: 11 | // panicingFn() 12 | 13 | // Case #2: 14 | protect(panicingFn) 15 | } 16 | 17 | func panicingFn() { 18 | panic("BOOM!") 19 | } 20 | 21 | func protect(fn func()) { 22 | defer func() { 23 | log.Println("this will be printed normally, even if there is a panic / or no panic") 24 | if x := recover(); x != nil { 25 | // This is how to print stacktrace from the panic: 26 | log.Printf("Runtime panic, recovered from: %v; stack: %s", x, debug.Stack()) 27 | } 28 | }() 29 | log.Println("start executing fn()...") 30 | fn() 31 | log.Println("...fn() executed without panic") 32 | } 33 | -------------------------------------------------------------------------------- /session05_Error_handling_and_best_practices_panic_and_recovery/code/recoveryexample_04/recovery_example_test.go: -------------------------------------------------------------------------------- 1 | package recoveryexample_04 2 | 3 | import "fmt" 4 | 5 | func ExampleRecovery() { 6 | f() 7 | fmt.Println("Returned normally from f.") 8 | 9 | // OUTPUT: 10 | // Calling g. 11 | // Printing in g 0 12 | // Printing in g 1 13 | // Printing in g 2 14 | // Printing in g 3 15 | // Panicking! 16 | // Defer in g 3 17 | // Defer in g 2 18 | // Defer in g 1 19 | // Defer in g 0 20 | // Recovered in f 4 21 | // Returned normally from f. 22 | } 23 | 24 | func f() { 25 | defer func() { 26 | if r := recover(); r != nil { 27 | fmt.Println("Recovered in f", r) 28 | } 29 | }() 30 | fmt.Println("Calling g.") 31 | g(0) 32 | fmt.Println("Returned normally from g.") 33 | } 34 | 35 | func g(i int) { 36 | if i > 3 { 37 | fmt.Println("Panicking!") 38 | panic(fmt.Sprintf("%v", i)) 39 | } 40 | defer fmt.Println("Defer in g", i) 41 | fmt.Println("Printing in g", i) 42 | g(i + 1) 43 | } 44 | -------------------------------------------------------------------------------- /session05_Error_handling_and_best_practices_panic_and_recovery/code/recoveryexample_04/recovery_gotcha_test.go: -------------------------------------------------------------------------------- 1 | package recoveryexample_04 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func doRecover() { 9 | fmt.Println("recovered =>", recover()) // prints: recovered => 10 | } 11 | 12 | func Test_recovery_gotcha(t *testing.T) { 13 | defer func() { 14 | doRecover() // panic is not recovered!!! 15 | 16 | // This is a way to fix the problem (uncomment lines): 17 | 18 | // if err := recover(); err != nil { 19 | // t.Log("Fixed: recovered from", err) 20 | // } 21 | }() 22 | 23 | panic("not good") 24 | } 25 | -------------------------------------------------------------------------------- /session06_Type_system_in_Go_overview/Type_system_in_Go_overview.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session06_Type_system_in_Go_overview/Type_system_in_Go_overview.pdf -------------------------------------------------------------------------------- /session06_Type_system_in_Go_overview/code/deferexample_01/anonymous_fn_with_defer_test.go: -------------------------------------------------------------------------------- 1 | package deferexample_01 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var mx sync.Mutex 9 | 10 | func fnWithoutDefer() { 11 | fmt.Println("Preparation...") 12 | 13 | mx.Lock() 14 | fmt.Println("I'm doing something...") 15 | // .......... 16 | fmt.Println("Still inside the critical section...") 17 | mx.Unlock() 18 | } 19 | 20 | func fnUsingDefer() { 21 | fmt.Println("Preparation...") 22 | 23 | mx.Lock() 24 | defer mx.Unlock() 25 | fmt.Println("Inside the critical section...") 26 | // .......... 27 | fmt.Println("Still inside the critical section...") 28 | } 29 | 30 | func fnUsingAnonymousFnWithDefer() { 31 | fmt.Println("Preparation...") 32 | 33 | func() { 34 | mx.Lock() 35 | defer mx.Unlock() 36 | 37 | fmt.Println("Inside the critical section...") 38 | }() 39 | 40 | // .......... 41 | fmt.Println("Outside of the critical section") 42 | } 43 | -------------------------------------------------------------------------------- /session06_Type_system_in_Go_overview/code/go.mod: -------------------------------------------------------------------------------- 1 | module session06 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /session06_Type_system_in_Go_overview/code/typedefinitions_02/type_definitions_test.go: -------------------------------------------------------------------------------- 1 | package typedefinitions_02 2 | 3 | type ( 4 | Point struct{ x, y float64 } // Point and struct{ x, y float64 } are different types 5 | polar Point // polar and Point denote different types!! 6 | ) 7 | 8 | // A Mutex is a data type with two methods, Lock and Unlock. 9 | type Mutex struct { /* Mutex fields */ 10 | } 11 | 12 | func (m *Mutex) Lock() { /* Lock implementation */ } 13 | func (m *Mutex) Unlock() { /* Unlock implementation */ } 14 | 15 | // NewMutex has the same composition as Mutex but its method set is empty. 16 | type NewMutex Mutex 17 | 18 | // The method set of PtrMutex's underlying type *Mutex remains unchanged, 19 | // but the method set of PtrMutex is empty. 20 | type PtrMutex *Mutex 21 | 22 | // The method set of *PrintableMutex contains the methods 23 | // Lock and Unlock bound to its embedded field Mutex. 24 | type PrintableMutex struct { 25 | Mutex 26 | } 27 | 28 | type Block interface { 29 | BlockSize() int 30 | Encrypt(src, dst []byte) 31 | Decrypt(src, dst []byte) 32 | } 33 | 34 | // MyBlock is an interface type that has the same method set as Block. 35 | type MyBlock Block 36 | -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/An_in-depth_look_at_Slices_and_Maps.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session07_An_in-depth_look_at_Slices_and_Maps/An_in-depth_look_at_Slices_and_Maps.pdf -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/code/binary_representation_test.go: -------------------------------------------------------------------------------- 1 | package code 2 | 3 | import "fmt" 4 | 5 | func Example_BinaryRepresentation() { 6 | var signed int8 = -126 7 | fmt.Printf("Decimal Value: %+d ; Binary form: %b\n", signed, signed) 8 | 9 | // the Most Significant Bit (MSB) == 1 in value 0b11111110 10 | // if the number is positive, this has a value of 0, but if the number is negative, the bit is 1. 11 | var unsigned uint8 = 0b11111110 12 | fmt.Printf("Decimal Value: %+d ; Binary form: %b\n", unsigned, unsigned) 13 | 14 | // output: 15 | // Decimal Value: -126 ; Binary form: -1111110 16 | // Decimal Value: +254 ; Binary form: 11111110 17 | } 18 | -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/code/go.mod: -------------------------------------------------------------------------------- 1 | module session07 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/code/mapsexample/maps_concurrent_access_03_test.go: -------------------------------------------------------------------------------- 1 | package mapsexample 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | ) 7 | 8 | func TestConcurrentAccess(t *testing.T) { 9 | const N = 10 10 | 11 | m := make(map[int]int) 12 | 13 | wg := &sync.WaitGroup{} 14 | 15 | for i := 0; i < N; i++ { 16 | wg.Add(1) 17 | 18 | go func(i int) { 19 | defer wg.Done() 20 | m[i] = i * 10 21 | }(i) 22 | } 23 | 24 | wg.Wait() 25 | t.Log(m) 26 | } 27 | -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/code/mapsexample/maps_iteration_order_01_test.go: -------------------------------------------------------------------------------- 1 | package mapsexample 2 | 3 | import ( 4 | "sort" 5 | "testing" 6 | ) 7 | 8 | func TestRandomIterationOrder(t *testing.T) { 9 | var m = map[int]string{ 10 | 100: "hundred", 11 | 1: "one", 12 | 10: "ten", 13 | } 14 | for k, v := range m { 15 | t.Logf("Key = %d; Value = %q\n", k, v) 16 | } 17 | } 18 | 19 | func TestIterationOrderPreserved(t *testing.T) { 20 | var m = map[int]string{ 21 | 100: "hundred", 22 | 1: "one", 23 | 10: "ten", 24 | } 25 | var keys []int 26 | for k := range m { 27 | keys = append(keys, k) 28 | } 29 | sort.Ints(keys) 30 | for _, k := range keys { 31 | t.Logf("Key = %d; Value = %q\n", k, m[k]) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/code/mapsexample/maps_literals_02_test.go: -------------------------------------------------------------------------------- 1 | package mapsexample 2 | 3 | import "testing" 4 | 5 | func TestLiterals(t *testing.T) { 6 | type Vertex struct { 7 | Lat, Long float64 8 | } 9 | 10 | var m = map[string]Vertex{ 11 | "Bell Labs": Vertex{ 12 | 40.68433, -74.39967, 13 | }, 14 | "Google": Vertex{ 15 | 37.42202, -122.08408, 16 | }, 17 | } 18 | _ = m 19 | } 20 | -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/code/mapsexample/sets_04_test.go: -------------------------------------------------------------------------------- 1 | package mapsexample 2 | 3 | import "fmt" 4 | 5 | func ExampleSet() { 6 | setOfStrings := map[string]struct{}{} 7 | 8 | // Add the same values multiple times: 9 | setOfStrings["one"] = struct{}{} 10 | setOfStrings["one"] = struct{}{} 11 | setOfStrings["one"] = struct{}{} 12 | 13 | for k := range setOfStrings { 14 | fmt.Println("value =", k) 15 | } 16 | 17 | // output: 18 | // value = one 19 | } 20 | -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/code/sliceexample/multidimentional_slices_01_test.go: -------------------------------------------------------------------------------- 1 | package sliceexample 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func ExampleMultidimensionalUsingMake() { 9 | ss := make([][]uint8, 2) // ss variable is []([]uint8) 10 | fmt.Printf("ss: %T %v %d/%d\n", ss, ss, len(ss), cap(ss)) 11 | for i, s := range ss { // s is []uint8 12 | s = make([]uint8, 10, 20) // initialize internal slices! 13 | fmt.Printf("ss[%d]: %T %v %d/%d\n", i, s, s, len(s), cap(s)) 14 | } 15 | // OUTPUT: 16 | // ss: [][]uint8 [[] []] 2/2 17 | // ss[0]: []uint8 [0 0 0 0 0 0 0 0 0 0] 10/20 18 | // ss[1]: []uint8 [0 0 0 0 0 0 0 0 0 0] 10/20 19 | } 20 | 21 | func ExampleMultidimensionalSlices() { 22 | // Create a tic-tac-toe board. 23 | board := [][]string{ 24 | []string{"_", "_", "_"}, 25 | []string{"_", "_", "_"}, 26 | []string{"_", "_", "_"}, 27 | } 28 | 29 | // The players take turns. 30 | board[0][0] = "X" 31 | board[2][2] = "O" 32 | board[2][0] = "X" 33 | board[1][0] = "O" 34 | board[0][2] = "X" 35 | 36 | for i := 0; i < len(board); i++ { 37 | fmt.Printf("%s\n", strings.Join(board[i], " ")) 38 | } 39 | 40 | // Output: 41 | // X _ X 42 | // O _ _ 43 | // X _ O 44 | } 45 | -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/code/sliceexample/slices_append_03_test.go: -------------------------------------------------------------------------------- 1 | package sliceexample 2 | 3 | func ExampleAppendToSlices() { 4 | s0 := []int{0, 0} 5 | s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2} 6 | s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7}} 7 | s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0} 8 | s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0} 9 | _ = s4 10 | 11 | var t []interface{} 12 | t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"} 13 | 14 | var b []byte 15 | b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' } 16 | } 17 | -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/code/sliceexample/slices_copy_04_test.go: -------------------------------------------------------------------------------- 1 | package sliceexample 2 | 3 | func ExampleCopy() { 4 | var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7} 5 | var s = make([]int, 6) 6 | var b = make([]byte, 5) 7 | 8 | n1 := copy(s, a[0:]) // n1 == 6, s == []int{0, 1, 2, 3, 4, 5} 9 | n2 := copy(s, s[2:]) // n2 == 4, s == []int{2, 3, 4, 5, 4, 5} 10 | n3 := copy(b, "Hello, World!") // n3 == 5, b == []byte("Hello") 11 | 12 | _ = n1 13 | _ = n2 14 | _ = n3 15 | } 16 | -------------------------------------------------------------------------------- /session07_An_in-depth_look_at_Slices_and_Maps/code/sliceexample/slices_iteration_02_test.go: -------------------------------------------------------------------------------- 1 | package sliceexample 2 | 3 | import "fmt" 4 | 5 | func ExampleSliceIteration() { 6 | var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} 7 | 8 | for i, v := range pow { 9 | fmt.Printf("2**%d = %d\n", i, v) 10 | } 11 | 12 | // OUTPUT: 13 | // 2**0 = 1 14 | // 2**1 = 2 15 | // 2**2 = 4 16 | // 2**3 = 8 17 | // 2**4 = 16 18 | // 2**5 = 32 19 | // 2**6 = 64 20 | // 2**7 = 128 21 | } 22 | 23 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/Slice_Gotchas_and_Struct_types.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session08_Slice_gotchas_and_Struct_types_in_Go/Slice_Gotchas_and_Struct_types.pdf -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/go.mod: -------------------------------------------------------------------------------- 1 | module session08 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/slicegotchas/append_to_inner_resliced_slice_04_test.go: -------------------------------------------------------------------------------- 1 | package slicegotchas 2 | 3 | import "fmt" 4 | 5 | func ExampleAppenToInnerReslicedSlice() { 6 | letters := []string{"a", "b", "c", "d", "e", "f"} 7 | 8 | slice1 := letters[:3] // Append operations on this 'slice1' is DANGEROUS!!! 9 | slice2 := letters[3:] 10 | 11 | fmt.Printf("so far so good: slice1 %v slice2 %v\n", slice1, slice2) 12 | 13 | slice1 = append(slice1, "BANG") 14 | 15 | fmt.Printf("append to slice1: %v\n", slice1) 16 | fmt.Printf("slice2 is now corrupt: %v\n", slice2) 17 | 18 | fmt.Println("Base array:", letters) 19 | 20 | // OUTPUT: 21 | // so far so good: slice1 [a b c] slice2 [d e f] 22 | // append to slice1: [a b c BANG] 23 | // slice2 is now corrupt: [BANG e f] 24 | // Base array: [a b c BANG e f] 25 | } 26 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/slicegotchas/append_with_new_allocated_array_03_test.go: -------------------------------------------------------------------------------- 1 | package slicegotchas 2 | 3 | import "fmt" 4 | 5 | func ExampleNewArrayAllocated() { 6 | var s []int 7 | for i := 1; i <= 3; i++ { 8 | s = append(s, i) 9 | } 10 | fmt.Println("before:", s, len(s), cap(s)) // before: [1 2 3] 3 4 11 | reverseAndAppend(s) 12 | fmt.Println("after :", s, len(s), cap(s)) // after : [1 2 3] 3 4 13 | 14 | // OUTPUT: 15 | // before: [1 2 3] 3 4 16 | // after : [1 2 3] 3 4 17 | } 18 | func reverseAndAppend(s []int) { 19 | s = append(s, 999, 1000, 1001) // new array will be allocated and then reversed 20 | for i, j := 0, len(s)-1; i < j; i++ { 21 | j = len(s) - (i + 1) 22 | s[i], s[j] = s[j], s[i] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/slicegotchas/capacity_is_not_reached_02_test.go: -------------------------------------------------------------------------------- 1 | package slicegotchas 2 | 3 | import "fmt" 4 | 5 | func ExampleCapacityIsNotReached() { 6 | var s []int 7 | for i := 1; i <= 3; i++ { 8 | s = append(s, i) 9 | } 10 | fmt.Println("before:", s, len(s), cap(s)) // before: [1 2 3] 3 4 11 | reverseSlice(s) 12 | fmt.Println("after :", s, len(s), cap(s)) // after : [999 3 2] 3 4 13 | 14 | // OUTPUT: 15 | // before: [1 2 3] 3 4 16 | // after : [999 3 2] 3 4 17 | } 18 | func reverseSlice(s []int) { 19 | // The new slice has new length attribute which isn't a pointer, but it still points to the same array. 20 | // Because the original slice passed “by value” its length wasn’t altered. 21 | s = append(s, 999) 22 | for i, j := 0, len(s)-1; i < j; i++ { 23 | j = len(s) - (i + 1) 24 | s[i], s[j] = s[j], s[i] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/slicegotchas/pass_slice_to_function_01_test.go: -------------------------------------------------------------------------------- 1 | package slicegotchas 2 | 3 | import "fmt" 4 | 5 | func ExamplePassSliceToFunction() { 6 | var s []int // nil slice 7 | for i := 1; i <= 3; i++ { 8 | s = append(s, i) // append() works fine with nil slices 9 | } 10 | fmt.Println("before:", s, len(s), cap(s)) // before: [1 2 3] 3 4 11 | reverseTheSlice(s) 12 | fmt.Println("after: ", s, len(s), cap(s)) // after: [3 2 1] 3 4 13 | 14 | // OUTPUT: 15 | // before: [1 2 3] 3 4 16 | // after: [3 2 1] 3 4 17 | } 18 | 19 | // function to reverse the content of the slice 20 | func reverseTheSlice(s []int) { 21 | for i, j := 0, len(s)-1; i < j; i++ { 22 | j = len(s) - (i + 1) 23 | s[i], s[j] = s[j], s[i] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/slicegotchas/unexpected_memory_usage_05_test.go: -------------------------------------------------------------------------------- 1 | package slicegotchas 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func brokenGet() []byte { 9 | raw := make([]byte, 10000) // obtain huge slice 10 | fmt.Println(len(raw), cap(raw), &raw[0]) // prints: 10000 10000 11 | return raw[:3] // re-slice to return just a small sub-slice 12 | } 13 | 14 | func TestBrokenGet(t *testing.T) { 15 | data := brokenGet() 16 | t.Log(len(data), cap(data), &data[0]) // prints: 3 10000 17 | } 18 | 19 | func fixedGet() []byte { 20 | raw := make([]byte, 10000) // get huge slice 21 | fmt.Println(len(raw), cap(raw), &raw[0]) // prints: 10000 10000 22 | 23 | res := make([]byte, 3) 24 | copy(res, raw[:3]) // copy just important subslice, `raw` will be garbage collected after 25 | return res 26 | } 27 | 28 | func TestFixedGet(t *testing.T) { 29 | data := fixedGet() 30 | t.Log(len(data), cap(data), &data[0]) // prints: 3 3 31 | } 32 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/structexample/struct_embedding_not_subclassing_02_test.go: -------------------------------------------------------------------------------- 1 | package structexample 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Human struct { 8 | Name string 9 | } 10 | 11 | func (b Human) Greet() { 12 | fmt.Println("Hello, i'm", b.Name) 13 | } 14 | 15 | type Contractor struct { 16 | Human 17 | Name string // This field has same name as in `Human` struct! 18 | } 19 | 20 | func ExampleEmbeddingIsNotSubclassing() { 21 | fb := Contractor{ 22 | Human: Human{Name: "Ivan"}, 23 | Name: "Petr", 24 | } 25 | fb.Greet() // call will be promoted to `fb.Human` field, which doesn't know anything about `Contractor.Name` field! 26 | fb.Human.Greet() 27 | 28 | // OUTPUT: 29 | // Hello, i'm Ivan 30 | // Hello, i'm Ivan 31 | } 32 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/structexample/struct_initialization_01_test.go: -------------------------------------------------------------------------------- 1 | package structexample 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type Point struct { 8 | X, Y int 9 | } 10 | 11 | func TestDumpValues(t *testing.T) { 12 | var ( 13 | p = Point{1, 2} // has type Point 14 | q = &Point{1, 2} // has type *Point 15 | r = Point{X: 1} // Y:0 is implicit 16 | s = Point{} // X:0 and Y:0 17 | ) 18 | 19 | t.Log(p, q, r, s) 20 | } 21 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/structexample/struct_tags_03_test.go: -------------------------------------------------------------------------------- 1 | package structexample 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | type Core struct { 9 | ID string `json:"id"` 10 | Num int `json:"num"` 11 | } 12 | 13 | type Data struct { 14 | Core // `json:"core"` // <-- try to comment/uncomment struct tag 15 | Count int `json:"count"` 16 | } 17 | 18 | func TestJsonMarshaling(t *testing.T) { 19 | data := Data{ 20 | Core: Core{ 21 | ID: "1", 22 | Num: 123, 23 | }, 24 | Count: 777, 25 | } 26 | 27 | out, err := json.MarshalIndent(data, " ", " ") 28 | if err != nil { 29 | panic(err) // for simplicity 30 | } 31 | t.Logf("\n%s\n", out) 32 | } 33 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/userdefinedtype/converting_slices_02_test.go: -------------------------------------------------------------------------------- 1 | package userdefinedtype 2 | 3 | import "fmt" 4 | 5 | func ExampleFailedCompilation() { 6 | type T1 int 7 | type T2 int 8 | var t1 T1 9 | 10 | var x = T2(t1) // OK 11 | _ = x 12 | 13 | var src []T1 14 | _ = src 15 | // var sx = ([]T2)(src) // NOT OK, code will not compile! 16 | } 17 | 18 | func ExampleProperConversion() { 19 | type T1 int 20 | type T2 int 21 | 22 | src := []T1{1, 2, 3, 4} 23 | fmt.Println(src) 24 | 25 | dest := make([]T2, 0, len(src)) 26 | for _, val := range src { 27 | dest = append(dest, T2(val)) 28 | } 29 | 30 | fmt.Println(dest) 31 | 32 | // Output: 33 | // [1 2 3 4] 34 | // [1 2 3 4] 35 | } 36 | -------------------------------------------------------------------------------- /session08_Slice_gotchas_and_Struct_types_in_Go/code/userdefinedtype/sample_type_definitions_01_test.go: -------------------------------------------------------------------------------- 1 | package userdefinedtype 2 | 3 | import "fmt" 4 | 5 | type ID string 6 | 7 | type IDs []ID 8 | 9 | type IDSet map[ID]struct{} 10 | 11 | type Human struct { 12 | ID ID 13 | Name string 14 | Age int 15 | } 16 | 17 | // Hello is a function with side effects. 18 | func (h Human) Hello() { 19 | fmt.Printf("Hello, my name is %q, my ID is %q, I'm %d years old", h.Name, h.ID, h.Age) 20 | } 21 | -------------------------------------------------------------------------------- /session09_Interfaces_and_Functions_in_Go/Funcitons_and_Methods_in_Go_Introduction_to_Interfaces.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session09_Interfaces_and_Functions_in_Go/Funcitons_and_Methods_in_Go_Introduction_to_Interfaces.pdf -------------------------------------------------------------------------------- /session09_Interfaces_and_Functions_in_Go/code/funcitonexample/01_function_named_return_variables_test.go: -------------------------------------------------------------------------------- 1 | package funcitonexample 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestNamedReturnVariables(t *testing.T) { 10 | gotErr := functionWithNamedReturnVariables() 11 | if gotErr == nil { 12 | t.FailNow() 13 | } 14 | } 15 | 16 | func functionWithNamedReturnVariables() (err error) { 17 | // If there is a panic we need to recover in a deferred func: 18 | defer func() { 19 | if r := recover(); r != nil { 20 | // the named return value will be modified: 21 | err = errors.New("return value will be modified successfully because of named return value") 22 | } 23 | }() 24 | 25 | legacyFunctionThatWillPanic() // always panic inside 26 | 27 | fmt.Println("This is unreachable call!") 28 | return err // because of panic, this return statement won't be executed 29 | } 30 | 31 | func legacyFunctionThatWillPanic() { 32 | panic("PANIC!") 33 | } 34 | -------------------------------------------------------------------------------- /session09_Interfaces_and_Functions_in_Go/code/funcitonexample/02_closure_generator_test.go: -------------------------------------------------------------------------------- 1 | package funcitonexample 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type producer func() uint 8 | 9 | func makeEvenGenerator() producer { 10 | i := uint(0) // this variable captured by reference!!! 11 | 12 | return func() (ret uint) { 13 | ret = i 14 | i += 2 15 | return 16 | } 17 | } 18 | 19 | func TestGenerator(t *testing.T) { 20 | nextEvenFn := makeEvenGenerator() 21 | 22 | evenNumber := nextEvenFn() 23 | t.Log("first call:", evenNumber) // Out: 0 24 | if evenNumber != 0 { 25 | t.FailNow() 26 | } 27 | 28 | evenNumber = nextEvenFn() 29 | t.Log("second call:", evenNumber) // Out: 2 30 | if evenNumber != 2 { 31 | t.FailNow() 32 | } 33 | 34 | evenNumber = nextEvenFn() 35 | t.Log("third call:", evenNumber) // Out: 4 36 | if evenNumber != 4 { 37 | t.FailNow() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /session09_Interfaces_and_Functions_in_Go/code/funcitonexample/03_closure_goroutine_pitfall_test.go: -------------------------------------------------------------------------------- 1 | package funcitonexample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestBrokenGoroutinePitfall(t *testing.T) { 9 | done := make(chan bool) 10 | 11 | values := []string{"a", "b", "c"} 12 | for _, v := range values { 13 | go func() { 14 | fmt.Println("value:", v) 15 | done <- true 16 | }() 17 | } 18 | 19 | // wait for all goroutines to complete before exiting 20 | for _ = range values { 21 | <-done 22 | } 23 | 24 | // OUTPUT: 25 | // value: c 26 | // value: c 27 | // value: c 28 | } 29 | 30 | func TestFixedGoroutinePitfall(t *testing.T) { 31 | done := make(chan bool) 32 | 33 | values := []string{"a", "b", "c"} 34 | for _, v := range values { 35 | v := v // create a new local variable `v` 36 | go func() { 37 | fmt.Println("value:", v) 38 | done <- true 39 | }() 40 | } 41 | 42 | // wait for all goroutines to complete before exiting 43 | for _ = range values { 44 | <-done 45 | } 46 | 47 | // OUTPUT might be random, but will contain all three letters: 48 | // value: a 49 | // value: c 50 | // value: b 51 | } 52 | -------------------------------------------------------------------------------- /session09_Interfaces_and_Functions_in_Go/code/go.mod: -------------------------------------------------------------------------------- 1 | module session09 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /session09_Interfaces_and_Functions_in_Go/code/interfacesexample/01_interface_general_info_test.go: -------------------------------------------------------------------------------- 1 | package interfacesexample 2 | 3 | import "testing" 4 | 5 | // Values of interface `GetSet` can be assigned to values of type `Getter`. 6 | type Getter interface { 7 | Get() string 8 | } 9 | 10 | // Values of interface `GetSet` can be assigned to values of type `Setter`. 11 | type Setter interface { 12 | Set(val string) // same as just: Set(int) 13 | } 14 | 15 | // Values of interfaces `Getter` or `Setter` CAN NOT be assigned to values of type `GetSet`. 16 | type GetSet interface { 17 | Getter 18 | Setter 19 | } 20 | 21 | type Human struct { 22 | name string 23 | } 24 | 25 | func (h *Human) Get() string { 26 | if h == nil { 27 | return "" 28 | } 29 | return h.name 30 | } 31 | 32 | func (h *Human) Set(name string) { 33 | if h == nil { 34 | return 35 | } 36 | h.name = name 37 | } 38 | 39 | func TestHuman(t *testing.T) { 40 | h := &Human{name: "Sergio"} 41 | 42 | var getter Getter = h 43 | t.Logf("calling getter.Get(): %q\n", getter.Get()) 44 | 45 | var setter Setter = h 46 | setter.Set("Another name") 47 | 48 | t.Logf("again calling getter.Get(): %q\n", getter.Get()) 49 | } 50 | -------------------------------------------------------------------------------- /session09_Interfaces_and_Functions_in_Go/code/methodexample/01_method_set_mutator_test.go: -------------------------------------------------------------------------------- 1 | package methodexample 2 | 3 | import "testing" 4 | 5 | type Total struct { 6 | data int 7 | } 8 | 9 | func (t *Total) Get() int { 10 | return t.data 11 | } 12 | 13 | func (t *Total) Add(n int) { 14 | t.data += n 15 | } 16 | 17 | // BAD STYLE (mixing value and pointer receivers): 18 | func (t Total) Sub(n int) { // there is a bug in this method: we need to use pointer receiver 19 | t.data -= n 20 | } 21 | 22 | func TestTotal(t *testing.T) { 23 | total := Total{} 24 | 25 | total.Add(10) // !!! This is same as (&total).Add(10) 26 | total.Add(20) 27 | if total.Get() != 30 { 28 | t.Fatalf("want: %d, got: %d", 30, total.Get()) 29 | } 30 | 31 | total.Sub(30) 32 | if total.Get() != 0 { // test will fail, if the `Sub()` method is defined on value receiver 33 | t.Fatalf("want: %d, got: %d", 0, total.Get()) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /session09_Interfaces_and_Functions_in_Go/code/methodexample/02_method_are_just_funcitons_test.go: -------------------------------------------------------------------------------- 1 | package methodexample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | type Human struct { 9 | name string 10 | } 11 | 12 | func (h Human) getName() string { 13 | return h.name 14 | } 15 | 16 | func (h *Human) setName(name string) { 17 | h.name = name 18 | } 19 | 20 | func (h *Human) String() string { 21 | return fmt.Sprintf("Human=%v", *h) // change here `*h` to just `h` 22 | } 23 | 24 | func TestMethodsAreJustFunctions(t *testing.T) { 25 | human := Human{name: "Sergio"} 26 | 27 | // Assign method on VALUE RECEIVER to the variable: 28 | var getFn = Human.getName 29 | gotName := getFn(human) 30 | t.Logf("got name: %q\n", gotName) 31 | 32 | // Assign method on POINTER RECEIVER to the variable: 33 | var setFn = (*Human).setName 34 | setFn(&human, "new name") 35 | t.Logf("After chaning the name: %q\n", human.name) 36 | } 37 | 38 | func TestStackOverflow(t *testing.T) { 39 | human := Human{name: "Sergio"} 40 | 41 | t.Log(human.String()) 42 | } 43 | -------------------------------------------------------------------------------- /session10_Interfaces_in_Go/Session_10_Interfaces_in_Go.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session10_Interfaces_in_Go/Session_10_Interfaces_in_Go.pdf -------------------------------------------------------------------------------- /session10_Interfaces_in_Go/code/go.mod: -------------------------------------------------------------------------------- 1 | module session10 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /session10_Interfaces_in_Go/code/interfaceecample/01_system_without_interfaces_test.go: -------------------------------------------------------------------------------- 1 | package interfaceecample_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | type PetrolEngine struct{} 9 | 10 | func (PetrolEngine) Refill() { 11 | fmt.Println("Petrol ENGINE refilled") 12 | } 13 | 14 | type CarBody struct{} 15 | 16 | func (CarBody) Load() { 17 | fmt.Println("Car BODY loaded") 18 | } 19 | 20 | type Vehicle struct { 21 | PetrolEngine 22 | CarBody 23 | } 24 | 25 | // NewVehicle is a Vehicle constructor. 26 | func NewVehicle(petrolEngine PetrolEngine, carBody CarBody) *Vehicle { 27 | return &Vehicle{PetrolEngine: petrolEngine, CarBody: carBody} 28 | } 29 | 30 | // ----------------------------------------------------------------------- 31 | 32 | func RefillVehicleEngine(vehicle *Vehicle) { 33 | vehicle.Refill() 34 | } 35 | 36 | func LoadVehicleBody(vehicle *Vehicle) { 37 | vehicle.Load() 38 | } 39 | 40 | func TestBuildRigidVehicle(t *testing.T) { 41 | engine := PetrolEngine{} 42 | body := CarBody{} 43 | 44 | vehicle := NewVehicle(engine, body) 45 | 46 | vehicle.Load() 47 | vehicle.Refill() 48 | 49 | // OR later on: 50 | LoadVehicleBody(vehicle) 51 | RefillVehicleEngine(vehicle) 52 | } 53 | -------------------------------------------------------------------------------- /session10_Interfaces_in_Go/code/interfaceecample/02_system_with_interfaces_test.go: -------------------------------------------------------------------------------- 1 | package interfaceecample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | type PetrolEngine struct{} 9 | 10 | func (PetrolEngine) Refill() { 11 | fmt.Println("Petrol ENGINE refilled") 12 | } 13 | 14 | type ElectricEngine struct{} 15 | 16 | func (ElectricEngine) Refill() { 17 | fmt.Println("Electric ENGINE refilled") 18 | } 19 | 20 | type CarBody struct{} 21 | 22 | func (CarBody) Load() { 23 | fmt.Println("Car BODY loaded") 24 | } 25 | 26 | type TruckBody struct{} 27 | 28 | func (TruckBody) Load() { 29 | fmt.Println("Truck BODY loaded") 30 | } 31 | 32 | // ----------------------------------------------------------------------- 33 | 34 | type Engine interface { 35 | Refill() 36 | } 37 | 38 | type Body interface { 39 | Load() 40 | } 41 | 42 | type Vehicle struct { 43 | Engine 44 | Body 45 | } 46 | 47 | // NewVehicle is a Vehicle constructor. 48 | func NewVehicle(engine Engine, body Body) *Vehicle { 49 | return &Vehicle{ 50 | Engine: engine, 51 | Body: body, 52 | } 53 | } 54 | 55 | // ----------------------------------------------------------------------- 56 | 57 | func RefillVehicleEngine(engine Engine) { 58 | engine.Refill() 59 | } 60 | 61 | func LoadVehicleBody(body Body) { 62 | body.Load() 63 | } 64 | 65 | func TestBuildFlexibleVehicle(t *testing.T) { 66 | engine := PetrolEngine{} // or ElectricEngine{} 67 | body := TruckBody{} // or CarBody{} 68 | 69 | vehicle := NewVehicle(engine, body) 70 | 71 | vehicle.Load() 72 | vehicle.Refill() 73 | 74 | // OR later on: 75 | LoadVehicleBody(vehicle) 76 | RefillVehicleEngine(vehicle) 77 | } 78 | -------------------------------------------------------------------------------- /session10_Interfaces_in_Go/code/interfaceecample/03_empty_interface_test.go: -------------------------------------------------------------------------------- 1 | package interfaceecample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func describe(i interface{}) { 9 | fmt.Printf("(%v, %T)\n", i, i) 10 | } 11 | 12 | func TestEmptyInterface(t *testing.T) { 13 | var i interface{} 14 | describe(i) // (, ) 15 | 16 | i = 42 17 | describe(i) // (42, int) 18 | 19 | i = "hello" 20 | describe(i) // (hello, string) 21 | } 22 | -------------------------------------------------------------------------------- /session10_Interfaces_in_Go/code/interfaceecample/04_type_assert_test.go: -------------------------------------------------------------------------------- 1 | package interfaceecample 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func timeMap(y interface{}) { 9 | z, ok := y.(map[string]interface{}) 10 | if ok { 11 | z["updated_at"] = time.Now() 12 | } 13 | } 14 | 15 | func TestTypeAssert(t *testing.T) { 16 | foo := map[string]interface{}{ 17 | "Matt": 42, 18 | } 19 | 20 | timeMap(foo) 21 | 22 | t.Log(foo) 23 | } 24 | -------------------------------------------------------------------------------- /session10_Interfaces_in_Go/code/interfaceecample/05_nil_receiver_in_methods_test.go: -------------------------------------------------------------------------------- 1 | package interfaceecample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | type I interface { 9 | M() 10 | } 11 | 12 | type T struct { 13 | S string 14 | } 15 | 16 | func (t *T) M() { 17 | if t == nil { 18 | fmt.Println("Got receiver") 19 | return 20 | } 21 | fmt.Println(t.S) 22 | } 23 | 24 | func describeInterface(i I) { 25 | fmt.Printf("(%v, %T)\n", i, i) 26 | } 27 | 28 | func TestNilValue(t *testing.T) { 29 | var i I 30 | 31 | var val *T // Not initialised! 32 | i = val 33 | describeInterface(i) 34 | i.M() // nil receiver 35 | val.M() // nil receiver 36 | 37 | // OUTPUT: 38 | // (, *interfaceecample.T) 39 | // Got receiver 40 | // Got receiver 41 | } 42 | -------------------------------------------------------------------------------- /session10_Interfaces_in_Go/code/interfaceecample/06_wrong_errors_check_test.go: -------------------------------------------------------------------------------- 1 | package interfaceecample 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | type MyError struct { 9 | } 10 | 11 | func (m MyError) Error() string { 12 | return "my error" 13 | } 14 | 15 | func incorrectlyReturnsError() error { 16 | var p *MyError = nil 17 | 18 | // ..... some code ......... 19 | 20 | return p // Will always return a non-nil error. 21 | } 22 | 23 | func correctlyReturnsError() error { 24 | if true { 25 | return errors.New("some error") 26 | } 27 | return nil 28 | } 29 | 30 | func TestReturnError(t *testing.T) { 31 | gotErr := incorrectlyReturnsError() 32 | t.Logf("got error: %v ; Is this error equal to nil? %t\n", gotErr, gotErr == nil) 33 | 34 | gotErr = correctlyReturnsError() 35 | t.Logf("got another error: %v\n", gotErr) 36 | } 37 | -------------------------------------------------------------------------------- /session10_Interfaces_in_Go/code/interfaceecample/07_failed_type_assertion_test.go: -------------------------------------------------------------------------------- 1 | package interfaceecample 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestFailedTypeAssertion(t *testing.T) { 8 | var data interface{} = "great" // actually a string value 9 | 10 | if res, ok := data.(int); ok { 11 | t.Log("[is an int] value =>", res) 12 | } else { 13 | t.Log("[not an int] value =>", res) // This is a BUG! 14 | 15 | // Failed type assertions return the "zero value" for the target type used in the assertion statement: 16 | // prints: [not an int] value => 0 (not "great") 17 | 18 | t.Log("[not an int] value =>", data) // OK! 19 | // prints: [not an int] value => great (as expected) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /session11_Testing_in_Go_and_Memory_allocations_and_alignment/Session11-Memory_allocations_and_alignment_Testing_in_Go-Part_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session11_Testing_in_Go_and_Memory_allocations_and_alignment/Session11-Memory_allocations_and_alignment_Testing_in_Go-Part_1.pdf -------------------------------------------------------------------------------- /session11_Testing_in_Go_and_Memory_allocations_and_alignment/code/go.mod: -------------------------------------------------------------------------------- 1 | module session11 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /session11_Testing_in_Go_and_Memory_allocations_and_alignment/code/interfacepollution/01_interface_pollution_test.go: -------------------------------------------------------------------------------- 1 | package interfacepollution 2 | 3 | // Code is from this article: https://www.ardanlabs.com/blog/2016/10/avoid-interface-pollution.html 4 | 5 | // ------------------------------------------------------------------------------------------------ 6 | 7 | // Here is the interface pollution smell list for the code : 8 | 9 | // 1. The package declares an interface that matches the entire API of its own concrete type. 10 | // 2. The factory function returns the interface value with the unexported concrete type value inside. 11 | // 3. The interface can be removed and nothing changes for the user of the API. 12 | // 4. The interface is not decoupling the API from change. 13 | 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | // Server defines a contract for tcp servers. 17 | type Server interface { 18 | Start() error 19 | Stop() error 20 | Wait() error 21 | } 22 | 23 | // server is our Server implementation. 24 | type server struct { 25 | host string 26 | } 27 | 28 | // THIS IS A BAD CONSTRUCTOR: 29 | 30 | // NewServer returns an interface value of type Server 31 | // with an xServer implementation. 32 | func NewServer(host string) Server { 33 | return &server{host} 34 | } 35 | 36 | // The next code listing shows how removing the interface changes nothing for the user: 37 | 38 | // func NewServer(host string) *Server { 39 | // return &Server{host} 40 | // } 41 | // Having the user work with the concrete type directly doesn’t change anything for the user or the API. 42 | 43 | // Start allows the server to begin to accept requests. 44 | func (s *server) Start() error { 45 | /* impl */ 46 | return nil 47 | } 48 | 49 | // Stop shuts the server down. 50 | func (s *server) Stop() error { 51 | /* impl */ 52 | return nil 53 | } 54 | 55 | // Wait prevents the server from accepting new connections. 56 | func (s *server) Wait() error { 57 | /* impl */ 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /session11_Testing_in_Go_and_Memory_allocations_and_alignment/code/memorylayout/01_stacktrace.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // -------------------------------- 8 | 9 | type oper interface { 10 | op() string 11 | } 12 | 13 | type B struct { 14 | s string 15 | } 16 | 17 | func (a *B) op() string { 18 | return a.s 19 | } 20 | 21 | func interfacePanic(o oper) { 22 | fmt.Println(o.op() + "!") 23 | panic("I'm outta here") 24 | } 25 | 26 | // -------------------------------- 27 | 28 | type A struct { 29 | i int 30 | s string 31 | } 32 | 33 | func (a A) iPanic(b bool) { 34 | fmt.Println(a.i + 1) 35 | panic("I'm outta here") 36 | } 37 | 38 | func main() { 39 | // Using interfaces: 40 | b := &B{"op"} 41 | interfacePanic(b) 42 | 43 | // Uncomment to see effect: 44 | // a := A{i: 50} 45 | // a.iPanic(true) 46 | } 47 | -------------------------------------------------------------------------------- /session11_Testing_in_Go_and_Memory_allocations_and_alignment/code/testspart1/greeter.go: -------------------------------------------------------------------------------- 1 | package testspart1 2 | 3 | func Greeting(name string) string { 4 | // log.Println("got argument:", name) // non-pure function 5 | 6 | return "Greeting, " + name + " !" // pure function 7 | } 8 | -------------------------------------------------------------------------------- /session11_Testing_in_Go_and_Memory_allocations_and_alignment/code/testspart1/greeter_test.go: -------------------------------------------------------------------------------- 1 | package testspart1 2 | 3 | import "testing" 4 | 5 | func TestGreeting1(t *testing.T) { 6 | // - Setup (optional) 7 | 8 | // - Execute the code under testing 9 | gotGreeting := Greeting("someName") 10 | 11 | // - Check (assert) the results 12 | want := "Greeting, someName !" 13 | if want != gotGreeting { 14 | t.FailNow() 15 | } 16 | 17 | // - Tear-down (optional) 18 | } 19 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/Session12_Testing-Part2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session12_Testing_Part_II/Session12_Testing-Part2.pdf -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/benchexample/01_fib_test.go: -------------------------------------------------------------------------------- 1 | package benchexample 2 | 3 | import "testing" 4 | 5 | // RUN from shell: go test -bench=. 6 | 7 | func BenchmarkFib10(b *testing.B) { 8 | // run the Fib function b.N times 9 | for n := 0; n < b.N; n++ { 10 | Fib(10) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/benchexample/02_fib_test.go: -------------------------------------------------------------------------------- 1 | package benchexample 2 | 3 | import "testing" 4 | 5 | // Various inputs: 6 | 7 | func benchmarkFib(i int, b *testing.B) { 8 | for n := 0; n < b.N; n++ { 9 | Fib(i) 10 | } 11 | } 12 | 13 | func BenchmarkFib1(b *testing.B) { benchmarkFib(1, b) } 14 | func BenchmarkFib2(b *testing.B) { benchmarkFib(2, b) } 15 | func BenchmarkFib3(b *testing.B) { benchmarkFib(3, b) } 16 | func BenchmarkFib20(b *testing.B) { benchmarkFib(20, b) } 17 | func BenchmarkFib40(b *testing.B) { benchmarkFib(40, b) } 18 | 19 | // Typical output: 20 | 21 | // BenchmarkFib1 1000000000 2.84 ns/op 22 | // BenchmarkFib2 500000000 7.92 ns/op 23 | // BenchmarkFib3 100000000 13.0 ns/op 24 | // BenchmarkFib10 5000000 447 ns/op 25 | // BenchmarkFib20 50000 55668 ns/op 26 | // BenchmarkFib40 2 942888676 ns/op 27 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/benchexample/03_fib_test.go: -------------------------------------------------------------------------------- 1 | package benchexample 2 | 3 | import "testing" 4 | 5 | // Wrong benchmarks: 6 | 7 | func BenchmarkFibWrong(b *testing.B) { 8 | for n := 0; n < b.N; n++ { 9 | Fib(n) // <- wrong argument 10 | } 11 | } 12 | 13 | func BenchmarkFibWrong2(b *testing.B) { 14 | Fib(b.N) 15 | } 16 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/benchexample/04_fib_test.go: -------------------------------------------------------------------------------- 1 | package benchexample 2 | 3 | import "testing" 4 | 5 | // A note on compiler optimisations: 6 | 7 | var result int 8 | 9 | func BenchmarkFibComplete(b *testing.B) { 10 | var r int 11 | for n := 0; n < b.N; n++ { 12 | // always record the result of Fib to prevent 13 | // the compiler eliminating the function call. 14 | r = Fib(10) 15 | } 16 | // always store the result to a package level variable 17 | // so the compiler cannot eliminate the Benchmark itself. 18 | result = r 19 | } 20 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/benchexample/fib.go: -------------------------------------------------------------------------------- 1 | package benchexample 2 | 3 | func Fib(n int) int { 4 | if n < 2 { 5 | return n 6 | } 7 | return Fib(n-1) + Fib(n-2) 8 | } 9 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/go.mod: -------------------------------------------------------------------------------- 1 | module session12 2 | 3 | go 1.19 4 | 5 | require github.com/stretchr/testify v1.8.1 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 8 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 9 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 10 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 11 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 12 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 16 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 17 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/testexample/01_greeter.go: -------------------------------------------------------------------------------- 1 | package testspart1 2 | 3 | func Greeting(name string) string { 4 | if name == "" { 5 | return "" 6 | } 7 | // log.Println("got argument:", name) // non-pure function 8 | return "Greeting, " + name + " !" // pure function 9 | } 10 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/testexample/01_greeter_test.go: -------------------------------------------------------------------------------- 1 | package testspart1 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestGreetingSimple(t *testing.T) { 10 | // 1. Setup (optional) 11 | // nothing here 12 | 13 | // 2. Execute the code under testing 14 | got := Greeting("Sergio") 15 | 16 | // 3. Check (assert) the results 17 | want := "Greeting, Sergio !" 18 | if got != want { 19 | t.Fatalf("Greeting() = %v, want %v", got, want) 20 | // t.Errorf("Greeting() = %v, want %v", got, want) 21 | } 22 | 23 | // 4. Tear-down (optional) 24 | // nothing here 25 | } 26 | 27 | // !! install testify library: 28 | // go get github.com/stretchr/testify 29 | // go mod tidy 30 | 31 | func TestGreetingWithSubtest(t *testing.T) { 32 | // 1. Setup (optional) 33 | // nothing here 34 | 35 | t.Run("non-empty_user_name", func(t *testing.T) { 36 | // 2. Execute the code under testing 37 | got := Greeting("Sergio") 38 | 39 | // 3. Check (assert) the results 40 | want := "Greeting, Sergio !" 41 | require.Equal(t, want, got) 42 | }) 43 | 44 | t.Run("empty_user_name", func(t *testing.T) { 45 | // 2. Execute the code under testing 46 | got := Greeting("") 47 | 48 | // 3. Check (assert) the results 49 | want := "" 50 | require.Equal(t, want, got) 51 | }) 52 | 53 | // 4. Tear-down (optional) 54 | // nothing here 55 | } 56 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/testexample/02_greeter_test.go: -------------------------------------------------------------------------------- 1 | package testspart1 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGreetingTableDriven(t *testing.T) { 8 | type args struct { 9 | name string 10 | } 11 | tests := map[string]struct { 12 | args args 13 | want string 14 | }{ 15 | "empty_input": { 16 | args: args{ 17 | name: "", 18 | }, 19 | want: "", 20 | }, 21 | "non-empty_input": { 22 | args: args{ 23 | name: "Sergio", 24 | }, 25 | want: "Greeting, Sergio !", 26 | }, 27 | } 28 | for name, tt := range tests { 29 | tt := tt // PLEASE DO NOT FORGET TO ADD THIS! 30 | t.Run(name, func(t *testing.T) { 31 | if got := Greeting(tt.args.name); got != tt.want { 32 | t.Errorf("Greeting() = %v, want %v", got, tt.want) 33 | } 34 | }) 35 | } 36 | } 37 | 38 | // TODO Try to generate table-driven tests for Greeting() using your IDE. 39 | -------------------------------------------------------------------------------- /session12_Testing_Part_II/code/testexample/03_greeter_test.go: -------------------------------------------------------------------------------- 1 | package testspart1 2 | 3 | import "fmt" 4 | 5 | func ExampleGreeting() { 6 | message := Greeting("Sergio") 7 | fmt.Printf("Got: %s", message) 8 | 9 | // Output: 10 | // Got: Greeting, Sergio ! 11 | } 12 | -------------------------------------------------------------------------------- /session13_Packages_in_Go/Session13_Benchmarks_and_Packages_in_Go.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session13_Packages_in_Go/Session13_Benchmarks_and_Packages_in_Go.pdf -------------------------------------------------------------------------------- /session13_Packages_in_Go/code/benchexample/01_fib_test.go: -------------------------------------------------------------------------------- 1 | package benchexample 2 | 3 | import "testing" 4 | 5 | // RUN from shell: go test -bench=. 6 | 7 | func BenchmarkFib10(b *testing.B) { 8 | // run the Fib function b.N times 9 | for n := 0; n < b.N; n++ { 10 | Fib(10) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /session13_Packages_in_Go/code/benchexample/02_fib_test.go: -------------------------------------------------------------------------------- 1 | package benchexample 2 | 3 | import "testing" 4 | 5 | // Various inputs: 6 | 7 | func benchmarkFib(i int, b *testing.B) { 8 | for n := 0; n < b.N; n++ { 9 | Fib(i) 10 | } 11 | } 12 | 13 | func BenchmarkFib1(b *testing.B) { benchmarkFib(1, b) } 14 | func BenchmarkFib2(b *testing.B) { benchmarkFib(2, b) } 15 | func BenchmarkFib3(b *testing.B) { benchmarkFib(3, b) } 16 | func BenchmarkFib20(b *testing.B) { benchmarkFib(20, b) } 17 | func BenchmarkFib40(b *testing.B) { benchmarkFib(40, b) } 18 | 19 | // Typical output: 20 | 21 | // BenchmarkFib1 1000000000 2.84 ns/op 22 | // BenchmarkFib2 500000000 7.92 ns/op 23 | // BenchmarkFib3 100000000 13.0 ns/op 24 | // BenchmarkFib10 5000000 447 ns/op 25 | // BenchmarkFib20 50000 55668 ns/op 26 | // BenchmarkFib40 2 942888676 ns/op 27 | -------------------------------------------------------------------------------- /session13_Packages_in_Go/code/benchexample/03_fib_test.go: -------------------------------------------------------------------------------- 1 | package benchexample 2 | 3 | import "testing" 4 | 5 | // Wrong benchmarks: 6 | 7 | func BenchmarkFibWrong(b *testing.B) { 8 | for n := 0; n < b.N; n++ { 9 | Fib(n) // <- wrong argument 10 | } 11 | } 12 | 13 | func BenchmarkFibWrong2(b *testing.B) { 14 | Fib(b.N) 15 | } 16 | -------------------------------------------------------------------------------- /session13_Packages_in_Go/code/benchexample/04_fib_test.go: -------------------------------------------------------------------------------- 1 | package benchexample 2 | 3 | import "testing" 4 | 5 | // A note on compiler optimisations: 6 | 7 | var result int 8 | 9 | func BenchmarkFibComplete(b *testing.B) { 10 | var r int 11 | for n := 0; n < b.N; n++ { 12 | // always record the result of Fib to prevent 13 | // the compiler eliminating the function call. 14 | r = Fib(10) 15 | } 16 | // always store the result to a package level variable 17 | // so the compiler cannot eliminate the Benchmark itself. 18 | result = r 19 | } 20 | -------------------------------------------------------------------------------- /session13_Packages_in_Go/code/benchexample/fib.go: -------------------------------------------------------------------------------- 1 | package benchexample 2 | 3 | func Fib(n int) int { 4 | if n < 2 { 5 | return n 6 | } 7 | return Fib(n-1) + Fib(n-2) 8 | } 9 | -------------------------------------------------------------------------------- /session13_Packages_in_Go/code/go.mod: -------------------------------------------------------------------------------- 1 | module session13 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /session14_Modules_in_Go/Session14_Modules_in_Go.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session14_Modules_in_Go/Session14_Modules_in_Go.pdf -------------------------------------------------------------------------------- /session14_Modules_in_Go/code/graterm-example/go.mod: -------------------------------------------------------------------------------- 1 | module gratermexample 2 | 3 | go 1.19 4 | 5 | require github.com/skovtunenko/graterm v1.1.0 6 | -------------------------------------------------------------------------------- /session14_Modules_in_Go/code/graterm-example/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 5 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 6 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/skovtunenko/graterm v1.1.0 h1:HBriftqKcrKraetGJ08UOHYE/5Pfmo+59tLqOnGx0Wg= 10 | github.com/skovtunenko/graterm v1.1.0/go.mod h1:cSyp3pV5Ll4fusmJLVBNb49F3y2khMNrNn9x/xgu1hE= 11 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 12 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 13 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 15 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 16 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 17 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 18 | go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= 19 | go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 20 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 21 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 22 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 23 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 24 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 25 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 26 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 27 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 28 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 29 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 30 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 31 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 32 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 33 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 34 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 36 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 37 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 38 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 39 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 40 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 41 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 42 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 43 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 44 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 45 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 46 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 47 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 48 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 49 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 50 | -------------------------------------------------------------------------------- /session14_Modules_in_Go/code/graterm-example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "time" 10 | 11 | "github.com/skovtunenko/graterm" 12 | ) 13 | 14 | // Instructions: 15 | // - run the application, reach out to http://localhost:8080/ 16 | // - terminate the application (CTRL+C) 17 | // - investigate the log output 18 | 19 | const hostPort = ":8080" 20 | 21 | const ( 22 | globalTerminationTimeout = 40 * time.Second 23 | 24 | httpServerTerminationTimeout = 5 * time.Second 25 | httpServerControllerSleepTime = 15 * time.Second 26 | 27 | messagingTerminationTimeout = 1 * time.Second 28 | 29 | fastDBTerminationTimeout = 1 * time.Second 30 | 31 | slowDBTerminationTimeout = 3 * time.Second 32 | slowDBSleepTime = 5 * time.Second // Termination of SlowDB can't be finished in time 33 | ) 34 | 35 | const ( 36 | HTTPServerOrder graterm.Order = 0 37 | MessagingOrder graterm.Order = 1 38 | FastDBOrder graterm.Order = 2 39 | SlowDBOrder graterm.Order = 2 40 | ) 41 | 42 | func main() { 43 | logger := log.Default() 44 | 45 | logger.Println("Application started...") 46 | 47 | terminator, appCtx := graterm.NewWithSignals(context.Background()) 48 | terminator.SetLogger(logger) 49 | 50 | // Wire application components: 51 | slowDB := NewSlowDB(terminator, logger) 52 | fastDB := NewFastDB(terminator, logger) 53 | messaging := NewMessaging(terminator, logger) 54 | srv := NewServer(terminator, logger) 55 | app := NewApplication(logger, srv, messaging, slowDB, fastDB) 56 | app.Run() 57 | 58 | if err := terminator.Wait(appCtx, globalTerminationTimeout); err != nil { 59 | logger.Printf("graceful termination period is timed out: %+v", err) 60 | } 61 | logger.Println("Application ended.") 62 | } 63 | 64 | type Application struct { 65 | Log *log.Logger 66 | Server *Server 67 | Messaging *Messaging 68 | SlowDB *SlowDB 69 | FastDB *FastDB 70 | } 71 | 72 | func NewApplication(log *log.Logger, server *Server, messaging *Messaging, slowDB *SlowDB, fastDB *FastDB) *Application { 73 | return &Application{Log: log, Server: server, Messaging: messaging, SlowDB: slowDB, FastDB: fastDB} 74 | } 75 | 76 | func (a *Application) Run() { 77 | a.SlowDB.Init() 78 | a.FastDB.Init() 79 | a.Messaging.Init() 80 | a.Server.Init() 81 | } 82 | 83 | type Server struct { 84 | logger *log.Logger 85 | terminator *graterm.Terminator 86 | } 87 | 88 | func NewServer(terminator *graterm.Terminator, logger *log.Logger) *Server { 89 | return &Server{terminator: terminator, logger: logger} 90 | } 91 | 92 | func (s *Server) Init() { 93 | defer s.logger.Println("HTTP Server initialized") 94 | 95 | httpServer := &http.Server{ 96 | Addr: hostPort, 97 | Handler: http.DefaultServeMux, 98 | } 99 | 100 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 101 | ctx := r.Context() 102 | select { 103 | case <-time.After(httpServerControllerSleepTime): // Simulate long-running in-flight HTTP request processing 104 | s.logger.Println("HTTP Server controller finished") 105 | case <-ctx.Done(): 106 | s.logger.Printf("HTTP Server controller interrupted because of: %+v\n", ctx.Err()) 107 | } 108 | fmt.Fprintf(w, "hello, world!\nSlept for %v seconds\n", httpServerControllerSleepTime) 109 | }) 110 | 111 | go func() { 112 | if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { 113 | log.Printf("terminated HTTP Server: %+v", err) 114 | } 115 | }() 116 | 117 | s.terminator.WithOrder(HTTPServerOrder). 118 | WithName("HTTPServer"). 119 | Register(httpServerTerminationTimeout, func(ctx context.Context) { 120 | s.logger.Println("terminating HTTP Server component...") 121 | defer s.logger.Println("...HTTP Server component terminated") 122 | 123 | if err := httpServer.Shutdown(ctx); err != nil { // stop terminating HTTP Server if the time for that is over. 124 | s.logger.Printf("shutdown HTTP Server: %+v", err) 125 | } 126 | }) 127 | 128 | s.logger.Printf("HTTP Server started on: %q\n", hostPort) 129 | } 130 | 131 | type Messaging struct { 132 | logger *log.Logger 133 | terminator *graterm.Terminator 134 | } 135 | 136 | func NewMessaging(terminator *graterm.Terminator, logger *log.Logger) *Messaging { 137 | return &Messaging{terminator: terminator, logger: logger} 138 | } 139 | 140 | func (m *Messaging) Init() { 141 | defer m.logger.Println("Messaging initialized") 142 | m.terminator.WithOrder(MessagingOrder). 143 | WithName("Messaging"). 144 | Register(messagingTerminationTimeout, func(ctx context.Context) { 145 | m.logger.Println("terminating Messaging component...") 146 | defer m.logger.Println("...Messaging component terminated") 147 | }) 148 | } 149 | 150 | type FastDB struct { 151 | logger *log.Logger 152 | terminator *graterm.Terminator 153 | } 154 | 155 | func NewFastDB(terminator *graterm.Terminator, logger *log.Logger) *FastDB { 156 | return &FastDB{terminator: terminator, logger: logger} 157 | } 158 | 159 | func (d *FastDB) Init() { 160 | defer d.logger.Println("FastDB initialized") 161 | d.terminator.WithOrder(FastDBOrder). 162 | WithName("FastDB"). 163 | Register(fastDBTerminationTimeout, func(ctx context.Context) { 164 | d.logger.Println("terminating FastDB component...") 165 | defer d.logger.Println("...FastDB component terminated") 166 | }) 167 | } 168 | 169 | type SlowDB struct { 170 | logger *log.Logger 171 | terminator *graterm.Terminator 172 | } 173 | 174 | func NewSlowDB(terminator *graterm.Terminator, logger *log.Logger) *SlowDB { 175 | return &SlowDB{terminator: terminator, logger: logger} 176 | } 177 | 178 | func (d *SlowDB) Init() { 179 | defer d.logger.Println("SlowDB initialized") 180 | d.terminator.WithOrder(SlowDBOrder). 181 | WithName("SlowDB"). 182 | Register(slowDBTerminationTimeout, func(ctx context.Context) { 183 | d.logger.Println("terminating SlowDB component...") 184 | defer d.logger.Println("...SlowDB component terminated") 185 | select { 186 | case <-time.After(slowDBSleepTime): 187 | d.logger.Println("SlowDB cleanup finished") 188 | case <-ctx.Done(): 189 | d.logger.Printf("SlowDB termination interrupted because of: %+v\n", ctx.Err()) 190 | } 191 | }) 192 | } 193 | -------------------------------------------------------------------------------- /session15_Goroutines_and_Channels_in_Go/Session15_Goroutines_and_Channels_in_Go.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session15_Goroutines_and_Channels_in_Go/Session15_Goroutines_and_Channels_in_Go.pdf -------------------------------------------------------------------------------- /session15_Goroutines_and_Channels_in_Go/code/go.mod: -------------------------------------------------------------------------------- 1 | module session 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /session15_Goroutines_and_Channels_in_Go/code/goroutinesexample/01_goroutines_test.go: -------------------------------------------------------------------------------- 1 | package goroutinesexample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func printValuesFrom(from string) { 10 | for i := 0; i < 3; i++ { 11 | fmt.Println(from, ":", i) 12 | } 13 | } 14 | 15 | func TestGoroutines_01(t *testing.T) { 16 | printValuesFrom("direct") 17 | 18 | go printValuesFrom("goroutine") 19 | 20 | go func(msg string) { 21 | fmt.Println(msg) 22 | }("going") 23 | 24 | time.Sleep(time.Second) 25 | fmt.Println("done") 26 | } 27 | -------------------------------------------------------------------------------- /session15_Goroutines_and_Channels_in_Go/code/goroutinesexample/02_bestpractice_test.go: -------------------------------------------------------------------------------- 1 | package goroutinesexample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestBestPractice1(t *testing.T) { 9 | c := make(chan string) 10 | 11 | for i := 1; i <= 2; i++ { 12 | go func(i int, co chan<- string) { 13 | for j := 1; j <= 2; j++ { 14 | co <- fmt.Sprintf("hi from %d.%d", i, j) 15 | } 16 | }(i, c) 17 | } 18 | 19 | for i := 1; i <= 4; i++ { 20 | fmt.Println(<-c) 21 | } 22 | } 23 | 24 | // OUTPUT: 25 | // Hi from 2.1 26 | // Hi from 2.2 27 | // Hi from 1.1 28 | // Hi from 1.2 29 | -------------------------------------------------------------------------------- /session15_Goroutines_and_Channels_in_Go/code/goroutinesexample/03_channel_test.go: -------------------------------------------------------------------------------- 1 | package goroutinesexample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestChannelBasic(t *testing.T) { 9 | messages := make(chan string) 10 | 11 | go func() { messages <- "ping" }() 12 | 13 | msg := <-messages 14 | fmt.Println(msg) 15 | } 16 | 17 | func TestChannelBuffering(t *testing.T) { 18 | messages := make(chan string, 2) 19 | 20 | messages <- "buffered" 21 | messages <- "channel" 22 | 23 | fmt.Println(<-messages) 24 | fmt.Println(<-messages) 25 | } 26 | 27 | func TestChannelDirections(t *testing.T) { 28 | ping := func(pings chan<- string, msg string) { 29 | pings <- msg 30 | } 31 | 32 | pong := func(pings <-chan string, pongs chan<- string) { 33 | msg := <-pings 34 | pongs <- msg 35 | } 36 | 37 | pings := make(chan string, 1) 38 | pongs := make(chan string, 1) 39 | ping(pings, "passed message") 40 | pong(pings, pongs) 41 | fmt.Println(<-pongs) 42 | } 43 | -------------------------------------------------------------------------------- /session16_The_most_useful_package_sync/Channels-Part2_The_most_Useful_package_in_Go_ sync.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session16_The_most_useful_package_sync/Channels-Part2_The_most_Useful_package_in_Go_ sync.pdf -------------------------------------------------------------------------------- /session16_The_most_useful_package_sync/code/01_chanexample/01_chan_iteration_test.go: -------------------------------------------------------------------------------- 1 | package chanexample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func FibonacciProducer(ch chan int, count int) { 9 | defer close(ch) 10 | n2, n1 := 0, 1 11 | for count >= 0 { 12 | ch <- n2 13 | count-- 14 | n2, n1 = n1, n2+n1 15 | } 16 | } 17 | 18 | func TestIterationUsingRangeOverChan(t *testing.T) { 19 | ch := make(chan int) 20 | go FibonacciProducer(ch, 10) 21 | idx := 0 22 | // To break such iteration channel needs to be closed explicitly. 23 | // Otherwise range would block forever in the same way as for nil channel. 24 | for num := range ch { 25 | fmt.Printf("F(%d): \t%d\n", idx, num) 26 | idx++ 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /session16_The_most_useful_package_sync/code/01_chanexample/02_select_statement_test.go: -------------------------------------------------------------------------------- 1 | package chanexample 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func f() int { 8 | return 42 9 | } 10 | func TestSelectStatementMix(t *testing.T) { 11 | var a []int 12 | 13 | var c1, c2, c3, c4 chan int 14 | var i1, i2 int 15 | 16 | select { 17 | case i1 = <-c1: 18 | print("received ", i1, " from c1\n") 19 | case c2 <- i2: 20 | print("sent ", i2, " to c2\n") 21 | case i3, ok := (<-c3): // same as: i3, ok := <-c3 22 | if ok { 23 | print("received ", i3, " from c3\n") 24 | } else { 25 | print("c3 is closed\n") 26 | } 27 | case a[f()] = <-c4: 28 | // same as: 29 | // case t := <-c4 30 | // a[f()] = t 31 | default: 32 | print("no communication\n") 33 | } 34 | 35 | } 36 | func TestSendRandomSequenceOfBits(t *testing.T) { 37 | var c = make(chan int) 38 | 39 | for { // send random sequence of bits to c 40 | select { 41 | case c <- 0: // note: no statement, no fallthrough, no folding of cases 42 | case c <- 1: 43 | } 44 | } 45 | } 46 | 47 | func TestBlockForever(t *testing.T) { 48 | select {} // block forever 49 | } 50 | -------------------------------------------------------------------------------- /session16_The_most_useful_package_sync/code/02_advancedexample/01_generate_data_test.go: -------------------------------------------------------------------------------- 1 | package advancedexample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func asChan(nums ...int) <-chan int { 11 | ch := make(chan int) // step 1 12 | 13 | go func() { // step 2 14 | defer close(ch) 15 | for _, val := range nums { 16 | ch <- val 17 | } 18 | }() 19 | 20 | return ch // step 3 21 | } 22 | 23 | func gen(nums ...int) <-chan int { 24 | out := make(chan int, len(nums)) 25 | defer close(out) 26 | for _, n := range nums { 27 | out <- n 28 | } 29 | return out 30 | } 31 | 32 | func sq(in <-chan int) <-chan int { 33 | out := make(chan int) // step 1 34 | 35 | fn := func(val int) int { 36 | return val * val 37 | } 38 | 39 | go func() { // step 2 40 | defer close(out) 41 | for val := range in { 42 | out <- fn(val) 43 | } 44 | }() 45 | 46 | return out // step 3 47 | } 48 | 49 | // --------------------------------------------------------------- 50 | // THIS IS TO DEMONSTRATE THE FLOW: 51 | // --------------------------------------------------------------- 52 | 53 | func TestExecuteDataPipeline(t *testing.T) { 54 | ch := asChan(2, 3) 55 | // OR : 56 | // c := gen(2, 3) 57 | 58 | out := sq(ch) 59 | 60 | fmt.Println(<-out) // 4 61 | fmt.Println(<-out) // 9 62 | } 63 | 64 | // --------------------------------------------------------------- 65 | // THIS IS TO SHOW HOW TO TEST CHAN-RELATED FUNCTIONS: 66 | // --------------------------------------------------------------- 67 | 68 | func Test_asChan(t *testing.T) { 69 | type args struct { 70 | nums []int 71 | } 72 | tests := []struct { 73 | name string 74 | args args 75 | want []int 76 | }{ 77 | { 78 | name: "nil arguments", 79 | args: args{nums: nil}, 80 | want: []int{}, 81 | }, 82 | { 83 | name: "empty incomming array", 84 | args: args{nums: make([]int, 0)}, 85 | want: []int{}, 86 | }, 87 | { 88 | name: "nil arguments", 89 | args: args{nums: []int{1, 2, 3}}, 90 | want: []int{1, 2, 3}, 91 | }, 92 | } 93 | for _, tt := range tests { 94 | tt := tt 95 | t.Run(tt.name, func(t *testing.T) { 96 | gotCh := asChan(tt.args.nums...) 97 | got := make([]int, 0, len(tt.args.nums)) 98 | for val := range gotCh { 99 | got = append(got, val) 100 | } 101 | require.Equal(t, tt.want, got) 102 | }) 103 | } 104 | } 105 | 106 | func Test_sq(t *testing.T) { 107 | type args struct { 108 | in func(t *testing.T) <-chan int 109 | } 110 | tests := []struct { 111 | name string 112 | args args 113 | want []int 114 | }{ 115 | { 116 | name: "closed chan", 117 | args: args{ 118 | in: func(t *testing.T) <-chan int { 119 | ch := make(chan int) 120 | defer close(ch) 121 | return ch 122 | }, 123 | }, 124 | want: []int{}, 125 | }, 126 | { 127 | name: "chan with one value", 128 | args: args{ 129 | in: func(t *testing.T) <-chan int { 130 | return asChan(1) 131 | }, 132 | }, 133 | want: []int{1}, 134 | }, 135 | { 136 | name: "chan with many values", 137 | args: args{ 138 | in: func(t *testing.T) <-chan int { 139 | return asChan(1, 2, 3) 140 | }, 141 | }, 142 | want: []int{1, 4, 9}, 143 | }, 144 | } 145 | for _, tt := range tests { 146 | tt := tt 147 | t.Run(tt.name, func(t *testing.T) { 148 | gotCh := sq(tt.args.in(t)) 149 | got := []int{} 150 | for val := range gotCh { 151 | got = append(got, val) 152 | } 153 | require.Equal(t, tt.want, got) 154 | }) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /session16_The_most_useful_package_sync/code/02_advancedexample/02_merge_two_channels_test.go: -------------------------------------------------------------------------------- 1 | package advancedexample 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func mergeTwo(in1 <-chan int, in2 <-chan int) <-chan int { 11 | out := make(chan int) // step 1 12 | 13 | go func() { // step 2 14 | defer close(out) 15 | 16 | for in1 != nil || in2 != nil { 17 | select { 18 | case val, ok := <-in1: 19 | if !ok { 20 | in1 = nil 21 | continue 22 | } 23 | out <- val 24 | case val, ok := <-in2: 25 | if !ok { 26 | in2 = nil 27 | continue 28 | } 29 | out <- val 30 | } 31 | } 32 | }() 33 | 34 | return out // step 3 35 | } 36 | 37 | func TestMergeTwoChannels(t *testing.T) { 38 | in1 := asChan(1, 1, 1) 39 | in2 := asChan(2, 2, 2) 40 | 41 | for val := range mergeTwo(in1, in2) { 42 | fmt.Println(val) 43 | } 44 | 45 | fmt.Println("Done.") 46 | } 47 | 48 | func Test_merge(t *testing.T) { 49 | type args struct { 50 | ch1 <-chan int 51 | ch2 <-chan int 52 | } 53 | tests := []struct { 54 | name string 55 | args func(t *testing.T) args 56 | want []int 57 | }{ 58 | { 59 | name: "empty chans", 60 | args: func(t *testing.T) args { 61 | t.Helper() 62 | return args{ 63 | ch1: asChan(), 64 | ch2: asChan(), 65 | } 66 | }, 67 | want: []int{}, 68 | }, 69 | { 70 | name: "empty first chan", 71 | args: func(t *testing.T) args { 72 | t.Helper() 73 | return args{ 74 | ch1: asChan(), 75 | ch2: asChan(1, 2, 3), 76 | } 77 | }, 78 | want: []int{1, 2, 3}, 79 | }, 80 | { 81 | name: "empty second chan", 82 | args: func(t *testing.T) args { 83 | t.Helper() 84 | return args{ 85 | ch1: asChan(1, 2, 3), 86 | ch2: asChan(), 87 | } 88 | }, 89 | want: []int{1, 2, 3}, 90 | }, 91 | } 92 | for _, tt := range tests { 93 | tt := tt 94 | t.Run(tt.name, func(t *testing.T) { 95 | args := tt.args(t) 96 | gotCh := mergeTwo(args.ch1, args.ch2) 97 | got := []int{} 98 | for val := range gotCh { 99 | got = append(got, val) 100 | } 101 | require.Equal(t, tt.want, got) 102 | }) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /session16_The_most_useful_package_sync/code/02_advancedexample/03_merge_many_channels_test.go: -------------------------------------------------------------------------------- 1 | package advancedexample 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "testing" 7 | ) 8 | 9 | func merge(cs ...<-chan int) <-chan int { 10 | var wg sync.WaitGroup 11 | out := make(chan int) // Step 1 12 | 13 | send := func(c <-chan int) { 14 | defer wg.Done() 15 | for n := range c { 16 | out <- n 17 | } 18 | } 19 | 20 | for _, c := range cs { 21 | wg.Add(1) 22 | go send(c) 23 | } 24 | 25 | go func() { 26 | defer close(out) 27 | wg.Wait() 28 | }() 29 | 30 | return out // Step 3 31 | } 32 | 33 | func TestMergeTwoChannelsExample(t *testing.T) { 34 | in1 := asChan(1, 1, 1) 35 | in2 := asChan(2, 2, 2) 36 | 37 | for val := range merge(in1, in2) { 38 | fmt.Println(val) 39 | } 40 | 41 | fmt.Println("Done.") 42 | } 43 | -------------------------------------------------------------------------------- /session16_The_most_useful_package_sync/code/go.mod: -------------------------------------------------------------------------------- 1 | module session 2 | 3 | go 1.19 4 | 5 | require github.com/stretchr/testify v1.8.1 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /session16_The_most_useful_package_sync/code/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 8 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 9 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 10 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 11 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 12 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 16 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 17 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | -------------------------------------------------------------------------------- /session17_HTTP_servers_and_routers/Package_context_HTTP_server_and_routers_in_Go.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session17_HTTP_servers_and_routers/Package_context_HTTP_server_and_routers_in_Go.pdf -------------------------------------------------------------------------------- /session17_HTTP_servers_and_routers/code/01_contextexample/01_context_test.go: -------------------------------------------------------------------------------- 1 | package contextexample 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestContextWithCancel(t *testing.T) { 10 | // gen generates integers in a separate goroutine and 11 | // sends them to the returned channel. 12 | // The callers of gen need to cancel the context once 13 | // they are done consuming generated integers not to leak 14 | // the internal goroutine started by gen. 15 | gen := func(ctx context.Context) <-chan int { 16 | dst := make(chan int) 17 | n := 1 18 | go func() { 19 | for { 20 | select { 21 | case <-ctx.Done(): 22 | return // returning not to leak the goroutine 23 | case dst <- n: 24 | n++ 25 | } 26 | } 27 | }() 28 | return dst 29 | } 30 | 31 | ctx, cancel := context.WithCancel(context.Background()) 32 | defer cancel() // cancel when we are finished consuming integers 33 | 34 | for n := range gen(ctx) { 35 | t.Log(n) 36 | if n == 5 { 37 | break 38 | } 39 | } 40 | } 41 | 42 | func TestContextWithDeadline(t *testing.T) { 43 | const shortDuration = 1 * time.Millisecond 44 | 45 | d := time.Now().Add(shortDuration) 46 | ctx, cancel := context.WithDeadline(context.Background(), d) 47 | 48 | // Even though ctx will be expired, it is good practice to call its 49 | // cancellation function in any case. Failure to do so may keep the 50 | // context and its parent alive longer than necessary. 51 | defer cancel() 52 | 53 | select { 54 | case <-time.After(1 * time.Second): 55 | t.Log("overslept") 56 | case <-ctx.Done(): 57 | t.Log(ctx.Err()) 58 | } 59 | } 60 | 61 | func TestContextWithTimeout(t *testing.T) { 62 | const shortDuration = 1 * time.Millisecond 63 | 64 | // Pass a context with a timeout to tell a blocking function that it 65 | // should abandon its work after the timeout elapses. 66 | ctx, cancel := context.WithTimeout(context.Background(), shortDuration) 67 | defer cancel() 68 | 69 | select { 70 | case <-time.After(1 * time.Second): 71 | t.Log("overslept") 72 | case <-ctx.Done(): 73 | t.Log(ctx.Err()) // prints "context deadline exceeded" 74 | } 75 | } 76 | 77 | func TestWithValue(t *testing.T) { 78 | type favContextKey string 79 | 80 | f := func(ctx context.Context, k favContextKey) { 81 | if v := ctx.Value(k); v != nil { 82 | t.Log("found value:", v) 83 | return 84 | } 85 | t.Log("key not found:", k) 86 | } 87 | 88 | k := favContextKey("language") 89 | ctx := context.WithValue(context.Background(), k, "Go") 90 | 91 | f(ctx, k) 92 | f(ctx, favContextKey("color")) 93 | } 94 | -------------------------------------------------------------------------------- /session17_HTTP_servers_and_routers/code/02_httpserverexample/01_simple/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | ) 9 | 10 | func main() { 11 | mux := http.NewServeMux() 12 | mux.HandleFunc("/", index) 13 | mux.HandleFunc("/headers", dumpHeaders) 14 | 15 | const addr = ":3000" 16 | 17 | httpServer := &http.Server{ 18 | Addr: addr, 19 | Handler: mux, // or http.DefaultServeMux, 20 | } 21 | 22 | fmt.Println("starting HTTP server on", addr) 23 | err := httpServer.ListenAndServe() 24 | // OR: 25 | // err := http.ListenAndServe(addr, mux) 26 | log.Fatal(err) 27 | } 28 | 29 | func index(w http.ResponseWriter, r *http.Request) { 30 | if r.URL.Path != "/" { 31 | http.NotFound(w, r) 32 | return 33 | } 34 | 35 | ctx, cancel := context.WithCancel(r.Context()) 36 | defer cancel() // super important! 37 | 38 | // Common code for all requests can go here... 39 | 40 | // non-blocking check: 41 | select { 42 | case <-ctx.Done(): 43 | msg := fmt.Sprintf("context is canceled because of: %q", ctx.Err()) 44 | http.Error(w, msg, http.StatusGatewayTimeout) 45 | return 46 | default: 47 | } 48 | 49 | // another way to check context: 50 | if err := ctx.Err(); err != nil { 51 | msg := fmt.Sprintf("context is canceled because of: %q", err) 52 | http.Error(w, msg, http.StatusGatewayTimeout) 53 | return 54 | } 55 | 56 | switch r.Method { 57 | case http.MethodGet: 58 | // Handle the GET request... 59 | 60 | fmt.Fprintf(w, "hello from %q method\n", r.Method) 61 | 62 | case http.MethodPost: 63 | // Handle the POST request... 64 | 65 | fmt.Fprintf(w, "hello from %q method\n", r.Method) 66 | 67 | case http.MethodOptions: 68 | w.Header().Set("Allow", "GET, POST, OPTIONS") 69 | w.WriteHeader(http.StatusNoContent) 70 | 71 | default: 72 | w.Header().Set("Allow", "GET, POST, OPTIONS") 73 | http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 74 | } 75 | } 76 | 77 | func dumpHeaders(w http.ResponseWriter, req *http.Request) { 78 | for name, headers := range req.Header { 79 | for _, h := range headers { 80 | fmt.Fprintf(w, "%v: %v\n", name, h) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /session17_HTTP_servers_and_routers/code/02_httpserverexample/01_simple/req.http: -------------------------------------------------------------------------------- 1 | ### req 1 2 | GET http://localhost:3000/ 3 | 4 | ### req 2 5 | POST http://localhost:3000/ 6 | 7 | ### dump the headers 8 | GET http://localhost:3000/headers 9 | Accept: application/json -------------------------------------------------------------------------------- /session17_HTTP_servers_and_routers/code/02_httpserverexample/02_httpclient/01_httpclient_test.go: -------------------------------------------------------------------------------- 1 | package httpclient 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | func TestHttpClient(t *testing.T) { 10 | res, err := http.Get("http://www.google.com/robots.txt") 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | defer res.Body.Close() 15 | 16 | body, err := io.ReadAll(res.Body) 17 | if res.StatusCode > 299 { 18 | t.Fatalf("Response failed with status code: %d and\nbody: %s\n", res.StatusCode, body) 19 | } 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | t.Logf("%s", body) 24 | } 25 | -------------------------------------------------------------------------------- /session17_HTTP_servers_and_routers/code/03_jsonexample/01_jsonexample_test.go: -------------------------------------------------------------------------------- 1 | package jsonexample 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func TestJSONmarshaling(t *testing.T) { 10 | bolB, _ := json.Marshal(true) 11 | t.Log(string(bolB)) 12 | 13 | intB, _ := json.Marshal(1) 14 | t.Log(string(intB)) 15 | 16 | fltB, _ := json.Marshal(2.34) 17 | t.Log(string(fltB)) 18 | 19 | strB, _ := json.Marshal("gopher") 20 | t.Log(string(strB)) 21 | 22 | slcD := []string{"apple", "peach", "pear"} 23 | slcB, _ := json.Marshal(slcD) 24 | t.Log(string(slcB)) 25 | 26 | mapD := map[string]int{"apple": 5, "lettuce": 7} 27 | mapB, _ := json.Marshal(mapD) 28 | t.Log(string(mapB)) 29 | } 30 | 31 | type response1 struct { 32 | Page int 33 | Fruits []string 34 | } 35 | 36 | type response2 struct { 37 | Page int `json:"page"` 38 | Fruits []string `json:"fruits"` 39 | } 40 | 41 | func TestJSONmarshal(t *testing.T) { 42 | res1D := &response1{ 43 | Page: 1, 44 | Fruits: []string{"apple", "peach", "pear"}} 45 | res1B, _ := json.Marshal(res1D) 46 | t.Log(string(res1B)) 47 | 48 | res2D := &response2{ 49 | Page: 1, 50 | Fruits: []string{"apple", "peach", "pear"}} 51 | res2B, _ := json.Marshal(res2D) 52 | t.Log(string(res2B)) 53 | } 54 | 55 | func TestUnknownJSONstructure(t *testing.T) { 56 | byt := []byte(`{"num":6.13,"strs":["a","b"]}`) 57 | var dat map[string]interface{} 58 | if err := json.Unmarshal(byt, &dat); err != nil { 59 | panic(err) 60 | } 61 | t.Log(dat) 62 | 63 | num := dat["num"].(float64) 64 | t.Log(num) 65 | // Accessing nested data requires a series of conversions: 66 | strs := dat["strs"].([]interface{}) // !!!!! 67 | str1 := strs[0].(string) 68 | t.Log(str1) 69 | } 70 | 71 | func TestJSONCustomDataType(t *testing.T) { 72 | // We can also decode JSON into custom data types: 73 | 74 | str := `{"page": 1, "fruits": ["apple", "peach"]}` 75 | res := response2{} 76 | json.Unmarshal([]byte(str), &res) 77 | t.Log(res) 78 | t.Log(res.Fruits[0]) 79 | } 80 | 81 | func TestStreamJSON(t *testing.T) { 82 | // We can also stream JSON encodings directly to os.Writers like os.Stdout or even HTTP response bodies: 83 | 84 | enc := json.NewEncoder(os.Stdout) 85 | d := map[string]int{"apple": 5, "lettuce": 7} 86 | enc.Encode(d) 87 | } 88 | -------------------------------------------------------------------------------- /session17_HTTP_servers_and_routers/code/04_httptestexample/01_httptesting_test.go: -------------------------------------------------------------------------------- 1 | package httptestexample 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func handlerUnderTest(w http.ResponseWriter, r *http.Request) { 13 | io.WriteString(w, "Hello World!") 14 | } 15 | 16 | func Test_01_HandlerUnderTest(t *testing.T) { 17 | // step 1: prepare incoming parameters 18 | req := httptest.NewRequest("GET", "http://example.com/foo", nil) 19 | w := httptest.NewRecorder() 20 | 21 | // step 2: invoke HTTP handler under test 22 | handlerUnderTest(w, req) 23 | 24 | // step 3: get response and analyse it 25 | resp := w.Result() 26 | body, _ := io.ReadAll(resp.Body) 27 | 28 | require.Equal(t, http.StatusOK, resp.StatusCode) 29 | t.Log(resp.StatusCode) 30 | 31 | require.Equal(t, `text/html; charset=utf-8`, resp.Header.Get("Content-Type")) 32 | t.Log(resp.Header.Get("Content-Type")) 33 | 34 | require.Equal(t, `Hello World!`, string(body)) 35 | t.Log(string(body)) 36 | } 37 | 38 | func Test_02_HTTPtestServer(t *testing.T) { 39 | ts := httptest.NewServer(http.HandlerFunc(handlerUnderTest)) 40 | defer ts.Close() 41 | 42 | // other way to configure server manually before start: 43 | // unstartedServer := httptest.NewUnstartedServer(http.HandlerFunc(handlerUnderTest)) 44 | // unstartedServer.EnableHTTP2=true 45 | // unstartedServer.Start() 46 | // defer unstartedServer.Close() 47 | 48 | res, err := http.Get(ts.URL) 49 | require.NoError(t, err) 50 | 51 | greeting, err := io.ReadAll(res.Body) 52 | require.NoError(t, err) 53 | res.Body.Close() 54 | 55 | t.Logf("%s", greeting) 56 | } 57 | -------------------------------------------------------------------------------- /session17_HTTP_servers_and_routers/code/go.mod: -------------------------------------------------------------------------------- 1 | module session 2 | 3 | go 1.19 4 | 5 | require github.com/stretchr/testify v1.8.1 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /session17_HTTP_servers_and_routers/code/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 8 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 9 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 10 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 11 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 12 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 16 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 17 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/Go_generics_concepts.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session18_Generics_in_Go/Go_generics_concepts.pdf -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/generic-constraint/generic-constraint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | val := MyInt(777) 7 | fmt.Println(GetIntWithCustomFormat[MyInt](val)) 8 | 9 | b := Box[MyInt]{} 10 | b = b 11 | 12 | v := GetIntWithCustomFormat[Point] 13 | v = v 14 | } 15 | 16 | type StringifiedInt interface { 17 | ~int | ~uint 18 | String() string // same as fmt.Stringer 19 | fmt.Stringer 20 | } 21 | 22 | type Point struct { 23 | x int 24 | y int 25 | } 26 | 27 | func GetIntWithCustomFormat[T StringifiedInt](val T) string { 28 | return val.String() 29 | } 30 | 31 | type ID int 32 | 33 | type MyInt int 34 | 35 | func (mi MyInt) String() string { 36 | return fmt.Sprintf("MyInt{%d}", mi) 37 | } 38 | 39 | // 1. func 40 | 41 | func findFirst[T StringifiedInt](s []T, el T) { 42 | el.String() 43 | } 44 | 45 | // 2. Types - struct 46 | 47 | type Box[T any] struct { 48 | val T 49 | vals []T 50 | } 51 | 52 | func (b *Box[whatever]) Hello() { 53 | b.vals = make([]whatever, 10) 54 | } 55 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/generic-constraint/go.mod: -------------------------------------------------------------------------------- 1 | module generic-constraint 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/generic-type-assertion/generictypeassertion.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | Greet("world") 7 | Greet(777) 8 | } 9 | 10 | func Greet[T any](val T) { 11 | //switch val.(type) { // <------ WILL NOT COMPILE 12 | 13 | // we need to cast to interface first: 14 | switch (any)(val).(type) { 15 | case string: 16 | fmt.Println("hello, string") 17 | case int: 18 | fmt.Println("hello, int") 19 | default: 20 | fmt.Println("not supported type") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/generic-type-assertion/go.mod: -------------------------------------------------------------------------------- 1 | module generictypeassertion 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/stack/go.mod: -------------------------------------------------------------------------------- 1 | module algo-stack 2 | 3 | go 1.19 4 | 5 | require golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/matryer/is v1.4.0 // indirect 10 | github.com/pmezard/go-difflib v1.0.0 // indirect 11 | github.com/stretchr/objx v0.4.0 // indirect 12 | github.com/stretchr/testify v1.8.0 // indirect 13 | gopkg.in/yaml.v3 v3.0.1 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/stack/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= 5 | github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 9 | github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= 10 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 11 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 12 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 13 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 14 | golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= 15 | golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= 16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 17 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 19 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 20 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/stack/node-stack.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import "golang.org/x/exp/constraints" 4 | 5 | var _ Stack[int] = &NodeStack[int]{} 6 | 7 | type node[T constraints.Ordered] struct { 8 | val T 9 | next *node[T] 10 | } 11 | 12 | // NodeStack is an implementation of Stack based on linked-list. 13 | type NodeStack[T constraints.Ordered] struct { 14 | first *node[T] 15 | } 16 | 17 | func NewNodeStack[T constraints.Ordered](data ...T) *NodeStack[T] { 18 | ns := &NodeStack[T]{} 19 | for _, el := range data { 20 | ns.Push(el) 21 | } 22 | return ns 23 | } 24 | 25 | func (ns *NodeStack[T]) Push(val T) { 26 | if ns == nil { 27 | return 28 | } 29 | newNode := &node[T]{val: val} 30 | newNode.next = ns.first 31 | ns.first = newNode 32 | } 33 | 34 | func (ns *NodeStack[T]) Pop() (T, bool) { 35 | if ns.IsEmpty() { 36 | return *new(T), false 37 | } 38 | val := ns.first.val 39 | ns.first = ns.first.next 40 | return val, true 41 | } 42 | 43 | func (ns *NodeStack[T]) Top() (T, bool) { 44 | if ns.IsEmpty() { 45 | return *new(T), false 46 | } 47 | return ns.first.val, true 48 | } 49 | 50 | func (ns *NodeStack[T]) IsEmpty() bool { 51 | return ns == nil || ns.first == nil 52 | } 53 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/stack/node-stack_test.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | "testing" 6 | ) 7 | 8 | func TestNodeStack_E2E(t *testing.T) { 9 | t.Run("operations_on_nil_stack", func(t *testing.T) { 10 | r := require.New(t) 11 | const defaultValue = 0 12 | 13 | var ns *NodeStack[int] 14 | 15 | empty := ns.IsEmpty() 16 | r.True(empty) 17 | 18 | val, ok := ns.Top() 19 | r.False(ok) 20 | r.Equal(defaultValue, val) 21 | 22 | val, ok = ns.Pop() 23 | r.False(ok) 24 | r.Equal(defaultValue, val) 25 | 26 | ns.Push(777) // no effect, because the `ns` is nil 27 | val, ok = ns.Pop() 28 | r.False(ok) 29 | r.Equal(defaultValue, val) 30 | }) 31 | 32 | t.Run("popping_from_empty_stack", func(t *testing.T) { 33 | r := require.New(t) 34 | 35 | const defaultValue = 0 36 | 37 | ns := NewNodeStack[int]() 38 | 39 | empty := ns.IsEmpty() 40 | r.True(empty) 41 | 42 | val, ok := ns.Pop() 43 | r.False(ok) 44 | r.Equal(defaultValue, val) 45 | 46 | val, ok = ns.Pop() 47 | r.False(ok) 48 | r.Equal(defaultValue, val) 49 | }) 50 | 51 | t.Run("popping_from_non-empty_stack", func(t *testing.T) { 52 | r := require.New(t) 53 | 54 | const defaultValue = 0 55 | 56 | ns := NewNodeStack[int](1, 2, 3) 57 | 58 | empty := ns.IsEmpty() 59 | r.False(empty) 60 | 61 | val, ok := ns.Pop() 62 | r.True(ok) 63 | r.Equal(3, val) 64 | 65 | val, ok = ns.Pop() 66 | r.True(ok) 67 | r.Equal(2, val) 68 | 69 | ns.Push(777) 70 | val, ok = ns.Top() 71 | r.True(ok) 72 | r.Equal(777, val) 73 | 74 | val, ok = ns.Pop() 75 | r.True(ok) 76 | r.Equal(777, val) 77 | 78 | val, ok = ns.Pop() 79 | r.True(ok) 80 | r.Equal(1, val) 81 | 82 | val, ok = ns.Pop() 83 | r.False(ok) 84 | r.Equal(defaultValue, val) 85 | 86 | empty = ns.IsEmpty() 87 | r.True(empty) 88 | }) 89 | } 90 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/stack/slice-stack.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import "golang.org/x/exp/constraints" 4 | 5 | var _ Stack[int] = &SliceStack[int]{} 6 | 7 | var _ Stack[string] = &SliceStack[string]{} 8 | 9 | // SliceStack is an implementation of Stack based on slices. 10 | type SliceStack[T constraints.Ordered] struct { 11 | data []T 12 | } 13 | 14 | // NewSliceStack constructs new Stack based internally on slices. 15 | func NewSliceStack[T constraints.Ordered](data ...T) *SliceStack[T] { 16 | return &SliceStack[T]{data: data} 17 | } 18 | 19 | func (ss *SliceStack[T]) IsEmpty() bool { 20 | if ss == nil { 21 | return true 22 | } 23 | return len(ss.data) == 0 24 | } 25 | 26 | func (ss *SliceStack[T]) Push(elem T) { 27 | ss.data = append(ss.data, elem) 28 | } 29 | 30 | func (ss *SliceStack[T]) Pop() (T, bool) { 31 | if len(ss.data) == 0 { 32 | return *new(T), false 33 | } 34 | res := ss.data[len(ss.data)-1] 35 | ss.data = ss.data[:len(ss.data)-1] 36 | return res, true 37 | } 38 | 39 | func (ss *SliceStack[T]) Top() (T, bool) { 40 | if len(ss.data) == 0 { 41 | return *new(T), false 42 | } 43 | return ss.data[len(ss.data)-1], true 44 | } 45 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/stack/slice-stack_test.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import ( 4 | is2 "github.com/matryer/is" 5 | "testing" 6 | ) 7 | 8 | func TestSliceStack_End2End(t *testing.T) { 9 | t.Run("popping_from_empty_stack", func(t *testing.T) { 10 | is := is2.New(t) 11 | 12 | st := NewSliceStack[int]() 13 | got, gotOk := st.Pop() 14 | is.True(!gotOk) 15 | is.Equal(0, got) 16 | }) 17 | t.Run("popping_from_non-empty_stack", func(t *testing.T) { 18 | is := is2.New(t) 19 | 20 | const four = 4 21 | st := NewSliceStack(1, 2, 3) 22 | st.Push(four) 23 | gotFour, ok := st.Top() 24 | is.True(ok) 25 | is.Equal(four, gotFour) 26 | 27 | gotFour, ok = st.Pop() 28 | is.True(ok) 29 | is.Equal(four, gotFour) 30 | 31 | gotThree, ok := st.Pop() 32 | is.True(ok) 33 | is.Equal(3, gotThree) 34 | 35 | gotTwo, ok := st.Pop() 36 | is.True(ok) 37 | is.Equal(2, gotTwo) 38 | }) 39 | } 40 | 41 | func TestNewSliceStack(t *testing.T) { 42 | type args struct { 43 | data []int 44 | } 45 | tests := []struct { 46 | name string 47 | args args 48 | want *SliceStack[int] 49 | }{ 50 | { 51 | name: "no_params", 52 | args: args{ 53 | data: nil, 54 | }, 55 | want: &SliceStack[int]{}, 56 | }, 57 | { 58 | name: "some_params", 59 | args: args{ 60 | data: []int{1, 2, 3}, 61 | }, 62 | want: &SliceStack[int]{ 63 | data: []int{1, 2, 3}, 64 | }, 65 | }, 66 | } 67 | for _, tt := range tests { 68 | tt := tt 69 | t.Run(tt.name, func(t *testing.T) { 70 | is := is2.New(t) 71 | got := NewSliceStack(tt.args.data...) 72 | is.Equal(*tt.want, *got) 73 | }) 74 | } 75 | } 76 | 77 | func TestSliceStack_IsEmpty(t *testing.T) { 78 | type fields struct { 79 | data []int 80 | } 81 | tests := []struct { 82 | name string 83 | fields fields 84 | want bool 85 | }{ 86 | { 87 | name: "nil stack", 88 | fields: fields{ 89 | data: nil, 90 | }, 91 | want: true, 92 | }, 93 | { 94 | name: "stack_without_elements", 95 | fields: fields{ 96 | data: []int{}, 97 | }, 98 | want: true, 99 | }, 100 | { 101 | name: "stack_with_elements", 102 | fields: fields{ 103 | data: []int{1, 2, 3}, 104 | }, 105 | want: false, 106 | }, 107 | } 108 | for _, tt := range tests { 109 | tt := tt 110 | t.Run(tt.name, func(t *testing.T) { 111 | is := is2.New(t) 112 | 113 | ss := NewSliceStack(tt.fields.data...) 114 | got := ss.IsEmpty() 115 | is.Equal(tt.want, got) 116 | }) 117 | } 118 | } 119 | 120 | func TestSliceStack_Pop(t *testing.T) { 121 | type fields struct { 122 | data []int 123 | } 124 | tests := []struct { 125 | name string 126 | fields fields 127 | want int 128 | wantOK bool 129 | }{ 130 | { 131 | name: "no_initial_values", 132 | fields: fields{ 133 | data: nil, 134 | }, 135 | want: 0, 136 | wantOK: false, 137 | }, 138 | { 139 | name: "one_initial_value", 140 | fields: fields{ 141 | data: []int{1}, 142 | }, 143 | want: 1, 144 | wantOK: true, 145 | }, 146 | { 147 | name: "many_initial_values", 148 | fields: fields{ 149 | data: []int{1, 2, 3}, 150 | }, 151 | want: 3, 152 | wantOK: true, 153 | }, 154 | } 155 | for _, tt := range tests { 156 | tt := tt 157 | t.Run(tt.name, func(t *testing.T) { 158 | is := is2.New(t) 159 | 160 | ss := NewSliceStack(tt.fields.data...) 161 | got, gotOk := ss.Pop() 162 | is.Equal(tt.want, got) 163 | is.Equal(tt.wantOK, gotOk) 164 | }) 165 | } 166 | } 167 | 168 | func TestSliceStack_Push(t *testing.T) { 169 | type fields struct { 170 | data []int 171 | } 172 | type args struct { 173 | val int 174 | } 175 | tests := []struct { 176 | name string 177 | fields fields 178 | args args 179 | want []int 180 | }{ 181 | { 182 | name: "pushing_new_value_to_nil_stack", 183 | fields: fields{ 184 | data: nil, 185 | }, 186 | args: args{ 187 | val: 777, 188 | }, 189 | want: []int{777}, 190 | }, 191 | { 192 | name: "pushing_new_value_to_empty_stack", 193 | fields: fields{ 194 | data: []int{}, 195 | }, 196 | args: args{ 197 | val: 777, 198 | }, 199 | want: []int{777}, 200 | }, 201 | { 202 | name: "pushing_new_value_to_existing_ones", 203 | fields: fields{ 204 | data: []int{1, 2, 3}, 205 | }, 206 | args: args{ 207 | val: 777, 208 | }, 209 | want: []int{1, 2, 3, 777}, 210 | }, 211 | } 212 | for _, tt := range tests { 213 | tt := tt 214 | t.Run(tt.name, func(t *testing.T) { 215 | is := is2.New(t) 216 | ss := NewSliceStack(tt.fields.data...) 217 | ss.Push(tt.args.val) 218 | is.Equal(tt.want, ss.data) 219 | }) 220 | } 221 | } 222 | 223 | func TestSliceStack_Top(t *testing.T) { 224 | type fields struct { 225 | data []int 226 | } 227 | tests := []struct { 228 | name string 229 | fields fields 230 | want int 231 | wantOK bool 232 | }{ 233 | { 234 | name: "nil_data", 235 | fields: fields{ 236 | data: nil, 237 | }, 238 | want: 0, 239 | wantOK: false, 240 | }, 241 | { 242 | name: "one_element", 243 | fields: fields{ 244 | data: []int{1}, 245 | }, 246 | want: 1, 247 | wantOK: true, 248 | }, 249 | { 250 | name: "couple_elements", 251 | fields: fields{data: []int{1, 2, 3}}, 252 | want: 3, 253 | wantOK: true, 254 | }, 255 | } 256 | for _, tt := range tests { 257 | tt := tt 258 | t.Run(tt.name, func(t *testing.T) { 259 | is := is2.New(t) 260 | ss := NewSliceStack(tt.fields.data...) 261 | got, gotOK := ss.Top() 262 | is.Equal(tt.want, got) 263 | is.Equal(tt.wantOK, gotOK) 264 | }) 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/code/stack/stack-interface.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import "golang.org/x/exp/constraints" 4 | 5 | // Stack is a generic stack contract. 6 | type Stack[T constraints.Ordered] interface { 7 | // Push adds value onto the stack. 8 | Push(val T) 9 | // Pop populates value from the stack (and removes it). 10 | Pop() (T, bool) 11 | // Top gets the topmost value without removing it from the Stack. 12 | Top() (T, bool) 13 | // IsEmpty checks if the stack is empty or not. 14 | IsEmpty() bool 15 | } 16 | -------------------------------------------------------------------------------- /session18_Generics_in_Go/session18_Generics_in_Go.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session18_Generics_in_Go/session18_Generics_in_Go.pdf -------------------------------------------------------------------------------- /session19_Possible_ways_to_design_flexible_APIs_in_Go/code/graterm-example/go.mod: -------------------------------------------------------------------------------- 1 | module graterm-example 2 | 3 | go 1.19 4 | 5 | require github.com/skovtunenko/graterm v1.1.0 6 | -------------------------------------------------------------------------------- /session19_Possible_ways_to_design_flexible_APIs_in_Go/code/graterm-example/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 5 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 6 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/skovtunenko/graterm v1.1.0 h1:HBriftqKcrKraetGJ08UOHYE/5Pfmo+59tLqOnGx0Wg= 10 | github.com/skovtunenko/graterm v1.1.0/go.mod h1:cSyp3pV5Ll4fusmJLVBNb49F3y2khMNrNn9x/xgu1hE= 11 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 12 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 13 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 15 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 16 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 17 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 18 | go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= 19 | go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 20 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 21 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 22 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 23 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 24 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 25 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 26 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 27 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 28 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 29 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 30 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 31 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 32 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 33 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 34 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 36 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 37 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 38 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 39 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 40 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 41 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 42 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 43 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 44 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 45 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 46 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 47 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 48 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 49 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 50 | -------------------------------------------------------------------------------- /session19_Possible_ways_to_design_flexible_APIs_in_Go/code/graterm-example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "time" 10 | 11 | "github.com/skovtunenko/graterm" 12 | ) 13 | 14 | // Instructions: 15 | // - run the application, reach out to http://localhost:8080/ 16 | // - terminate the application (CTRL+C) 17 | // - investigate the log output 18 | 19 | const hostPort = ":8080" 20 | 21 | const ( 22 | globalTerminationTimeout = 40 * time.Second 23 | 24 | httpServerTerminationTimeout = 5 * time.Second 25 | httpServerControllerSleepTime = 15 * time.Second 26 | 27 | messagingTerminationTimeout = 1 * time.Second 28 | 29 | fastDBTerminationTimeout = 1 * time.Second 30 | 31 | slowDBTerminationTimeout = 3 * time.Second 32 | slowDBSleepTime = 5 * time.Second // Termination of SlowDB can't be finished in time 33 | ) 34 | 35 | const ( 36 | HTTPServerOrder graterm.Order = 0 37 | MessagingOrder graterm.Order = 1 38 | FastDBOrder graterm.Order = 2 39 | SlowDBOrder graterm.Order = 2 40 | ) 41 | 42 | func main() { 43 | logger := log.Default() 44 | 45 | logger.Println("Application started...") 46 | 47 | terminator, appCtx := graterm.NewWithSignals(context.Background()) 48 | terminator.SetLogger(logger) 49 | 50 | // Wire application components: 51 | slowDB := NewSlowDB(terminator, logger) 52 | fastDB := NewFastDB(terminator, logger) 53 | messaging := NewMessaging(terminator, logger) 54 | srv := NewServer(terminator, logger) 55 | app := NewApplication(logger, srv, messaging, slowDB, fastDB) 56 | app.Run() 57 | 58 | if err := terminator.Wait(appCtx, globalTerminationTimeout); err != nil { 59 | logger.Printf("graceful termination period is timed out: %+v", err) 60 | } 61 | logger.Println("Application ended.") 62 | } 63 | 64 | type Application struct { 65 | Log *log.Logger 66 | Server *Server 67 | Messaging *Messaging 68 | SlowDB *SlowDB 69 | FastDB *FastDB 70 | } 71 | 72 | func NewApplication(log *log.Logger, server *Server, messaging *Messaging, slowDB *SlowDB, fastDB *FastDB) *Application { 73 | return &Application{Log: log, Server: server, Messaging: messaging, SlowDB: slowDB, FastDB: fastDB} 74 | } 75 | 76 | func (a *Application) Run() { 77 | a.SlowDB.Init() 78 | a.FastDB.Init() 79 | a.Messaging.Init() 80 | a.Server.Init() 81 | } 82 | 83 | type Server struct { 84 | logger *log.Logger 85 | terminator *graterm.Terminator 86 | } 87 | 88 | func NewServer(terminator *graterm.Terminator, logger *log.Logger) *Server { 89 | return &Server{terminator: terminator, logger: logger} 90 | } 91 | 92 | func (s *Server) Init() { 93 | defer s.logger.Println("HTTP Server initialized") 94 | 95 | httpServer := &http.Server{ 96 | Addr: hostPort, 97 | Handler: http.DefaultServeMux, 98 | } 99 | 100 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 101 | s.logger.Println("Got request:", r.URL) 102 | ctx := r.Context() 103 | select { 104 | case <-time.After(httpServerControllerSleepTime): // Simulate long-running in-flight HTTP request processing 105 | s.logger.Println("HTTP Server controller finished") 106 | case <-ctx.Done(): 107 | s.logger.Printf("HTTP Server controller interrupted because of: %+v\n", ctx.Err()) 108 | } 109 | fmt.Fprintf(w, "hello, world!\nSlept for %v seconds\n", httpServerControllerSleepTime) 110 | }) 111 | 112 | go func() { 113 | if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { 114 | log.Printf("terminated HTTP Server: %+v", err) 115 | } 116 | }() 117 | 118 | s.terminator.WithOrder(HTTPServerOrder). 119 | WithName("HTTPServer"). 120 | Register(httpServerTerminationTimeout, func(ctx context.Context) { 121 | s.logger.Println("terminating HTTP Server component...") 122 | defer s.logger.Println("...HTTP Server component terminated") 123 | 124 | if err := httpServer.Shutdown(ctx); err != nil { // stop terminating HTTP Server if the time for that is over. 125 | s.logger.Printf("shutdown HTTP Server: %+v", err) 126 | } 127 | }) 128 | 129 | s.logger.Printf("HTTP Server started on: %q\n", hostPort) 130 | } 131 | 132 | type Messaging struct { 133 | logger *log.Logger 134 | terminator *graterm.Terminator 135 | } 136 | 137 | func NewMessaging(terminator *graterm.Terminator, logger *log.Logger) *Messaging { 138 | return &Messaging{terminator: terminator, logger: logger} 139 | } 140 | 141 | func (m *Messaging) Init() { 142 | defer m.logger.Println("Messaging initialized") 143 | m.terminator.WithOrder(MessagingOrder). 144 | WithName("Messaging"). 145 | Register(messagingTerminationTimeout, func(ctx context.Context) { 146 | m.logger.Println("terminating Messaging component...") 147 | defer m.logger.Println("...Messaging component terminated") 148 | }) 149 | } 150 | 151 | type FastDB struct { 152 | logger *log.Logger 153 | terminator *graterm.Terminator 154 | } 155 | 156 | func NewFastDB(terminator *graterm.Terminator, logger *log.Logger) *FastDB { 157 | return &FastDB{terminator: terminator, logger: logger} 158 | } 159 | 160 | func (d *FastDB) Init() { 161 | defer d.logger.Println("FastDB initialized") 162 | d.terminator.WithOrder(FastDBOrder). 163 | WithName("FastDB"). 164 | Register(fastDBTerminationTimeout, func(ctx context.Context) { 165 | d.logger.Println("terminating FastDB component...") 166 | defer d.logger.Println("...FastDB component terminated") 167 | }) 168 | } 169 | 170 | type SlowDB struct { 171 | logger *log.Logger 172 | terminator *graterm.Terminator 173 | } 174 | 175 | func NewSlowDB(terminator *graterm.Terminator, logger *log.Logger) *SlowDB { 176 | return &SlowDB{terminator: terminator, logger: logger} 177 | } 178 | 179 | func (d *SlowDB) Init() { 180 | defer d.logger.Println("SlowDB initialized") 181 | d.terminator.WithOrder(SlowDBOrder). 182 | WithName("SlowDB"). 183 | Register(slowDBTerminationTimeout, func(ctx context.Context) { 184 | d.logger.Println("terminating SlowDB component...") 185 | defer d.logger.Println("...SlowDB component terminated") 186 | select { 187 | case <-time.After(slowDBSleepTime): 188 | d.logger.Println("SlowDB cleanup finished") 189 | case <-ctx.Done(): 190 | d.logger.Printf("SlowDB termination interrupted because of: %+v\n", ctx.Err()) 191 | } 192 | }) 193 | } 194 | -------------------------------------------------------------------------------- /session19_Possible_ways_to_design_flexible_APIs_in_Go/code/graterm-example/rest.http: -------------------------------------------------------------------------------- 1 | ### GET request 2 | GET http://localhost:8080/some-request 3 | Accept: application/json -------------------------------------------------------------------------------- /session19_Possible_ways_to_design_flexible_APIs_in_Go/session19_Possible_ways_to_design_flexible_APIs_in_Go.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skovtunenko/exadel-golang-course-2022/dd079e41f7c106b0772585031cacbecceb917cd6/session19_Possible_ways_to_design_flexible_APIs_in_Go/session19_Possible_ways_to_design_flexible_APIs_in_Go.pdf --------------------------------------------------------------------------------