├── src ├── k8s │ ├── .gitignore │ ├── demo-flux │ │ ├── clusters │ │ │ ├── production │ │ │ │ └── .gitkeep │ │ │ ├── staging │ │ │ │ ├── flux-system │ │ │ │ │ ├── kustomization.yaml │ │ │ │ │ └── gotk-sync.yaml │ │ │ │ └── apps.yaml │ │ │ └── development │ │ │ │ ├── flux-system │ │ │ │ ├── kustomization.yaml │ │ │ │ └── gotk-sync.yaml │ │ │ │ ├── sources.yaml │ │ │ │ ├── apps.yaml │ │ │ │ └── infrastructure.yaml │ │ ├── apps │ │ │ ├── base │ │ │ │ ├── calculator │ │ │ │ │ ├── namespace.yaml │ │ │ │ │ ├── configMap.yaml │ │ │ │ │ ├── service.yaml │ │ │ │ │ ├── kustomization.yaml │ │ │ │ │ ├── virtualService.yaml │ │ │ │ │ ├── canary.yaml │ │ │ │ │ └── deployment.yaml │ │ │ │ ├── redis-cache │ │ │ │ │ ├── namespace.yaml │ │ │ │ │ ├── service.yaml │ │ │ │ │ ├── kustomization.yaml │ │ │ │ │ └── deployment.yaml │ │ │ │ └── postgresql-core │ │ │ │ │ ├── helmRepository.yaml │ │ │ │ │ ├── kustomization.yaml │ │ │ │ │ └── helmRelease.yaml │ │ │ └── overlays │ │ │ │ ├── production │ │ │ │ └── calculator │ │ │ │ │ ├── kustomization.yaml │ │ │ │ │ └── configMap.yaml │ │ │ │ └── staging │ │ │ │ └── calculator │ │ │ │ ├── kustomization.yaml │ │ │ │ └── configMap.yaml │ │ └── infrastructure │ │ │ └── base │ │ │ ├── flagger │ │ │ ├── namespace.yaml │ │ │ ├── helmRepository.yaml │ │ │ ├── kustomization.yaml │ │ │ └── helmRelease.yaml │ │ │ ├── datadog │ │ │ ├── namespace.yaml │ │ │ ├── helmRepository.yaml │ │ │ ├── kustomization.yaml │ │ │ └── helmRelease.yaml │ │ │ ├── gloo-system │ │ │ ├── namespace.yaml │ │ │ ├── deployment.yaml │ │ │ ├── helmRepository.yaml │ │ │ ├── gateway.yaml │ │ │ ├── upstreams.yaml │ │ │ ├── kustomization.yaml │ │ │ ├── virtualServices.yaml │ │ │ ├── helmRelease.yaml │ │ │ └── settings.yaml │ │ │ ├── centralized-log │ │ │ ├── namespace.yaml │ │ │ ├── kibana.yaml │ │ │ ├── kustomization.yaml │ │ │ ├── elasticsearch.yaml │ │ │ ├── logstash.yaml │ │ │ └── beat.yaml │ │ │ └── elastic-system │ │ │ └── kustomization.yaml │ ├── demo-kustomization │ │ ├── overlays │ │ │ ├── staging │ │ │ │ ├── redis-cache │ │ │ │ │ └── kustomization.yaml │ │ │ │ └── calculator │ │ │ │ │ ├── kustomization.yaml │ │ │ │ │ └── configMap.yaml │ │ │ └── production │ │ │ │ ├── calculator │ │ │ │ ├── configMap.yaml │ │ │ │ ├── deployment.yaml │ │ │ │ └── kustomization.yaml │ │ │ │ └── redis-cache │ │ │ │ ├── kustomization.yaml │ │ │ │ └── values.yaml │ │ └── base │ │ │ ├── calculator │ │ │ ├── configMap.yaml │ │ │ ├── kustomization.yaml │ │ │ ├── service.yaml │ │ │ └── deployment.yaml │ │ │ ├── postgresql-core │ │ │ ├── helmRepository.yaml │ │ │ ├── kustomization.yaml │ │ │ └── helmRelease.yaml │ │ │ └── redis-cache │ │ │ ├── kustomization.yaml │ │ │ └── values.yaml │ └── demo-helm │ │ └── charts │ │ └── calculator │ │ ├── values.yaml │ │ ├── templates │ │ ├── configmap.yaml │ │ ├── service.yaml │ │ └── deployment.yaml │ │ ├── .helmignore │ │ └── Chart.yaml ├── etc │ ├── demo-kong-plugin │ │ ├── pkg │ │ │ └── .gitkeep │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── internal │ │ │ ├── domain │ │ │ │ ├── entity │ │ │ │ │ └── keychecker │ │ │ │ │ │ ├── validate_key_input.go │ │ │ │ │ │ └── errors.go │ │ │ │ ├── repo │ │ │ │ │ └── token_repository.go │ │ │ │ └── usecase │ │ │ │ │ └── keychecker │ │ │ │ │ ├── get_token_use_case.go │ │ │ │ │ └── validate_key_use_case.go │ │ │ ├── di │ │ │ │ ├── app_module.go │ │ │ │ ├── repo_module.go │ │ │ │ └── usecase_module.go │ │ │ ├── data │ │ │ │ └── token_repository.go │ │ │ └── delivery │ │ │ │ └── kong │ │ │ │ └── key_checker.go │ │ ├── Dockerfile │ │ ├── scripts │ │ │ └── start-development.sh │ │ ├── config.yml │ │ ├── cmd │ │ │ └── kong │ │ │ │ └── keychecker │ │ │ │ └── main.go │ │ ├── go.mod │ │ ├── README.md │ │ ├── docker-compose.yml │ │ └── go.sum │ ├── demo-nats │ │ ├── .gitignore │ │ ├── go.mod │ │ ├── pubsub │ │ │ ├── publisher │ │ │ │ └── main.go │ │ │ └── consumer │ │ │ │ └── main.go │ │ ├── go.sum │ │ └── docker-compose.yaml │ ├── demo-oauth2 │ │ ├── .gitignore │ │ ├── internal │ │ │ ├── auth │ │ │ │ ├── types.go │ │ │ │ ├── clients.go │ │ │ │ ├── repositories.go │ │ │ │ └── handlers.go │ │ │ └── pokedex │ │ │ │ ├── clients.go │ │ │ │ └── handlers.go │ │ ├── docker-compose.yaml │ │ ├── go.mod │ │ └── cmd │ │ │ ├── pokedex │ │ │ └── main.go │ │ │ └── auth │ │ │ └── main.go │ ├── demo-event-sourcing │ │ ├── .gitignore │ │ ├── go.mod │ │ ├── go.sum │ │ ├── internal │ │ │ ├── domain │ │ │ │ ├── communication │ │ │ │ │ └── service.go │ │ │ │ ├── task │ │ │ │ │ ├── repository.go │ │ │ │ │ ├── events.go │ │ │ │ │ └── task.go │ │ │ │ ├── types.go │ │ │ │ └── report │ │ │ │ │ ├── repository.go │ │ │ │ │ └── report.go │ │ │ ├── infrastructure │ │ │ │ ├── service │ │ │ │ │ └── service.go │ │ │ │ └── repository │ │ │ │ │ └── task_repository.go │ │ │ └── application │ │ │ │ ├── projection_handler.go │ │ │ │ └── command_handler.go │ │ └── cmd │ │ │ └── todo │ │ │ └── main.go │ ├── XAAM │ │ ├── .env │ │ ├── .test.env │ │ ├── internal │ │ │ ├── domain │ │ │ │ ├── entity │ │ │ │ │ ├── resource.go │ │ │ │ │ ├── action.go │ │ │ │ │ ├── business │ │ │ │ │ │ └── get_one_response.go │ │ │ │ │ ├── auth │ │ │ │ │ │ ├── find_resources_by_compliance_request.go │ │ │ │ │ │ ├── check_response.go │ │ │ │ │ │ └── check_request.go │ │ │ │ │ ├── target_resource_action.go │ │ │ │ │ └── errors.go │ │ │ │ ├── repo │ │ │ │ │ ├── business_repo.go │ │ │ │ │ ├── resource_action_repo.go │ │ │ │ │ └── resource_repo.go │ │ │ │ └── usecase │ │ │ │ │ ├── business │ │ │ │ │ └── find_one_by_business_id_use_case.go │ │ │ │ │ └── auth │ │ │ │ │ ├── find_resources_by_compliance_use_case.go │ │ │ │ │ └── find_resources_by_compliance_use_case_test.go │ │ │ ├── delivery │ │ │ │ ├── ping_handler.go │ │ │ │ ├── utils.go │ │ │ │ └── auth_check_handler.go │ │ │ ├── source │ │ │ │ ├── business_repo.go │ │ │ │ ├── resource_action_repo.go │ │ │ │ └── resource_repo.go │ │ │ ├── di │ │ │ │ └── di.go │ │ │ └── mock │ │ │ │ └── domain │ │ │ │ └── repo │ │ │ │ ├── business_repo.go │ │ │ │ ├── policy_repo.go │ │ │ │ └── resource_repo.go │ │ ├── pkg │ │ │ └── slices │ │ │ │ └── slices.go │ │ ├── docker-compose.yaml │ │ ├── e2e │ │ │ ├── ping_test.go │ │ │ └── api_v1_authotrisation_check_test.go │ │ ├── cmd │ │ │ └── http │ │ │ │ └── main.go │ │ ├── go.mod │ │ └── files │ │ │ └── sql │ │ │ ├── 0001.sql │ │ │ └── 0002.sql │ ├── demo-unit-test │ │ ├── internal │ │ │ ├── article │ │ │ │ ├── delivery.go │ │ │ │ ├── repo.go │ │ │ │ ├── entity.go │ │ │ │ └── usecase.go │ │ │ └── user │ │ │ │ ├── usecase.go │ │ │ │ ├── entity.go │ │ │ │ ├── delivery.go │ │ │ │ ├── repo.go │ │ │ │ ├── usecase_mock_test.go │ │ │ │ ├── repo_test.go │ │ │ │ ├── usecase_test.go │ │ │ │ ├── delivery_test.go │ │ │ │ └── repo_mock_test.go │ │ ├── pkg │ │ │ └── calc │ │ │ │ ├── calc.go │ │ │ │ └── calc_test.go │ │ ├── go.mod │ │ ├── usecase_test.go │ │ └── go.sum │ ├── clean-arch-ddd-cqrs-es │ │ ├── go.mod │ │ ├── internal │ │ │ ├── infrastructure │ │ │ │ └── times │ │ │ │ │ └── datetime.go │ │ │ └── domain │ │ │ │ ├── user │ │ │ │ ├── error_code.go │ │ │ │ └── user.go │ │ │ │ ├── error.go │ │ │ │ └── task │ │ │ │ ├── error_code.go │ │ │ │ └── task.go │ │ └── pkg │ │ │ └── errr │ │ │ └── errr.go │ ├── demo-redlock │ │ ├── go.mod │ │ ├── docker-compose.yml │ │ ├── example │ │ │ └── main.go │ │ └── redlock.go │ ├── kafka │ │ ├── go.mod │ │ ├── basic │ │ │ ├── publisher │ │ │ │ └── main.go │ │ │ └── consumer │ │ │ │ └── main.go │ │ ├── docker-compose.yml │ │ └── go.sum │ └── go-calculator │ │ ├── Dockerfile │ │ ├── go.mod │ │ └── cmd.go ├── concurrency │ ├── workerpool │ │ ├── types.go │ │ ├── example │ │ │ └── main.go │ │ └── worker_pool.go │ ├── semaphore │ │ ├── semaphore.go │ │ └── example │ │ │ └── main.go │ └── pipeline │ │ ├── example │ │ ├── e01 │ │ │ └── main.go │ │ ├── util.go │ │ ├── e02 │ │ │ └── main.go │ │ ├── e00 │ │ │ └── main.go │ │ ├── e03 │ │ │ └── main.go │ │ └── e04 │ │ │ └── main.go │ │ ├── pipeline_test.go │ │ ├── pipeline.go │ │ └── pipeline_bench_test.go ├── datastructure │ ├── types │ │ ├── generic.go │ │ └── func.go │ ├── stack │ │ └── stack.go │ ├── binarytree │ │ └── binarytree.go │ ├── hashmap │ │ └── hashmap.go │ ├── linkedlist │ │ ├── example │ │ │ └── main.go │ │ ├── linkedlist.go │ │ └── linkedlist_test.go │ └── queue │ │ ├── queue.go │ │ └── queue_test.go └── distributed │ └── semaphore │ ├── go.mod │ ├── go.sum │ ├── example │ └── main.go │ └── semaphore.go ├── .gitignore └── README.md /src/k8s/.gitignore: -------------------------------------------------------------------------------- 1 | charts 2 | .env -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/pkg/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/etc/demo-nats/.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ -------------------------------------------------------------------------------- /src/k8s/demo-flux/clusters/production/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | vendor -------------------------------------------------------------------------------- /src/etc/demo-oauth2/.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | volumes/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/playground.go 2 | .DS_Store 3 | .vscode 4 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | tmp/ -------------------------------------------------------------------------------- /src/etc/XAAM/.env: -------------------------------------------------------------------------------- 1 | DB_NAME=xaam 2 | DB_USER=root 3 | DB_PASSWORD=root -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/article/delivery.go: -------------------------------------------------------------------------------- 1 | package article 2 | -------------------------------------------------------------------------------- /src/etc/XAAM/.test.env: -------------------------------------------------------------------------------- 1 | DB_NAME=xaam_test 2 | DB_USER=root 3 | DB_PASSWORD=root -------------------------------------------------------------------------------- /src/etc/clean-arch-ddd-cqrs-es/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/clean-arch-ddd-cqrs-es 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/calculator/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: calculator -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/redis-cache/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: redis-cache -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/flagger/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: flagger -------------------------------------------------------------------------------- /src/concurrency/workerpool/types.go: -------------------------------------------------------------------------------- 1 | package workerpool 2 | 3 | // T is a type alias to accept any type. 4 | type T = interface{} 5 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/datadog/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: calculator -------------------------------------------------------------------------------- /src/datastructure/types/generic.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type T interface{} 4 | 5 | type U interface{} 6 | 7 | type V interface{} 8 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/entity/resource.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type Resource struct { 4 | ID int64 5 | Name string 6 | } 7 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/demo-es 2 | 3 | go 1.20 4 | 5 | require github.com/google/uuid v1.6.0 6 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/gloo-system/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: gloo-system -------------------------------------------------------------------------------- /src/etc/demo-redlock/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/demo-redlock 2 | 3 | go 1.15 4 | 5 | require github.com/go-redis/redis/v8 v8.9.0 6 | -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/overlays/staging/redis-cache/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../../base/redis-cache 3 | namePrefix: staging- -------------------------------------------------------------------------------- /src/etc/kafka/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/go-exercise/src/etc/kafka 2 | 3 | go 1.15 4 | 5 | require github.com/segmentio/kafka-go v0.4.25 6 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/centralized-log/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: centralized-log -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/base/calculator/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: calculator-configmap 5 | data: 6 | max_number: "100" 7 | -------------------------------------------------------------------------------- /src/etc/clean-arch-ddd-cqrs-es/internal/infrastructure/times/datetime.go: -------------------------------------------------------------------------------- 1 | package times 2 | 3 | import "time" 4 | 5 | type TimeProvider interface { 6 | Now() time.Time 7 | } 8 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/overlays/production/calculator/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../../base/calculator 3 | namePrefix: staging- 4 | patches: 5 | - path: configMap.yaml -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/overlays/staging/calculator/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../../base/calculator 3 | namePrefix: staging- 4 | patches: 5 | - path: configMap.yaml -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/overlays/staging/calculator/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../../base/calculator 3 | namePrefix: staging- 4 | patches: 5 | - path: configMap.yaml -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | all: key-checker 4 | 5 | key-checker: cmd/kong/keychecker/main.go 6 | go build -o bin/key-checker cmd/kong/keychecker/main.go 7 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/pkg/calc/calc.go: -------------------------------------------------------------------------------- 1 | package calc 2 | 3 | func Factorial(n uint) uint { 4 | if n <= 1 { 5 | return 1 6 | } 7 | 8 | return n * Factorial(n-1) 9 | } 10 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/overlays/production/calculator/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: calculator-configmap 5 | data: 6 | max_number: "75" 7 | -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/overlays/staging/calculator/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: calculator-configmap 5 | data: 6 | max_number: "75" 7 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/internal/domain/entity/keychecker/validate_key_input.go: -------------------------------------------------------------------------------- 1 | package keychecker 2 | 3 | type ValidateKeyInput struct { 4 | GivenKey string 5 | ValidKey string 6 | } 7 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/elastic-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - crds.yaml 5 | - operator.yaml -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/gloo-system/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: gateway-proxy 5 | namespace: gloo-system 6 | spec: {} 7 | -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/overlays/production/calculator/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: calculator-configmap 5 | data: 6 | max_number: "666" 7 | -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/overlays/production/calculator/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: calculator-deployment 5 | spec: 6 | replicas: 5 -------------------------------------------------------------------------------- /src/datastructure/stack/stack.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import "github.com/syafdia/go-exercise/src/datastructure/types" 4 | 5 | type Stack interface { 6 | Pop() types.T 7 | Push(v types.T) 8 | } 9 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/calculator/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: calculator-configmap 5 | namespace: calculator 6 | data: 7 | max_number: "100" 8 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/clusters/staging/flux-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - gotk-components.yaml 5 | - gotk-sync.yaml 6 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 2 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 3 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/clusters/development/flux-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - gotk-components.yaml 5 | - gotk-sync.yaml 6 | -------------------------------------------------------------------------------- /src/k8s/demo-helm/charts/calculator/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for go-calculator. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | maxNumber: 50 6 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/entity/action.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type Action struct { 4 | ID int64 `db:"id"` 5 | ResourceID int64 `db:"resource_id"` 6 | Name string `db:"name"` 7 | } 8 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/overlays/staging/calculator/configMap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: calculator-configmap 5 | namespace: calculator 6 | data: 7 | max_number: "75" 8 | -------------------------------------------------------------------------------- /src/k8s/demo-helm/charts/calculator/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Release.Name }}-configmap 5 | data: 6 | max_number: {{ .Values.maxNumber | quote }} 7 | -------------------------------------------------------------------------------- /src/datastructure/binarytree/binarytree.go: -------------------------------------------------------------------------------- 1 | package binarytree 2 | 3 | import "github.com/syafdia/go-exercise/src/datastructure/types" 4 | 5 | type BinaryTree interface { 6 | Left() types.T 7 | Right() types.T 8 | } 9 | -------------------------------------------------------------------------------- /src/datastructure/hashmap/hashmap.go: -------------------------------------------------------------------------------- 1 | package hashmap 2 | 3 | import "github.com/syafdia/go-exercise/src/datastructure/types" 4 | 5 | type HashMap interface { 6 | Set(k types.T, v types.U) 7 | Get(k types.T) 8 | } 9 | -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/overlays/production/calculator/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../../base/calculator 3 | namePrefix: production- 4 | patches: 5 | - path: configMap.yaml 6 | - path: deployment.yaml -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/entity/business/get_one_response.go: -------------------------------------------------------------------------------- 1 | package business 2 | 3 | type GetOneResponse struct { 4 | BusinessID string 5 | CountryID string 6 | IndustryID int64 7 | LegalEntityID int64 8 | } 9 | -------------------------------------------------------------------------------- /src/etc/XAAM/pkg/slices/slices.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | func ContainsStr(xs []string, s string) bool { 4 | for _, x := range xs { 5 | if x == s { 6 | return true 7 | } 8 | } 9 | 10 | return false 11 | } 12 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/entity/auth/find_resources_by_compliance_request.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | type FindResourcesByComplianceRequest struct { 4 | IndustryID int64 5 | LegalEntityID int64 6 | Resources []string 7 | } 8 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build plugin. 2 | FROM kong/go-plugin-tool:2.0.4-alpine-latest 3 | 4 | ENV BUILD_DIR /tmp/go-plugins/ 5 | 6 | WORKDIR $BUILD_DIR 7 | 8 | COPY . . 9 | RUN apk add make 10 | RUN make all 11 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/demo-unit-test 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/DATA-DOG/go-sqlmock v1.5.0 7 | github.com/golang/mock v1.6.0 8 | github.com/jmoiron/sqlx v1.3.4 9 | ) 10 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/datadog/helmRepository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1beta2 2 | kind: HelmRepository 3 | metadata: 4 | name: datadog-helm-repository 5 | spec: 6 | interval: 5m 7 | url: https://helm.datadoghq.com 8 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/delivery/ping_handler.go: -------------------------------------------------------------------------------- 1 | package delivery 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func GetPingHandler() gin.HandlerFunc { 6 | return func(c *gin.Context) { 7 | c.JSON(200, gin.H{"message": "pong"}) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/internal/domain/entity/keychecker/errors.go: -------------------------------------------------------------------------------- 1 | package keychecker 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | ErrKeyEmpty = errors.New("key is empty") 9 | ErrKeyNotValid = errors.New("key is not valid") 10 | ) 11 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/postgresql-core/helmRepository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1beta2 2 | kind: HelmRepository 3 | metadata: 4 | name: bitnami-helm-repository 5 | spec: 6 | interval: 5m 7 | url: https://charts.bitnami.com/bitnami 8 | -------------------------------------------------------------------------------- /src/etc/demo-oauth2/internal/auth/types.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrNotFound = errors.New("data is not found") 7 | ) 8 | 9 | type User struct { 10 | ID string 11 | Username string 12 | Password string 13 | } 14 | -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/base/postgresql-core/helmRepository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1beta2 2 | kind: HelmRepository 3 | metadata: 4 | name: bitnami-helm-repository 5 | spec: 6 | interval: 5m 7 | url: https://charts.bitnami.com/bitnami 8 | -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/overlays/production/redis-cache/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../../base/redis-cache 3 | namePrefix: production- 4 | # helmCharts: 5 | # - name: redis 6 | # repo: https://charts.bitnami.com/bitnami 7 | # valuesFile: values.yaml -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/domain/communication/service.go: -------------------------------------------------------------------------------- 1 | package communication 2 | 3 | import "context" 4 | 5 | type EmailMessage = string 6 | 7 | type EmailService interface { 8 | SendEmail(ctx context.Context, message EmailMessage) 9 | } 10 | -------------------------------------------------------------------------------- /src/datastructure/types/func.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type Func0 func(v T) 4 | 5 | type Func1 func(v T) U 6 | 7 | type Func2 func(v1 T, v2 U) V 8 | 9 | type Mapper func(v T) U 10 | 11 | type Filterrer func(v T) bool 12 | 13 | type Reducer func(acc U, v T) U 14 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/scripts/start-development.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run docker 4 | docker-compose up -d 5 | 6 | # Hot Reload 7 | CompileDaemon \ 8 | -build='docker-compose exec -T kong-plugin-builder make all' \ 9 | -command='docker-compose exec -T kong kong reload' -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/flagger/helmRepository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1beta2 2 | kind: HelmRepository 3 | metadata: 4 | name: flagger-helm-repository 5 | namespace: flagger 6 | spec: 7 | interval: 10m 8 | url: https://flagger.app 9 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/internal/domain/repo/token_repository.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import "context" 4 | 5 | type TokenRepository interface { 6 | ExchangeToken(ctx context.Context, key string) (string, error) 7 | Invalidate(ctx context.Context, token string) error 8 | } 9 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/postgresql-core/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | metadata: 4 | name: postgresql-core 5 | commonLabels: 6 | app: postgresql-core 7 | resources: 8 | - helmRepository.yaml 9 | - helmRelease.yaml -------------------------------------------------------------------------------- /src/k8s/demo-flux/clusters/development/sources.yaml: -------------------------------------------------------------------------------- 1 | # apiVersion: source.toolkit.fluxcd.io/v1beta2 2 | # kind: HelmRepository 3 | # metadata: 4 | # name: datadog-helm-repository 5 | # namespace: flux-system 6 | # spec: 7 | # interval: 5m 8 | # url: https://helm.datadoghq.com -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/base/postgresql-core/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | metadata: 4 | name: postgresql-core 5 | commonLabels: 6 | app: postgresql-core 7 | resources: 8 | - helmRepository.yaml 9 | - helmRelease.yaml -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/config.yml: -------------------------------------------------------------------------------- 1 | _format_version: '2.1' 2 | services: 3 | - name: mockbin-service-1 4 | url: http://mockbin.com/request 5 | routes: 6 | - paths: 7 | - '/' 8 | plugins: 9 | - name: key-checker 10 | config: 11 | api_key: mysecretconsumerkey 12 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/datadog/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: datadog 4 | metadata: 5 | name: datadog 6 | commonLabels: 7 | app: datadog 8 | resources: 9 | - namespace.yaml 10 | - helmRelease.yaml -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/base/calculator/kustomization.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | metadata: 5 | name: calculator 6 | commonLabels: 7 | app: calculator 8 | resources: 9 | - deployment.yaml 10 | - service.yaml 11 | - configMap.yaml -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/base/calculator/service.yaml: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: calculator-service 5 | spec: 6 | selector: 7 | app: calculator 8 | type: LoadBalancer 9 | ports: 10 | - protocol: TCP 11 | port: 8080 12 | targetPort: 8080 -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/redis-cache/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: redis-service 5 | namespace: redis-cache 6 | spec: 7 | type: LoadBalancer 8 | selector: 9 | appName: redis-cache 10 | ports: 11 | - port: 6379 12 | targetPort: 6379 -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/gloo-system/helmRepository.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: source.toolkit.fluxcd.io/v1beta2 2 | kind: HelmRepository 3 | metadata: 4 | name: solo-helm-repository 5 | namespace: gloo-system 6 | spec: 7 | interval: 5m 8 | url: https://storage.googleapis.com/solo-public-helm -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/cmd/kong/keychecker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Kong/go-pdk/server" 5 | kongdelivery "github.com/syafdia/demo-kong-plugin/internal/delivery/kong" 6 | ) 7 | 8 | func main() { 9 | server.StartServer(kongdelivery.NewKeyChecker, "0.1", 0) 10 | } 11 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/centralized-log/kibana.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kibana.k8s.elastic.co/v1 2 | kind: Kibana 3 | metadata: 4 | name: centralized-log-kb 5 | namespace: centralized-log 6 | spec: 7 | version: 8.9.1 8 | count: 1 9 | elasticsearchRef: 10 | name: centralized-log-es -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/entity/auth/check_response.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | type CheckResultResponse struct { 4 | Kind string `json:"kind"` 5 | Actions map[string]string `json:"actions"` 6 | } 7 | 8 | type CheckResponse struct { 9 | Results []CheckResultResponse `json:"results"` 10 | } 11 | -------------------------------------------------------------------------------- /src/etc/demo-oauth2/internal/pokedex/clients.go: -------------------------------------------------------------------------------- 1 | package pokedex 2 | 3 | import "golang.org/x/oauth2" 4 | 5 | type Client struct { 6 | OAuth2 *oauth2.Config 7 | Pokemon interface{} 8 | } 9 | 10 | func NewClient(oauth2 *oauth2.Config) *Client { 11 | return &Client{ 12 | OAuth2: oauth2, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/redis-cache/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: redis-cache 4 | metadata: 5 | name: redis-cache 6 | commonLabels: 7 | app: redis-cache 8 | resources: 9 | - namespace.yaml 10 | - deployment.yaml 11 | - service.yaml -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/flagger/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: flagger 4 | metadata: 5 | name: flagger 6 | commonLabels: 7 | app: flagger 8 | resources: 9 | - namespace.yaml 10 | - helmRepository.yaml 11 | - helmRelease.yaml -------------------------------------------------------------------------------- /src/k8s/demo-helm/charts/calculator/templates/service.yaml: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: {{ .Release.Name }}-service 5 | spec: 6 | selector: 7 | app: {{ .Release.Name }} 8 | type: LoadBalancer 9 | ports: 10 | - protocol: TCP 11 | port: 8080 12 | targetPort: 8080 -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/repo/business_repo.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | 6 | be "github.com/syafdia/xaam/internal/domain/entity/business" 7 | ) 8 | 9 | type BusinessRepo interface { 10 | FindOneByBusinessID(ctx context.Context, businessID string) (be.GetOneResponse, error) 11 | } 12 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/calculator/service.yaml: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: calculator-service 5 | namespace: calculator 6 | spec: 7 | type: LoadBalancer 8 | selector: 9 | appName: calculator 10 | ports: 11 | - protocol: TCP 12 | port: 8080 13 | targetPort: 8080 -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/article/repo.go: -------------------------------------------------------------------------------- 1 | package article 2 | 3 | import "context" 4 | 5 | type ArticleRepo interface { 6 | Create(ctx context.Context, input CreateArticleInput) (Article, error) 7 | FindOneByID(ctx context.Context, id int64) (Article, error) 8 | Destroy(ctx context.Context, id int64) error 9 | } 10 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/domain/task/repository.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/syafdia/demo-es/internal/domain" 7 | ) 8 | 9 | type TaskRepository interface { 10 | Store(ctx context.Context, task Task) error 11 | Find(ctx context.Context, id domain.ID) (Task, error) 12 | } 13 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/domain/types.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/google/uuid" 7 | ) 8 | 9 | type ID string 10 | 11 | func NewID() ID { 12 | return ID(uuid.New().String()) 13 | } 14 | 15 | type Event interface { 16 | AggregateID() ID 17 | OccuredAt() time.Time 18 | } 19 | -------------------------------------------------------------------------------- /src/etc/clean-arch-ddd-cqrs-es/internal/domain/user/error_code.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "github.com/syafdia/clean-arch-ddd-cqrs-es/internal/domain" 4 | 5 | const ( 6 | ErrCodeInvalidPasswordLength domain.ErrorCode = "USER-INVALID_PASSWORD_LENGTH" 7 | ErrCodeInvalidEmailFormat domain.ErrorCode = "USER-INVALID_EMAIL_FORMAT" 8 | ) 9 | -------------------------------------------------------------------------------- /src/etc/demo-redlock/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | redis-1: 5 | image: "redis:alpine" 6 | ports: 7 | - '7001:6379' 8 | redis-2: 9 | image: "redis:alpine" 10 | ports: 11 | - '7002:6379' 12 | redis-3: 13 | image: "redis:alpine" 14 | ports: 15 | - '7003:6379' 16 | 17 | -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/base/redis-cache/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | metadata: 4 | name: redis-cache 5 | commonLabels: 6 | app: redis-cache 7 | helmCharts: 8 | - name: redis 9 | repo: https://charts.bitnami.com/bitnami 10 | version: 7.0.11 11 | valuesFile: values.yaml -------------------------------------------------------------------------------- /src/distributed/semaphore/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/distributed/semaphore 2 | 3 | go 1.20 4 | 5 | require github.com/go-redis/redis/v8 v8.11.5 6 | 7 | require ( 8 | github.com/cespare/xxhash/v2 v2.1.2 // indirect 9 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 10 | github.com/google/uuid v1.6.0 11 | ) 12 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/centralized-log/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: centralized-log 4 | metadata: 5 | name: centralized-log 6 | commonLabels: 7 | app: centralized-log 8 | resources: 9 | - namespace.yaml 10 | - elasticsearch.yaml 11 | - kibana.yaml 12 | - beat.yaml -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/gloo-system/gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.solo.io/v1 2 | kind: Gateway 3 | metadata: 4 | name: gateway-proxy 5 | namespace: gloo-system 6 | 7 | spec: 8 | bindAddress: '::' 9 | bindPort: 8080 10 | httpGateway: {} 11 | proxyNames: 12 | - gateway-proxy 13 | ssl: false 14 | useProxyProto: false 15 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/clusters/staging/apps.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: calculator 5 | namespace: flux-system 6 | spec: 7 | interval: 30s 8 | sourceRef: 9 | kind: GitRepository 10 | name: flux-system 11 | path: ./src/k8s/demo-flux/apps/overlays/staging/calculator 12 | prune: true -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/repo/resource_action_repo.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | 6 | e "github.com/syafdia/xaam/internal/domain/entity" 7 | ) 8 | 9 | type PolicyRepo interface { 10 | FindMultipleByIDandTargetType( 11 | ctx context.Context, 12 | targetID int64, 13 | targetType e.TargetType, 14 | ) ([]e.Action, error) 15 | } 16 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/demo-kong-plugin 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/Kong/go-pdk v0.6.0 7 | github.com/fatih/color v1.10.0 // indirect 8 | github.com/fsnotify/fsnotify v1.4.9 // indirect 9 | github.com/githubnemo/CompileDaemon v1.2.1 // indirect 10 | github.com/pkg/errors v0.9.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/calculator/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: calculator 4 | metadata: 5 | name: calculator 6 | commonLabels: 7 | app: calculator 8 | resources: 9 | - namespace.yaml 10 | - configMap.yaml 11 | - deployment.yaml 12 | - service.yaml 13 | - virtualService.yaml 14 | - canary.yaml -------------------------------------------------------------------------------- /src/datastructure/linkedlist/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/syafdia/go-exercise/src/datastructure/linkedlist" 7 | ) 8 | 9 | type Person struct { 10 | Name string 11 | Age int 12 | } 13 | 14 | func tryLinkedList() { 15 | 16 | } 17 | 18 | func main() { 19 | xs := linkedlist.New("H", "E", "L") 20 | fmt.Println(xs) 21 | } 22 | -------------------------------------------------------------------------------- /src/etc/XAAM/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | redis-xaam: 5 | image: "redis:alpine" 6 | ports: 7 | - '6379:6379' 8 | postgresql-xaam: 9 | image: "postgres:14.2-alpine" 10 | restart: always 11 | environment: 12 | POSTGRES_USER: root 13 | POSTGRES_PASSWORD: root 14 | POSTGRES_DB: xaam 15 | ports: 16 | - '5432:5432' -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/entity/target_resource_action.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type TargetType = string 4 | 5 | const ( 6 | TargetTypeLegalEntity TargetType = "legal_entity" 7 | TargetTypeIndustry TargetType = "industry" 8 | ) 9 | 10 | type TargetAction struct { 11 | ID int64 12 | ResourceID int64 13 | Name string 14 | TargetID int64 15 | TargetType TargetType 16 | } 17 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/article/entity.go: -------------------------------------------------------------------------------- 1 | package article 2 | 3 | type Article struct { 4 | ID int64 5 | Title string 6 | Body string 7 | UserID int64 8 | } 9 | 10 | type ArticleWithAuthor struct { 11 | Article 12 | UserFirstName string 13 | UserLastName string 14 | } 15 | 16 | type CreateArticleInput struct { 17 | Title string 18 | Body string 19 | UserID int64 20 | } 21 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/gloo-system/upstreams.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: gloo.solo.io/v1 3 | kind: Upstream 4 | metadata: 5 | name: auth-server-upstream 6 | namespace: gloo-system 7 | spec: 8 | discoveryMetadata: {} 9 | kube: 10 | selector: 11 | app: auth-server 12 | serviceName: auth-server-service 13 | serviceNamespace: gloo-system 14 | servicePort: 8000 15 | --- -------------------------------------------------------------------------------- /src/etc/clean-arch-ddd-cqrs-es/pkg/errr/errr.go: -------------------------------------------------------------------------------- 1 | package errr 2 | 3 | type Err struct { 4 | code string 5 | message string 6 | } 7 | 8 | func NewErr(code string, message string) *Err { 9 | return &Err{ 10 | code: code, 11 | message: message, 12 | } 13 | } 14 | 15 | func (e *Err) GetCode() string { 16 | return e.code 17 | } 18 | 19 | func (e *Err) Error() string { 20 | return e.message 21 | } 22 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/centralized-log/elasticsearch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: elasticsearch.k8s.elastic.co/v1 2 | kind: Elasticsearch 3 | metadata: 4 | name: centralized-log-es 5 | namespace: centralized-log 6 | spec: 7 | version: 8.9.1 8 | nodeSets: 9 | - name: default-nodes 10 | count: 1 11 | config: 12 | node.roles: ["master", "data", "ingest"] 13 | node.store.allow_mmap: false -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/gloo-system/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: gloo-system 4 | metadata: 5 | name: gloo-system 6 | commonLabels: 7 | app: gloo-system 8 | resources: 9 | - namespace.yaml 10 | - helmRepository.yaml 11 | - helmRelease.yaml 12 | - gateway.yaml 13 | - upstreams.yaml 14 | - virtualServices.yaml 15 | - settings.yaml -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/gloo-system/virtualServices.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.solo.io/v1 2 | kind: VirtualService 3 | metadata: 4 | name: default 5 | namespace: gloo-system 6 | spec: 7 | virtualHost: 8 | domains: 9 | - '*' 10 | routes: 11 | - matchers: 12 | - prefix: / 13 | directResponseAction: 14 | status: 404 15 | body: 'whoops, service is not found' 16 | --- -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/domain/report/repository.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/syafdia/demo-es/internal/domain" 7 | ) 8 | 9 | type ReportRepository interface { 10 | Store(ctx context.Context, taskReport TaskReport) error 11 | Update(ctx context.Context, taskReport TaskReport) error 12 | FindByTaskID(ctx context.Context, taskID domain.ID) (TaskReport, error) 13 | } 14 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/internal/di/app_module.go: -------------------------------------------------------------------------------- 1 | package di 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type AppModule struct { 8 | } 9 | 10 | func NewAppModule() *AppModule { 11 | return &AppModule{} 12 | } 13 | 14 | var onceAppModule sync.Once 15 | var appModule *AppModule 16 | 17 | func GetAppModule() *AppModule { 18 | onceAppModule.Do(func() { 19 | appModule = NewAppModule() 20 | }) 21 | 22 | return appModule 23 | } 24 | -------------------------------------------------------------------------------- /src/etc/demo-nats/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/demo-nats 2 | 3 | go 1.20 4 | 5 | require github.com/nats-io/nats.go v1.36.0 6 | 7 | require ( 8 | github.com/klauspost/compress v1.17.2 // indirect 9 | github.com/nats-io/nkeys v0.4.7 // indirect 10 | github.com/nats-io/nuid v1.0.1 // indirect 11 | golang.org/x/crypto v0.18.0 // indirect 12 | golang.org/x/sys v0.16.0 // indirect 13 | golang.org/x/text v0.14.0 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /src/etc/go-calculator/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build binary. 2 | FROM golang:1.20 AS builder 3 | 4 | WORKDIR /app 5 | 6 | COPY go.mod go.sum ./ 7 | RUN go mod download 8 | 9 | COPY *.go ./ 10 | 11 | RUN CGO_ENABLED=0 GOOS=linux go build -o /go-calculator 12 | 13 | # Pack binary. 14 | FROM ubuntu:22.04 AS build-release-stage 15 | 16 | WORKDIR /home 17 | 18 | COPY --from=builder /go-calculator ./go-calculator 19 | 20 | EXPOSE 8080 21 | 22 | CMD ["./go-calculator"] -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/base/redis-cache/values.yaml: -------------------------------------------------------------------------------- 1 | nameOverride: "redis-cache" 2 | fullnameOverride: "redis-cache" 3 | architecture: standalone 4 | replica: 5 | replicaCount: 0 6 | auth: 7 | enabled: false 8 | password: "rediscachepassword" 9 | master: 10 | disableCommands: [] 11 | persistence: 12 | enabled: false 13 | resources: 14 | limits: 15 | cpu: 500m 16 | memory: 256Mi 17 | requests: 18 | cpu: 300m 19 | memory: 128Mi -------------------------------------------------------------------------------- /src/concurrency/semaphore/semaphore.go: -------------------------------------------------------------------------------- 1 | package semaphore 2 | 3 | type Semaphore interface { 4 | Acquire() 5 | Release() 6 | } 7 | 8 | type semaphore struct { 9 | semC chan struct{} 10 | } 11 | 12 | func New(maxConcurrency int) Semaphore { 13 | return &semaphore{ 14 | semC: make(chan struct{}, maxConcurrency), 15 | } 16 | } 17 | 18 | func (s *semaphore) Acquire() { 19 | s.semC <- struct{}{} 20 | } 21 | 22 | func (s *semaphore) Release() { 23 | <-s.semC 24 | } 25 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/infrastructure/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/syafdia/demo-es/internal/domain/communication" 8 | ) 9 | 10 | type EmailService struct{} 11 | 12 | func NewEmailService() *EmailService { 13 | return &EmailService{} 14 | } 15 | 16 | func (e *EmailService) SendEmail(ctx context.Context, message communication.EmailMessage) { 17 | fmt.Println("Send email:", message) 18 | } 19 | -------------------------------------------------------------------------------- /src/etc/demo-oauth2/internal/auth/clients.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type Client struct { 8 | HydraAdmin *HydraAdmin 9 | } 10 | 11 | func NewClient() *Client { 12 | return &Client{} 13 | } 14 | 15 | type HydraAdmin struct { 16 | } 17 | 18 | type AcceptLoginChallengeRequest struct { 19 | LoginChallenge string 20 | } 21 | 22 | func (h *HydraAdmin) AcceptLoginChallenge(ctx context.Context, in AcceptLoginChallengeRequest) error { 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /src/k8s/demo-helm/charts/calculator/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/delivery/utils.go: -------------------------------------------------------------------------------- 1 | package delivery 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/syafdia/xaam/internal/domain/entity" 9 | ) 10 | 11 | func handleError(ctx *gin.Context, err error) { 12 | if errr, ok := err.(*entity.Err); ok { 13 | ctx.JSON(errr.Status, gin.H{ 14 | "message": errr.Error(), 15 | }) 16 | return 17 | } 18 | 19 | ctx.JSON(http.StatusInternalServerError, gin.H{ 20 | "message": err.Error(), 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/gloo-system/helmRelease.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: gloo-system 5 | namespace: gloo-system 6 | spec: 7 | interval: 5m 8 | chart: 9 | spec: 10 | chart: gloo 11 | version: 1.17.15 12 | sourceRef: 13 | kind: HelmRepository 14 | name: solo-helm-repository 15 | namespace: gloo-system 16 | values: 17 | discovery: 18 | enabled: false 19 | 20 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/repo/resource_repo.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "context" 5 | 6 | e "github.com/syafdia/xaam/internal/domain/entity" 7 | ) 8 | 9 | // mockgen -source=internal/domain/repo/resource_repo.go -destination=internal/mock/domain/repo/resource_repo.go 10 | type ResourceRepo interface { 11 | FindMultipleByIDs(ctx context.Context, ids []int64) (map[int64]e.Resource, error) 12 | FindMultipleByNames(ctx context.Context, names []string) (map[string]e.Resource, error) 13 | } 14 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/calculator/virtualService.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.solo.io/v1 2 | kind: VirtualService 3 | metadata: 4 | name: calculator-vs 5 | namespace: calculator 6 | spec: 7 | virtualHost: 8 | domains: 9 | - calculator.service.local 10 | options: 11 | extauth: 12 | customAuth: {} 13 | routes: 14 | - matchers: 15 | - prefix: / 16 | delegateAction: 17 | ref: 18 | name: calculator-deployment 19 | namespace: calculator -------------------------------------------------------------------------------- /src/etc/XAAM/e2e/ping_test.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "net/http" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/dghubble/sling" 9 | ) 10 | 11 | func Test_GetPing_200(t *testing.T) { 12 | client := &http.Client{} 13 | req, _ := sling.New().Get("http://0.0.0.0:8080/ping").Request() 14 | 15 | resp, _ := client.Do(req) 16 | defer resp.Body.Close() 17 | 18 | if !reflect.DeepEqual(resp.StatusCode, http.StatusOK) { 19 | t.Errorf("http status is not 200, got %d", resp.StatusCode) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/etc/clean-arch-ddd-cqrs-es/internal/domain/error.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "github.com/syafdia/clean-arch-ddd-cqrs-es/pkg/errr" 4 | 5 | type ErrorCode string 6 | 7 | type ValidationError struct { 8 | errr.Err 9 | } 10 | 11 | func NewValidationError(code ErrorCode, message string) *ValidationError { 12 | return &ValidationError{*errr.NewErr(string(code), message)} 13 | } 14 | 15 | var ( 16 | ErrNotImplemented = errr.NewErr("CORE-FEATURE_NOT_IMPLEMENTED", "feature is not implemented yet") 17 | ) 18 | -------------------------------------------------------------------------------- /src/concurrency/pipeline/example/e01/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/syafdia/go-exercise/src/concurrency/pipeline/example" 8 | ) 9 | 10 | func main() { 11 | startTime := time.Now() 12 | 13 | for i := 0; i < 3; i++ { 14 | val := example.AddFoo(example.AddQuoute(example.Square(example.MultiplyTwo(i)))) 15 | fmt.Printf("Input: %d, Output %s\n", i, val) 16 | 17 | } 18 | 19 | fmt.Printf("Elapsed time without concurrency: %s\n", time.Since(startTime)) 20 | } 21 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/postgresql-core/helmRelease.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: postgresql-core 5 | spec: 6 | interval: 5m 7 | chart: 8 | spec: 9 | chart: postgresql 10 | version: 12.5.6 11 | sourceRef: 12 | kind: HelmRepository 13 | name: bitnami-helm-repository 14 | values: 15 | auth: 16 | username: "postgresqlcore" 17 | password: "postgresqlcorepassword" 18 | database: "postgresqlcore" 19 | -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/base/postgresql-core/helmRelease.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: postgresql-core 5 | spec: 6 | interval: 5m 7 | chart: 8 | spec: 9 | chart: postgresql 10 | version: 12.5.6 11 | sourceRef: 12 | kind: HelmRepository 13 | name: bitnami-helm-repository 14 | values: 15 | auth: 16 | username: "postgresqlcore" 17 | password: "postgresqlcorepassword" 18 | database: "postgresqlcore" 19 | -------------------------------------------------------------------------------- /src/concurrency/pipeline/example/util.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func MultiplyTwo(v int) int { 9 | time.Sleep(2 * time.Second) 10 | return v * 2 11 | } 12 | 13 | func Square(v int) int { 14 | time.Sleep(2 * time.Second) 15 | return v * v 16 | } 17 | 18 | func AddQuoute(v int) string { 19 | time.Sleep(2 * time.Second) 20 | return fmt.Sprintf("'%d'", v) 21 | } 22 | 23 | func AddFoo(v string) string { 24 | time.Sleep(2 * time.Second) 25 | return fmt.Sprintf("Foo %v", v) 26 | } 27 | -------------------------------------------------------------------------------- /src/etc/XAAM/cmd/http/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/syafdia/xaam/internal/delivery" 6 | "github.com/syafdia/xaam/internal/di" 7 | ) 8 | 9 | func main() { 10 | appModule := di.GetAppModule() 11 | repoModule := di.GetRepoModule(appModule) 12 | useCaseModule := di.GetUseCaseModule(repoModule) 13 | 14 | r := gin.Default() 15 | r.GET("/ping", delivery.GetPingHandler()) 16 | r.POST("/api/v1/authorisation/check", delivery.PostAuthorisationHandler(useCaseModule)) 17 | 18 | r.Run() 19 | } 20 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/redis-cache/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: redis-deployment 5 | namespace: redis-cache 6 | labels: 7 | type: db 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | appName: redis-cache 13 | progressDeadlineSeconds: 60 14 | template: 15 | metadata: 16 | labels: 17 | appName: redis-cache 18 | spec: 19 | containers: 20 | - name: redis-cache 21 | image: redis:7.0 22 | ports: 23 | - containerPort: 6379 -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/overlays/production/redis-cache/values.yaml: -------------------------------------------------------------------------------- 1 | nameOverride: "redis-cache" 2 | fullnameOverride: "redis-cache" 3 | architecture: standalone 4 | replica: 5 | replicaCount: 0 6 | image: 7 | registry: docker.io 8 | repository: bitnami/redis 9 | tag: 6.2.3-debian-10-r0 10 | auth: 11 | enabled: true 12 | password: "rediscachepassword" 13 | master: 14 | disableCommands: [] 15 | persistence: 16 | enabled: false 17 | resources: 18 | limits: 19 | cpu: 750m 20 | memory: 256Mi 21 | requests: 22 | cpu: 500m 23 | memory: 128Mi -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Exercise 2 | 3 | Exercise for learning Go programming language, includes : 4 | 5 | ## Data structure 6 | * Binary Tree 7 | * Hashmap 8 | * [Linked List](src/datastructure/linkedlist) 9 | * [Queue](src/datastructure/queue) 10 | * Stack 11 | 12 | 13 | ## Concurrency 14 | * [Pipeline](src/concurrency/pipeline) 15 | * [Semaphore](src/concurrency/semaphore) 16 | * [Worker Pool](src/concurrency/workerpool) 17 | 18 | ## Etc 19 | * [Kong Plugin](src/etc/demo-kong-plugin) 20 | * [Redlock](src/etc/demo-redlock) 21 | * [Unit Test](src/etc/demo-unit-test) 22 | 23 | ## Network 24 | * TBA -------------------------------------------------------------------------------- /src/etc/kafka/basic/publisher/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | kafka "github.com/segmentio/kafka-go" 8 | ) 9 | 10 | func main() { 11 | w := &kafka.Writer{ 12 | Addr: kafka.TCP("localhost:9092"), 13 | Topic: "topic-1", 14 | Balancer: &kafka.LeastBytes{}, 15 | } 16 | 17 | defer w.Close() 18 | 19 | err := w.WriteMessages(context.Background(), kafka.Message{ 20 | Key: []byte("Key-A"), 21 | Value: []byte("Hello World!"), 22 | }) 23 | 24 | if err != nil { 25 | log.Fatal("failed when writing message, err:", err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/etc/clean-arch-ddd-cqrs-es/internal/domain/task/error_code.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import "github.com/syafdia/clean-arch-ddd-cqrs-es/internal/domain" 4 | 5 | const ( 6 | ErrCodeInvalidTitleLength domain.ErrorCode = "TASK-INVALID_TITLE_LENGTH" 7 | ErrCodeInvalidDescriptionLength domain.ErrorCode = "TASK-INVALID_DESCRIPTION" 8 | ErrCodeInvalidStateTransition domain.ErrorCode = "TASK-INVALID_STATE_TRANSITION" 9 | ErrCodeInvalidTagNameLength domain.ErrorCode = "TASK-INVALID_TAG_NAME_LENGTH" 10 | ErrCodeTagAlreadyExists domain.ErrorCode = "TASK-TAG_ALREADY_EXISTS" 11 | ) 12 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/entity/auth/check_request.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | type CheckResourceRequest struct { 4 | Kind string `json:"kind"` 5 | } 6 | 7 | type CheckPrincipalRequest struct { 8 | Kind string `json:"kind"` 9 | ID string `json:"id"` 10 | Roles []string `json:"roles"` 11 | Metadata map[string]string `json:"metadata"` 12 | } 13 | 14 | type CheckRequest struct { 15 | Actions []string `json:"actions"` 16 | Resource CheckResourceRequest `json:"resource"` 17 | Principal CheckPrincipalRequest `json:"principal"` 18 | } 19 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/internal/data/token_repository.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/syafdia/demo-kong-plugin/internal/domain/repo" 8 | ) 9 | 10 | type tokenRepository struct { 11 | } 12 | 13 | func NewTokenRepository() repo.TokenRepository { 14 | return &tokenRepository{} 15 | } 16 | 17 | func (t *tokenRepository) ExchangeToken(ctx context.Context, key string) (string, error) { 18 | return fmt.Sprintf("%s-thisisasecrettoken", key), nil 19 | } 20 | 21 | func (t *tokenRepository) Invalidate(ctx context.Context, token string) error { 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /src/etc/kafka/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | zookeeper: 4 | image: 'bitnami/zookeeper:latest' 5 | ports: 6 | - '2181:2181' 7 | environment: 8 | - ALLOW_ANONYMOUS_LOGIN=yes 9 | kafka: 10 | image: 'bitnami/kafka:latest' 11 | ports: 12 | - '9092:9092' 13 | environment: 14 | - KAFKA_BROKER_ID=1 15 | - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092 16 | - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:9092 17 | - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 18 | - ALLOW_PLAINTEXT_LISTENER=yes 19 | depends_on: 20 | - zookeeper -------------------------------------------------------------------------------- /src/etc/kafka/basic/consumer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/segmentio/kafka-go" 8 | ) 9 | 10 | func main() { 11 | r := kafka.NewReader(kafka.ReaderConfig{ 12 | Brokers: []string{"localhost:9092"}, 13 | Topic: "topic-1", 14 | Partition: 0, 15 | MinBytes: 10e3, // 10KB 16 | MaxBytes: 10e6, // 10MB 17 | }) 18 | 19 | defer r.Close() 20 | 21 | for { 22 | m, err := r.ReadMessage(context.Background()) 23 | if err != nil { 24 | break 25 | } 26 | fmt.Printf("message at offset %d: %s = %s\n", m.Offset, string(m.Key), string(m.Value)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Develop Kong plugin using Go 2 | 3 | ### How to Develop 4 | 5 | 1. Install [Docker](https://www.docker.com) 6 | 7 | 2. Install [CompileDaemon](https://github.com/githubnemo/CompileDaemon) 8 | ``` 9 | go get github.com/githubnemo/CompileDaemon 10 | 11 | ``` 12 | 13 | 2. Run development script 14 | ``` 15 | sh scripts/start-development.sh 16 | ``` 17 | 18 | 19 | ### How to test 20 | 1. Visit `curl --location --request GET '0.0.0.0:8000?key=invalidconsumerkey'`, it will return `401` 21 | 2. Visit `curl --location --request GET '0.0.0.0:8000?key=mysecretconsumerkey'`, it will return `200` with valid data -------------------------------------------------------------------------------- /src/k8s/demo-flux/clusters/development/apps.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: calculator 5 | namespace: flux-system 6 | spec: 7 | interval: 1m0s 8 | sourceRef: 9 | kind: GitRepository 10 | name: flux-system 11 | path: ./src/k8s/demo-flux/apps/base/calculator 12 | prune: true 13 | --- 14 | apiVersion: kustomize.toolkit.fluxcd.io/v1 15 | kind: Kustomization 16 | metadata: 17 | name: redis-cache 18 | namespace: flux-system 19 | spec: 20 | interval: 1m0s 21 | sourceRef: 22 | kind: GitRepository 23 | name: flux-system 24 | path: ./src/k8s/demo-flux/apps/base/redis-cache 25 | prune: true 26 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/internal/di/repo_module.go: -------------------------------------------------------------------------------- 1 | package di 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/syafdia/demo-kong-plugin/internal/data" 7 | "github.com/syafdia/demo-kong-plugin/internal/domain/repo" 8 | ) 9 | 10 | type RepoModule struct { 11 | TokenRepository repo.TokenRepository 12 | } 13 | 14 | func NewRepoModule(appModule *AppModule) *RepoModule { 15 | return &RepoModule{ 16 | TokenRepository: data.NewTokenRepository(), 17 | } 18 | } 19 | 20 | var onceRepoModule sync.Once 21 | var repoModule *RepoModule 22 | 23 | func GetRepoModule() *RepoModule { 24 | onceRepoModule.Do(func() { 25 | repoModule = NewRepoModule(GetAppModule()) 26 | }) 27 | 28 | return repoModule 29 | } 30 | -------------------------------------------------------------------------------- /src/concurrency/semaphore/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/syafdia/go-exercise/src/concurrency/semaphore" 8 | ) 9 | 10 | func main() { 11 | sem := semaphore.New(3) 12 | doneC := make(chan bool, 1) 13 | totProcess := 10 14 | 15 | for i := 1; i <= totProcess; i++ { 16 | sem.Acquire() 17 | go func(v int) { 18 | defer sem.Release() 19 | longRunningProcess(v) 20 | 21 | if v == totProcess { 22 | doneC <- true 23 | } 24 | }(i) 25 | } 26 | 27 | <-doneC 28 | } 29 | 30 | func longRunningProcess(taskID int) { 31 | fmt.Println(time.Now().Format("15:04:05"), "Running task with ID", taskID) 32 | time.Sleep(2 * time.Second) 33 | } 34 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/entity/errors.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | ) 7 | 8 | type Err struct { 9 | Errr error 10 | Status int 11 | } 12 | 13 | func NewErr(status int, message string) error { 14 | return &Err{Status: status, Errr: errors.New(message)} 15 | } 16 | 17 | func WrapErr(status int, err error) error { 18 | return &Err{Status: status, Errr: err} 19 | } 20 | 21 | func (e *Err) Error() string { 22 | return e.Errr.Error() 23 | } 24 | 25 | func (e *Err) Unwrap() error { 26 | return e.Errr 27 | } 28 | 29 | var ( 30 | ErrNotFound = NewErr(http.StatusNotFound, "not found") 31 | ErrBadRequest = NewErr(http.StatusBadRequest, "bad request") 32 | ) 33 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/calculator/canary.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: flagger.app/v1beta1 2 | kind: Canary 3 | metadata: 4 | name: calculator-canary 5 | namespace: calculator 6 | spec: 7 | provider: gloo 8 | targetRef: 9 | apiVersion: apps/v1 10 | kind: Deployment 11 | name: calculator-deployment 12 | service: 13 | port: 8080 14 | targetPort: 8080 15 | analysis: 16 | # schedule interval (default 60s) 17 | interval: 10s 18 | # max number of failed metric checks before rollback 19 | threshold: 5 20 | # max traffic percentage routed to canary 21 | # percentage (0-100) 22 | maxWeight: 50 23 | # canary increment step 24 | # percentage (0-100) 25 | stepWeight: 10 -------------------------------------------------------------------------------- /src/k8s/demo-kustomization/base/calculator/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: calculator-deployment 5 | labels: 6 | app: calculator 7 | spec: 8 | replicas: 3 9 | selector: 10 | matchLabels: 11 | app: calculator 12 | template: 13 | metadata: 14 | labels: 15 | app: calculator 16 | spec: 17 | containers: 18 | - name: go-calculator 19 | image: syafdia/go-calculator:latest 20 | ports: 21 | - containerPort: 8080 22 | env: 23 | - name: MAX_NUMBER 24 | valueFrom: 25 | configMapKeyRef: 26 | name: calculator-configmap 27 | key: max_number -------------------------------------------------------------------------------- /src/etc/demo-oauth2/internal/pokedex/handlers.go: -------------------------------------------------------------------------------- 1 | package pokedex 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | type PokedexHandler struct { 12 | client *Client 13 | } 14 | 15 | func NewPokedexhandler(client *Client) *PokedexHandler { 16 | return &PokedexHandler{ 17 | client: client, 18 | } 19 | } 20 | 21 | func (a *PokedexHandler) ListPokemons(c *gin.Context) { 22 | authCodeURL := a.client.OAuth2.AuthCodeURL(fmt.Sprintf("%d", time.Now().Unix())) 23 | c.HTML(http.StatusOK, "pokemons.list", map[string]interface{}{ 24 | "AuthCodeURL": authCodeURL, 25 | }) 26 | } 27 | 28 | func (a *PokedexHandler) OAuth2Callbacks(c *gin.Context) { 29 | // TODO 30 | } 31 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/user/usecase.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "context" 4 | 5 | type RegisterUserUseCase interface { 6 | Execute(ctx context.Context, input CreateUserInput) (User, error) 7 | } 8 | 9 | type registerUserUseCase struct { 10 | userRepo UserRepo 11 | } 12 | 13 | func NewRegisterUserUseCase( 14 | userRepo UserRepo, 15 | ) RegisterUserUseCase { 16 | return ®isterUserUseCase{userRepo: userRepo} 17 | } 18 | 19 | func (w *registerUserUseCase) Execute(ctx context.Context, input CreateUserInput) (User, error) { 20 | user, err := w.userRepo.Create(ctx, input) 21 | if err != nil { 22 | return User{}, err 23 | } 24 | 25 | // Do something 26 | // ... 27 | // ... 28 | 29 | return user, nil 30 | } 31 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/clusters/staging/flux-system/gotk-sync.yaml: -------------------------------------------------------------------------------- 1 | # This manifest was generated by flux. DO NOT EDIT. 2 | --- 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: GitRepository 5 | metadata: 6 | name: flux-system 7 | namespace: flux-system 8 | spec: 9 | interval: 1m0s 10 | ref: 11 | branch: master 12 | secretRef: 13 | name: flux-system 14 | url: ssh://git@github.com/syafdia/go-exercise 15 | --- 16 | apiVersion: kustomize.toolkit.fluxcd.io/v1 17 | kind: Kustomization 18 | metadata: 19 | name: flux-system 20 | namespace: flux-system 21 | spec: 22 | interval: 10m0s 23 | path: ./src/k8s/demo-flux/clusters/staging 24 | prune: true 25 | sourceRef: 26 | kind: GitRepository 27 | name: flux-system 28 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/clusters/development/flux-system/gotk-sync.yaml: -------------------------------------------------------------------------------- 1 | # This manifest was generated by flux. DO NOT EDIT. 2 | --- 3 | apiVersion: source.toolkit.fluxcd.io/v1 4 | kind: GitRepository 5 | metadata: 6 | name: flux-system 7 | namespace: flux-system 8 | spec: 9 | interval: 1m0s 10 | ref: 11 | branch: master 12 | secretRef: 13 | name: flux-system 14 | url: ssh://git@github.com/syafdia/go-exercise 15 | --- 16 | apiVersion: kustomize.toolkit.fluxcd.io/v1 17 | kind: Kustomization 18 | metadata: 19 | name: flux-system 20 | namespace: flux-system 21 | spec: 22 | interval: 10m0s 23 | path: ./src/k8s/demo-flux/clusters/development 24 | prune: true 25 | sourceRef: 26 | kind: GitRepository 27 | name: flux-system 28 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/internal/domain/usecase/keychecker/get_token_use_case.go: -------------------------------------------------------------------------------- 1 | package keychecker 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/syafdia/demo-kong-plugin/internal/domain/repo" 8 | ) 9 | 10 | type GetTokenUseCase interface { 11 | Execute(ctx context.Context, key string) (string, error) 12 | } 13 | 14 | type getTokenUseCase struct { 15 | tokenRepo repo.TokenRepository 16 | } 17 | 18 | func NewGetTokenUseCase(tokenRepo repo.TokenRepository) GetTokenUseCase { 19 | return &getTokenUseCase{tokenRepo} 20 | } 21 | 22 | func (v *getTokenUseCase) Execute(ctx context.Context, key string) (string, error) { 23 | log.Println("[GetTokenUseCase] Start get token use case") 24 | return v.tokenRepo.ExchangeToken(ctx, key) 25 | } 26 | -------------------------------------------------------------------------------- /src/k8s/demo-helm/charts/calculator/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ .Release.Name }}-deployment 5 | labels: 6 | app: {{ .Release.Name }} 7 | spec: 8 | replicas: 3 9 | selector: 10 | matchLabels: 11 | app: {{ .Release.Name }} 12 | template: 13 | metadata: 14 | labels: 15 | app: {{ .Release.Name }} 16 | spec: 17 | containers: 18 | - name: go-calculator 19 | image: syafdia/go-calculator:latest 20 | ports: 21 | - containerPort: 8080 22 | env: 23 | - name: MAX_NUMBER 24 | valueFrom: 25 | configMapKeyRef: 26 | name: {{ .Release.Name }}-configmap 27 | key: max_number -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/user/entity.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | type Gender string 4 | 5 | const ( 6 | GenderMale Gender = "male" 7 | GenderFemale Gender = "female" 8 | GenderOther Gender = "other" 9 | ) 10 | 11 | type User struct { 12 | ID int64 `json:"id"` 13 | Email string `json:"email"` 14 | FirstName string `json:"first_name"` 15 | LastName string `json:"last_name"` 16 | Gender Gender `json:"gender"` 17 | } 18 | 19 | type CreateUserInput struct { 20 | Email string `json:"email"` 21 | FirstName string `json:"first_name"` 22 | LastName string `json:"last_name"` 23 | Gender Gender `json:"gender"` 24 | } 25 | 26 | type UpdateUserInput struct { 27 | FirstName string 28 | LastName string 29 | Gender Gender 30 | } 31 | -------------------------------------------------------------------------------- /src/distributed/semaphore/go.sum: -------------------------------------------------------------------------------- 1 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 2 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 3 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 4 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 5 | github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= 6 | github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= 7 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 8 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 9 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/flagger/helmRelease.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: flagger-helm-release 5 | namespace: flagger 6 | spec: 7 | install: 8 | remediation: 9 | retries: 5 10 | remediateLastFailure: True 11 | upgrade: 12 | remediation: 13 | retries: 5 14 | remediateLastFailure: True 15 | timeout: 15m 16 | targetNamespace: flagger 17 | interval: 5m 18 | chart: 19 | spec: 20 | chart: flagger 21 | version: 1.34.0 22 | sourceRef: 23 | kind: HelmRepository 24 | name: flagger-helm-repository 25 | namespace: flagger 26 | values: 27 | crds: 28 | create: true 29 | fullnameOverride: flagger 30 | meshProvider: gloo 31 | includeLabelPrefix: '*' -------------------------------------------------------------------------------- /src/etc/demo-unit-test/usecase_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/syafdia/demo-unit-test/internal/user" 8 | ) 9 | 10 | func TestNewRegisterUserUseCase(t *testing.T) { 11 | type args struct { 12 | userRepo user.UserRepo 13 | } 14 | tests := []struct { 15 | name string 16 | args args 17 | want user.RegisterUserUseCase 18 | }{ 19 | { 20 | name: "success creating RegisterUserUseCase instance", 21 | args: args{}, 22 | want: user.NewRegisterUserUseCase(nil), 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := user.NewRegisterUserUseCase(tt.args.userRepo); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("NewRegisterUserUseCase() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/usecase/business/find_one_by_business_id_use_case.go: -------------------------------------------------------------------------------- 1 | package business 2 | 3 | import ( 4 | "context" 5 | 6 | be "github.com/syafdia/xaam/internal/domain/entity/business" 7 | "github.com/syafdia/xaam/internal/domain/repo" 8 | ) 9 | 10 | type FindOneByBusinessIDUseCase interface { 11 | Execute(ctx context.Context, businessID string) (be.GetOneResponse, error) 12 | } 13 | 14 | type findOneByBusinessIDUC struct { 15 | businessRepo repo.BusinessRepo 16 | } 17 | 18 | func NewFindOneByBusinessIDUseCase(businessRepo repo.BusinessRepo) FindOneByBusinessIDUseCase { 19 | return &findOneByBusinessIDUC{businessRepo: businessRepo} 20 | } 21 | 22 | func (g *findOneByBusinessIDUC) Execute(ctx context.Context, businessID string) (be.GetOneResponse, error) { 23 | return g.businessRepo.FindOneByBusinessID(ctx, businessID) 24 | } 25 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/apps/base/calculator/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: calculator-deployment 5 | namespace: calculator 6 | labels: 7 | app: calculator 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | appName: calculator 13 | template: 14 | metadata: 15 | labels: 16 | appName: calculator 17 | spec: 18 | containers: 19 | - name: go-calculator 20 | image: syafdia/go-calculator:latest 21 | ports: 22 | - containerPort: 8080 23 | env: 24 | - name: MAX_NUMBER 25 | valueFrom: 26 | configMapKeyRef: 27 | name: calculator-configmap 28 | key: max_number 29 | - name: REDIS_URL 30 | value: 'redis://redis-service.redis-cache.svc.cluster.local:6379' -------------------------------------------------------------------------------- /src/datastructure/queue/queue.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/syafdia/go-exercise/src/datastructure/types" 7 | ) 8 | 9 | type Queue interface { 10 | Enqueue(v types.T) 11 | Dequeue() types.T 12 | Size() int 13 | String() string 14 | } 15 | 16 | type queue struct { 17 | values []types.T 18 | } 19 | 20 | func New() Queue { 21 | return &queue{ 22 | values: []types.T{}, 23 | } 24 | } 25 | 26 | func (q *queue) Enqueue(v types.T) { 27 | q.values = append(q.values, v) 28 | } 29 | 30 | func (q *queue) Dequeue() types.T { 31 | if q.Size() == 0 { 32 | return nil 33 | } 34 | 35 | v := q.values[0] 36 | q.values = q.values[1:] 37 | 38 | return v 39 | } 40 | 41 | func (q *queue) Size() int { 42 | return len(q.values) 43 | } 44 | 45 | func (q *queue) String() string { 46 | return fmt.Sprintf("%v", q.values) 47 | } 48 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/gloo-system/settings.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gloo.solo.io/v1 2 | kind: Settings 3 | metadata: 4 | name: default 5 | namespace: gloo-system 6 | spec: 7 | discoveryNamespace: gloo-system 8 | gateway: 9 | validation: 10 | alwaysAccept: true 11 | proxyValidationServerAddr: gloo:9988 12 | gloo: 13 | xdsBindAddr: 0.0.0.0:9977 14 | kubernetesArtifactSource: {} 15 | kubernetesConfigSource: {} 16 | kubernetesSecretSource: {} 17 | refreshRate: 60s 18 | extauth: 19 | extauthzServerRef: 20 | name: auth-server-upstream 21 | namespace: gloo-system 22 | httpService: 23 | request: 24 | allowedHeadersRegex: ['.*'] 25 | response: 26 | allowedUpstreamHeaders: 27 | - 'x-user-context' 28 | requestBody: 29 | maxRequestBytes: 10240 30 | requestTimeout: 0.5s -------------------------------------------------------------------------------- /src/concurrency/pipeline/example/e02/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | 8 | "github.com/syafdia/go-exercise/src/concurrency/pipeline/example" 9 | ) 10 | 11 | func main() { 12 | N := 3 // Foo '0',Foo '4',Foo '16' -> Sequence is not guarantee. 13 | startTime := time.Now() 14 | valC := make(chan string, N) 15 | 16 | fmt.Printf("Input: %d\n", N) 17 | 18 | for i := 0; i < N; i++ { 19 | go func(input int) { 20 | val := example.AddFoo(example.AddQuoute(example.Square(example.MultiplyTwo(input)))) 21 | 22 | valC <- val 23 | }(i) 24 | } 25 | 26 | vals := []string{} 27 | for i := 0; i < N; i++ { 28 | vals = append(vals, <-valC) 29 | } 30 | 31 | result := strings.Join(vals, ",") 32 | 33 | fmt.Printf("Result: %s\n", result) 34 | fmt.Printf("Elapsed time without concurrency: %s", time.Since(startTime)) 35 | } 36 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/internal/di/usecase_module.go: -------------------------------------------------------------------------------- 1 | package di 2 | 3 | import ( 4 | "sync" 5 | 6 | keycheckerusecase "github.com/syafdia/demo-kong-plugin/internal/domain/usecase/keychecker" 7 | ) 8 | 9 | type UseCaseModule struct { 10 | ValidateKeyUseCase keycheckerusecase.ValidateKeyUseCase 11 | GetTokenUseCase keycheckerusecase.GetTokenUseCase 12 | } 13 | 14 | func NewUseCaseModule(repoModule *RepoModule) *UseCaseModule { 15 | return &UseCaseModule{ 16 | ValidateKeyUseCase: keycheckerusecase.NewValidateKeyUseCase(), 17 | GetTokenUseCase: keycheckerusecase.NewGetTokenUseCase(repoModule.TokenRepository), 18 | } 19 | } 20 | 21 | var onceUseCaseModule sync.Once 22 | var useCaseModule *UseCaseModule 23 | 24 | func GetUseCaseModule() *UseCaseModule { 25 | onceUseCaseModule.Do(func() { 26 | useCaseModule = NewUseCaseModule(GetRepoModule()) 27 | }) 28 | 29 | return useCaseModule 30 | } 31 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/datadog/helmRelease.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: helm.toolkit.fluxcd.io/v2beta1 2 | kind: HelmRelease 3 | metadata: 4 | name: datadog 5 | namespace: datadog 6 | spec: 7 | interval: 5m 8 | chart: 9 | spec: 10 | chart: datadog 11 | version: 3.32.7 12 | sourceRef: 13 | kind: HelmRepository 14 | name: datadog-helm-repository 15 | namespace: flux-system 16 | values: 17 | targetSystem: linux 18 | datadog: 19 | apiKey: xxxx 20 | site: xxxx 21 | logs: 22 | enabled: true 23 | containerCollectAll: true 24 | confd: 25 | openmetrics.yaml: | 26 | init_config: 27 | instances: 28 | - prometheus_url: https://foo-bar/metrics 29 | namespace: foo_bar 30 | ssl_ca_cert: false 31 | metrics: 32 | - foo 33 | 34 | -------------------------------------------------------------------------------- /src/etc/demo-oauth2/internal/auth/repositories.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "context" 4 | 5 | type Repo struct { 6 | User *UserRepo 7 | } 8 | 9 | func NewRepo() *Repo { 10 | return &Repo{ 11 | User: NewUserRepo(), 12 | } 13 | } 14 | 15 | type UserRepo struct { 16 | stores []User 17 | } 18 | 19 | func NewUserRepo() *UserRepo { 20 | u := &UserRepo{} 21 | u.seed() 22 | 23 | return u 24 | } 25 | 26 | func (u *UserRepo) FindByUsernameAndPassword(_ctx context.Context, username string, password string) (User, error) { 27 | for _, user := range u.stores { 28 | if user.Username == username && user.Password == password { 29 | return user, nil 30 | } 31 | } 32 | 33 | return User{}, ErrNotFound 34 | } 35 | 36 | func (u *UserRepo) seed() { 37 | u.stores = []User{ 38 | {ID: "usr-0001", Username: "john123", Password: "123"}, 39 | {ID: "usr-0002", Username: "doe456", Password: "456"}, 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/pkg/calc/calc_test.go: -------------------------------------------------------------------------------- 1 | package calc 2 | 3 | import "testing" 4 | 5 | func TestFactorial(t *testing.T) { 6 | type args struct { 7 | n uint 8 | } 9 | tests := []struct { 10 | name string 11 | args args 12 | want uint 13 | }{ 14 | { 15 | name: "N is 5", 16 | args: args{n: 5}, 17 | want: 120, 18 | }, 19 | { 20 | name: "N is 0", 21 | args: args{n: 0}, 22 | want: 1, 23 | }, 24 | { 25 | name: "N is 1", 26 | args: args{n: 1}, 27 | want: 1, 28 | }, 29 | { 30 | name: "N is 3", 31 | args: args{n: 3}, 32 | want: 6, 33 | }, 34 | { 35 | name: "N is 10", 36 | args: args{n: 10}, 37 | want: 3628800, 38 | }, 39 | } 40 | for _, tt := range tests { 41 | t.Run(tt.name, func(t *testing.T) { 42 | if got := Factorial(tt.args.n); got != tt.want { 43 | t.Errorf("Factorial() = %v, want %v", got, tt.want) 44 | } 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/domain/report/report.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/syafdia/demo-es/internal/domain" 7 | "github.com/syafdia/demo-es/internal/domain/task" 8 | ) 9 | 10 | type TaskReport struct { 11 | ID domain.ID 12 | TaskID domain.ID 13 | TaskTitle string 14 | StartedAt time.Time 15 | FinishedAt time.Time 16 | Duration time.Duration 17 | } 18 | 19 | func NewTaskReport() TaskReport { 20 | return TaskReport{ 21 | ID: domain.NewID(), 22 | } 23 | } 24 | 25 | func (t *TaskReport) ApplyEvent(event domain.Event) { 26 | switch e := event.(type) { 27 | case task.TaskCreated: 28 | t.TaskID = e.ID 29 | t.TaskTitle = e.Title 30 | t.StartedAt = e.OccuredAt() 31 | 32 | case task.TaskStatusChanged: 33 | if e.Status == task.TaskStatusCompleted { 34 | t.FinishedAt = e.OccuredAt() 35 | t.Duration = t.FinishedAt.Sub(t.StartedAt) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/user/delivery.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | ) 7 | 8 | type userDelivery struct { 9 | registerUserUseCase RegisterUserUseCase 10 | } 11 | 12 | func (u *userDelivery) Register(w http.ResponseWriter, r *http.Request) { 13 | var createUserInput CreateUserInput 14 | 15 | err := json.NewDecoder(r.Body).Decode(&createUserInput) 16 | if err != nil { 17 | http.Error(w, err.Error(), http.StatusBadRequest) 18 | return 19 | } 20 | 21 | newUser, err := u.registerUserUseCase.Execute(r.Context(), createUserInput) 22 | if err != nil { 23 | http.Error(w, err.Error(), http.StatusBadRequest) 24 | return 25 | } 26 | 27 | w.Header().Set("Content-Type", "application/json") 28 | w.WriteHeader(http.StatusCreated) 29 | json.NewEncoder(w).Encode(newUser) 30 | } 31 | 32 | func (u *userDelivery) DeleteAccount(w http.ResponseWriter, r *http.Request) { 33 | // TODO 34 | } 35 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | kong-plugin-builder: 5 | build: . 6 | image: kong-plugin-builder 7 | tty: true 8 | stdin_open: true 9 | volumes: 10 | - .:/tmp/go-plugins 11 | kong: 12 | image: kong:2.3.3-alpine 13 | environment: 14 | - KONG_DATABASE=off 15 | - KONG_DECLARATIVE_CONFIG=/usr/local/kong/config.yml 16 | - KONG_PLUGINS=bundled,key-checker 17 | - KONG_PLUGINSERVER_NAMES=key-checker 18 | - KONG_PLUGINSERVER_KEY_CHECKER_START_CMD=/usr/local/kong/go-plugins/bin/key-checker 19 | - KONG_PLUGINSERVER_KEY_CHECKER_QUERY_CMD=/usr/local/kong/go-plugins/bin/key-checker -dump 20 | - KONG_PROXY_LISTEN=0.0.0.0:8000 21 | - KONG_LOG_LEVEL=debug 22 | ports: 23 | - '8000:8000' 24 | restart: on-failure 25 | volumes: 26 | - ./config.yml:/usr/local/kong/config.yml 27 | - .:/usr/local/kong/go-plugins 28 | 29 | volumes: 30 | shared-volume: 31 | 32 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/internal/domain/usecase/keychecker/validate_key_use_case.go: -------------------------------------------------------------------------------- 1 | package keychecker 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | keycheckerentity "github.com/syafdia/demo-kong-plugin/internal/domain/entity/keychecker" 8 | ) 9 | 10 | type ValidateKeyUseCase interface { 11 | Execute(ctx context.Context, forwardInput keycheckerentity.ValidateKeyInput) error 12 | } 13 | 14 | type validateKeyUseCase struct{} 15 | 16 | func NewValidateKeyUseCase() ValidateKeyUseCase { 17 | return &validateKeyUseCase{} 18 | } 19 | 20 | func (v *validateKeyUseCase) Execute(ctx context.Context, forwardInput keycheckerentity.ValidateKeyInput) error { 21 | log.Println("[ValidateKeyUseCase] Start validate key use case") 22 | 23 | if forwardInput.GivenKey == "" { 24 | return keycheckerentity.ErrKeyEmpty 25 | } 26 | 27 | if forwardInput.GivenKey != forwardInput.ValidKey { 28 | return keycheckerentity.ErrKeyNotValid 29 | } 30 | 31 | log.Println("[ValidateKeyUseCase] Key is valid") 32 | 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /src/etc/demo-nats/pubsub/publisher/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/nats-io/nats.go" 10 | "github.com/nats-io/nats.go/jetstream" 11 | ) 12 | 13 | func main() { 14 | nc, err := nats.Connect(nats.DefaultURL) 15 | if err != nil { 16 | panic(err) 17 | } 18 | 19 | js, err := jetstream.New(nc) 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 25 | defer cancel() 26 | 27 | log.Println("[publisher] Will publish some messages") 28 | 29 | js.Publish(ctx, "hi.joe", buildGreetPayload("Joe")) 30 | time.Sleep(1 * time.Second) 31 | 32 | js.Publish(ctx, "hi.john", buildGreetPayload("John")) 33 | time.Sleep(1 * time.Second) 34 | 35 | js.Publish(ctx, "hi.doe", buildGreetPayload("Doe")) 36 | time.Sleep(1 * time.Second) 37 | 38 | log.Println("[publisher] Messages has been published") 39 | } 40 | 41 | func buildGreetPayload(name string) []byte { 42 | return []byte(fmt.Sprintf("Hello %s, %d", name, time.Now().Unix())) 43 | } 44 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/source/business_repo.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/syafdia/xaam/internal/domain/entity" 7 | be "github.com/syafdia/xaam/internal/domain/entity/business" 8 | "github.com/syafdia/xaam/internal/domain/repo" 9 | ) 10 | 11 | type businessRepo struct { 12 | } 13 | 14 | var businessIDWithBusiness = map[string]be.GetOneResponse{ 15 | "abcd-0001-56789-asdf": { 16 | BusinessID: "abcd-0001-56789-asdf", 17 | CountryID: "ID", 18 | IndustryID: 1, 19 | LegalEntityID: 1, 20 | }, 21 | "abcd-0002-56789-asdf": { 22 | BusinessID: "abcd-0002-56789-asdf", 23 | CountryID: "ID", 24 | IndustryID: 2, 25 | LegalEntityID: 2, 26 | }, 27 | } 28 | 29 | func NewBusinessRepo() repo.BusinessRepo { 30 | return &businessRepo{} 31 | } 32 | 33 | func (b *businessRepo) FindOneByBusinessID(ctx context.Context, businessID string) (be.GetOneResponse, error) { 34 | resp, ok := businessIDWithBusiness[businessID] 35 | if !ok { 36 | return be.GetOneResponse{}, entity.ErrNotFound 37 | } 38 | 39 | return resp, nil 40 | } 41 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/source/resource_action_repo.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jmoiron/sqlx" 7 | e "github.com/syafdia/xaam/internal/domain/entity" 8 | "github.com/syafdia/xaam/internal/domain/repo" 9 | ) 10 | 11 | const ( 12 | sqlFindMultipleByIDandTargetType = ` 13 | SELECT 14 | ra.id, 15 | ra.resource_id, 16 | ra.name 17 | FROM target_resource_actions tra 18 | JOIN resource_actions ra ON ra.id = tra.resource_action_id 19 | WHERE tra.target_id = $1 AND tra.target_type = $2; 20 | ` 21 | ) 22 | 23 | type PolicyRepo struct { 24 | db *sqlx.DB 25 | } 26 | 27 | func NewPolicyRepo(db *sqlx.DB) repo.PolicyRepo { 28 | return &PolicyRepo{db: db} 29 | } 30 | 31 | func (r *PolicyRepo) FindMultipleByIDandTargetType( 32 | ctx context.Context, 33 | targetID int64, 34 | targetType e.TargetType, 35 | ) ([]e.Action, error) { 36 | results := []e.Action{} 37 | err := r.db.SelectContext(ctx, &results, sqlFindMultipleByIDandTargetType, targetID, targetType) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | return results, nil 43 | } 44 | -------------------------------------------------------------------------------- /src/distributed/semaphore/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/go-redis/redis/v8" 9 | "github.com/syafdia/distributed/semaphore" 10 | ) 11 | 12 | func main() { 13 | redisClient := redis.NewClient(&redis.Options{ 14 | Addr: "localhost:6379", 15 | }) 16 | 17 | sem, err := semaphore.New(20, "example", redisClient, 2*time.Minute) 18 | if err != nil { 19 | panic(err) 20 | } 21 | 22 | totalProceses := 11 23 | for i := 0; i < totalProceses; i++ { 24 | n := i 25 | go runHeavyProcess(sem, n) 26 | } 27 | 28 | waitForeverC := make(chan bool, 1) 29 | <-waitForeverC 30 | } 31 | 32 | func runHeavyProcess(sem *semaphore.Semaphore, n int) { 33 | ctx := context.Background() 34 | 35 | resource, err := sem.Acquire(ctx) 36 | if err != nil { 37 | fmt.Println("Got error", err) 38 | return 39 | } 40 | 41 | fmt.Println("Heavy process started, id:", n) 42 | time.Sleep(10 * time.Second) 43 | fmt.Println("Heavy process finished, id:", n) 44 | 45 | err = sem.Release(ctx, resource) 46 | if err != nil { 47 | fmt.Println("Got error", err) 48 | return 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/concurrency/workerpool/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "runtime" 6 | "time" 7 | 8 | "github.com/syafdia/go-exercise/src/concurrency/workerpool" 9 | ) 10 | 11 | func main() { 12 | log.SetFlags(log.Ltime) 13 | 14 | // For monitoring purpose. 15 | waitC := make(chan bool) 16 | go func() { 17 | for { 18 | log.Printf("[main] Total current goroutine: %d", runtime.NumGoroutine()) 19 | time.Sleep(1 * time.Second) 20 | } 21 | }() 22 | 23 | // Start Worker Pool. 24 | totalWorker := 5 25 | wp := workerpool.NewWorkerPool(totalWorker) 26 | wp.Run() 27 | 28 | type result struct { 29 | id int 30 | value int 31 | } 32 | 33 | totalTask := 100 34 | resultC := make(chan result, totalTask) 35 | 36 | for i := 0; i < totalTask; i++ { 37 | id := i + 1 38 | wp.AddTask(func() { 39 | log.Printf("[main] Starting task %d", id) 40 | time.Sleep(5 * time.Second) 41 | resultC <- result{id, id * 2} 42 | }) 43 | } 44 | 45 | for i := 0; i < totalTask; i++ { 46 | res := <-resultC 47 | log.Printf("[main] Task %d has been finished with result %d", res.id, res.value) 48 | } 49 | 50 | <-waitC 51 | } 52 | -------------------------------------------------------------------------------- /src/etc/demo-nats/go.sum: -------------------------------------------------------------------------------- 1 | github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= 2 | github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 3 | github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU= 4 | github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= 5 | github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= 6 | github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= 7 | github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= 8 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 9 | golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= 10 | golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= 11 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 12 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 13 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 14 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 15 | -------------------------------------------------------------------------------- /src/concurrency/pipeline/pipeline_test.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_pipeline_Pipe(t *testing.T) { 8 | type fields struct { 9 | dataC chan interface{} 10 | errC chan error 11 | executors []Executor 12 | } 13 | type args struct { 14 | executor Executor 15 | } 16 | tests := []struct { 17 | name string 18 | fields fields 19 | args args 20 | check func(f fields) bool 21 | }{ 22 | { 23 | name: "should success adding executor to executors", 24 | fields: fields{ 25 | executors: []Executor{}, 26 | }, 27 | args: args{ 28 | executor: func(in interface{}) (interface{}, error) { 29 | return 1, nil 30 | }, 31 | }, 32 | check: func(f fields) bool { 33 | return len(f.executors) == 1 34 | }, 35 | }, 36 | } 37 | for _, tt := range tests { 38 | t.Run(tt.name, func(t *testing.T) { 39 | p := &pipeline{ 40 | dataC: tt.fields.dataC, 41 | errC: tt.fields.errC, 42 | executors: tt.fields.executors, 43 | } 44 | p.Pipe(tt.args.executor) 45 | if !tt.check(fields{p.dataC, p.errC, p.executors}) { 46 | t.Errorf("pipeline.Pipe() not run as expected") 47 | } 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/article/usecase.go: -------------------------------------------------------------------------------- 1 | package article 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/syafdia/demo-unit-test/internal/user" 7 | ) 8 | 9 | type WriteArticleUseCase interface { 10 | Execute(ctx context.Context, input CreateArticleInput) (ArticleWithAuthor, error) 11 | } 12 | 13 | type writeArticleUseCase struct { 14 | articleRepo ArticleRepo 15 | userRepo user.UserRepo 16 | } 17 | 18 | func NewWriteArticleUseCase( 19 | articleRepo ArticleRepo, 20 | userRepo user.UserRepo, 21 | ) WriteArticleUseCase { 22 | return &writeArticleUseCase{ 23 | articleRepo: articleRepo, 24 | userRepo: userRepo, 25 | } 26 | } 27 | 28 | func (w *writeArticleUseCase) Execute( 29 | ctx context.Context, 30 | input CreateArticleInput, 31 | ) (ArticleWithAuthor, error) { 32 | article, err := w.articleRepo.Create(ctx, input) 33 | if err != nil { 34 | return ArticleWithAuthor{}, err 35 | } 36 | 37 | author, err := w.userRepo.FindOneByID(ctx, article.UserID) 38 | if err != nil { 39 | return ArticleWithAuthor{}, err 40 | } 41 | 42 | return ArticleWithAuthor{ 43 | Article: article, 44 | UserFirstName: author.FirstName, 45 | UserLastName: author.LastName, 46 | }, nil 47 | } 48 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/application/projection_handler.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/syafdia/demo-es/internal/domain" 7 | "github.com/syafdia/demo-es/internal/domain/report" 8 | "github.com/syafdia/demo-es/internal/domain/task" 9 | ) 10 | 11 | type ProjectionHandler struct { 12 | reportRepository report.ReportRepository 13 | } 14 | 15 | func (c *ProjectionHandler) ReceiveEvent(ctx context.Context, event domain.Event) error { 16 | switch e := event.(type) { 17 | case task.TaskCreated: 18 | tr := report.NewTaskReport() 19 | 20 | tr.TaskID = e.ID 21 | tr.TaskTitle = e.Title 22 | tr.StartedAt = e.OccuredAt() 23 | 24 | err := c.reportRepository.Store(ctx, tr) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | case task.TaskStatusChanged: 30 | tr, err := c.reportRepository.FindByTaskID(ctx, e.ID) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | if e.Status != task.TaskStatusCompleted { 36 | break 37 | } 38 | 39 | tr.FinishedAt = e.OccuredAt() 40 | tr.Duration = tr.FinishedAt.Sub(tr.StartedAt) 41 | 42 | err = c.reportRepository.Update(ctx, tr) 43 | if err != nil { 44 | return err 45 | } 46 | } 47 | 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /src/etc/demo-nats/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | services: 3 | nats-1: 4 | container_name: nats-1 5 | image: nats 6 | entrypoint: /nats-server 7 | command: --server_name N1 --cluster_name JSC --js --sd /data --cluster nats://0.0.0.0:4245 --routes nats://nats-2:4245,nats://nats-3:4245 -p 4222 8 | networks: 9 | - demo-nats-network 10 | ports: 11 | - 4222:4222 12 | 13 | nats-2: 14 | container_name: nats-2 15 | image: nats 16 | entrypoint: /nats-server 17 | command: --server_name N2 --cluster_name JSC --js --sd /data --cluster nats://0.0.0.0:4245 --routes nats://nats-1:4245,nats://nats-3:4245 -p 4222 18 | networks: 19 | - demo-nats-network 20 | ports: 21 | - 4223:4222 22 | 23 | nats-3: 24 | container_name: nats-3 25 | image: nats 26 | entrypoint: /nats-server 27 | command: --server_name N3 --cluster_name JSC --js --sd /data --cluster nats://0.0.0.0:4245 --routes nats://nats-1:4245,nats://nats-2:4245 -p 4222 28 | networks: 29 | - demo-nats-network 30 | ports: 31 | - 4224:4222 32 | 33 | networks: 34 | demo-nats-network: 35 | driver: bridge 36 | ipam: 37 | config: 38 | - subnet: 172.22.0.0/24 39 | volumes: 40 | hydra-sqlite: -------------------------------------------------------------------------------- /src/concurrency/workerpool/worker_pool.go: -------------------------------------------------------------------------------- 1 | package workerpool 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | // WorkerPool is a contract for Worker Pool implementation 8 | type WorkerPool interface { 9 | Run() 10 | AddTask(task func()) 11 | } 12 | 13 | type workerPool struct { 14 | maxWorker int 15 | queuedTaskC chan func() 16 | } 17 | 18 | // NewWorkerPool will create an instance of WorkerPool. 19 | func NewWorkerPool(maxWorker int) WorkerPool { 20 | wp := &workerPool{ 21 | maxWorker: maxWorker, 22 | queuedTaskC: make(chan func()), 23 | } 24 | 25 | return wp 26 | } 27 | 28 | func (wp *workerPool) Run() { 29 | wp.run() 30 | } 31 | 32 | func (wp *workerPool) AddTask(task func()) { 33 | wp.queuedTaskC <- task 34 | } 35 | 36 | func (wp *workerPool) GetTotalQueuedTask() int { 37 | return len(wp.queuedTaskC) 38 | } 39 | 40 | func (wp *workerPool) run() { 41 | for i := 0; i < wp.maxWorker; i++ { 42 | wID := i + 1 43 | log.Printf("[WorkerPool] Worker %d has been spawned", wID) 44 | 45 | go func(workerID int) { 46 | for task := range wp.queuedTaskC { 47 | log.Printf("[WorkerPool] Worker %d start processing task", wID) 48 | task() 49 | log.Printf("[WorkerPool] Worker %d finish processing task", wID) 50 | } 51 | }(wID) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/etc/XAAM/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/xaam 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.7.7 7 | github.com/jmoiron/sqlx v1.3.4 8 | ) 9 | 10 | require ( 11 | github.com/google/go-querystring v1.1.0 // indirect 12 | github.com/joho/godotenv v1.4.0 // indirect 13 | ) 14 | 15 | require ( 16 | github.com/dghubble/sling v1.4.0 17 | github.com/gin-contrib/sse v0.1.0 // indirect 18 | github.com/go-playground/locales v0.13.0 // indirect 19 | github.com/go-playground/universal-translator v0.17.0 // indirect 20 | github.com/go-playground/validator/v10 v10.4.1 // indirect 21 | github.com/golang/mock v1.6.0 22 | github.com/golang/protobuf v1.3.3 // indirect 23 | github.com/json-iterator/go v1.1.9 // indirect 24 | github.com/leodido/go-urn v1.2.0 // indirect 25 | github.com/lib/pq v1.10.4 26 | github.com/mattn/go-isatty v0.0.12 // indirect 27 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect 28 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect 29 | github.com/ugorji/go/codec v1.1.7 // indirect 30 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect 31 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect 32 | gopkg.in/yaml.v2 v2.2.8 // indirect 33 | ) 34 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/centralized-log/logstash.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: logstash.k8s.elastic.co/v1alpha1 2 | kind: Logstash 3 | metadata: 4 | name: centralized-log-ls 5 | namespace: centralized-log 6 | spec: 7 | count: 1 8 | version: 8.9.0 9 | elasticsearchRefs: 10 | - clusterName: centralized-log-es 11 | name: default-nodes 12 | pipelines: 13 | - pipeline.id: main 14 | config.string: | 15 | input { 16 | beats { 17 | port => 5044 18 | } 19 | } 20 | filter { 21 | grok { 22 | match => { "message" => "%{HTTPD_COMMONLOG}"} 23 | } 24 | geoip { 25 | source => "[source][address]" 26 | target => "[source]" 27 | } 28 | } 29 | output { 30 | elasticsearch { 31 | hosts => [ "${ECK_ES_HOSTS}" ] 32 | user => "${ECK_ES_USER}" 33 | password => "${ECK_ES_PASSWORD}" 34 | ssl_certificate_authorities => "${ECK_ES_SSL_CERTIFICATE_AUTHORITY}" 35 | } 36 | } 37 | services: 38 | - name: beats 39 | service: 40 | spec: 41 | type: ClusterIP 42 | ports: 43 | - port: 5044 44 | targetPort: 5044 -------------------------------------------------------------------------------- /src/etc/demo-oauth2/internal/auth/handlers.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | type AuthHandler struct { 10 | repo *Repo 11 | client *Client 12 | } 13 | 14 | func NewAuthHandler(repo *Repo, client *Client) *AuthHandler { 15 | return &AuthHandler{ 16 | repo: repo, 17 | client: client, 18 | } 19 | } 20 | 21 | func (a *AuthHandler) NewSession(c *gin.Context) { 22 | c.HTML(http.StatusOK, "session.new", map[string]interface{}{ 23 | "LoginChallenge": c.Query("login_challenge"), 24 | }) 25 | } 26 | 27 | func (a *AuthHandler) CreateSession(c *gin.Context) { 28 | username := c.Request.FormValue("username") 29 | password := c.Request.FormValue("password") 30 | // loginChallenge := c.Request.FormValue("login_challenge") 31 | 32 | user, err := a.repo.User.FindByUsernameAndPassword(c, username, password) 33 | if err != nil { 34 | c.JSON(http.StatusUnauthorized, map[string]interface{}{ 35 | "error": err, 36 | }) 37 | return 38 | } 39 | 40 | c.JSON(http.StatusOK, map[string]interface{}{ 41 | // TODO: render token 42 | "username": user.Username, 43 | }) 44 | } 45 | 46 | func (a *AuthHandler) NewConsent(c *gin.Context) { 47 | 48 | } 49 | 50 | func (a *AuthHandler) CreateConsent(c *gin.Context) { 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/k8s/demo-helm/charts/calculator/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: calculator 3 | description: A Helm chart for Kubernetes for deploying go-calculator app 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | # It is recommended to use it with quotes. 24 | appVersion: "1.16.0" 25 | -------------------------------------------------------------------------------- /src/concurrency/pipeline/pipeline.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | type Executor func(interface{}) (interface{}, error) 4 | 5 | type Pipeline interface { 6 | Pipe(executor Executor) Pipeline 7 | Merge() <-chan interface{} 8 | } 9 | 10 | type pipeline struct { 11 | dataC chan interface{} 12 | errC chan error 13 | executors []Executor 14 | } 15 | 16 | func New(f func(chan interface{})) Pipeline { 17 | inC := make(chan interface{}) 18 | 19 | go f(inC) 20 | 21 | return &pipeline{ 22 | dataC: inC, 23 | errC: make(chan error), 24 | executors: []Executor{}, 25 | } 26 | } 27 | 28 | func (p *pipeline) Pipe(executor Executor) Pipeline { 29 | p.executors = append(p.executors, executor) 30 | 31 | return p 32 | } 33 | 34 | func (p *pipeline) Merge() <-chan interface{} { 35 | for i := 0; i < len(p.executors); i++ { 36 | p.dataC, p.errC = run(p.dataC, p.executors[i]) 37 | } 38 | 39 | return p.dataC 40 | } 41 | 42 | func run(inC <-chan interface{}, f Executor) (chan interface{}, chan error) { 43 | outC := make(chan interface{}) 44 | errC := make(chan error) 45 | 46 | go func() { 47 | defer close(outC) 48 | for v := range inC { 49 | res, err := f(v) 50 | if err != nil { 51 | errC <- err 52 | continue 53 | } 54 | 55 | outC <- res 56 | } 57 | }() 58 | 59 | return outC, errC 60 | } 61 | -------------------------------------------------------------------------------- /src/etc/XAAM/files/sql/0001.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE resources ( 2 | id SERIAL PRIMARY KEY, 3 | name VARCHAR (64) UNIQUE NOT NULL, 4 | created_at TIMESTAMP NOT NULL, 5 | updated_at TIMESTAMP 6 | ); 7 | 8 | CREATE TABLE resource_actions ( 9 | id SERIAL PRIMARY KEY, 10 | name VARCHAR (64) NOT NULL, 11 | resource_id BIGINT NOT NULL, 12 | created_at TIMESTAMP NOT NULL, 13 | updated_at TIMESTAMP 14 | ); 15 | 16 | CREATE INDEX idx_resource_id ON resource_actions(resource_id); 17 | CREATE INDEX idx_name ON resource_actions(name); 18 | 19 | ALTER TABLE resource_actions ADD CONSTRAINT fk_resource_id FOREIGN KEY (resource_id) REFERENCES resources(id); 20 | 21 | CREATE TYPE TARGET_TYPE AS ENUM ('legal_entity', 'industry'); 22 | 23 | CREATE TABLE target_resource_actions ( 24 | id SERIAL PRIMARY KEY, 25 | resource_action_id BIGINT NOT NULL, 26 | target_id BIGINT NOT NULL, 27 | target_type TARGET_TYPE NOT NULL, 28 | created_at TIMESTAMP NOT NULL, 29 | updated_at TIMESTAMP 30 | ); 31 | 32 | CREATE INDEX idx_resource_action_id ON target_resource_actions(resource_action_id); 33 | CREATE INDEX idx_target_id ON target_resource_actions(target_id); 34 | CREATE INDEX idx_target_type ON target_resource_actions(target_type); 35 | 36 | ALTER TABLE target_resource_actions ADD CONSTRAINT fk_resource_action_id FOREIGN KEY (resource_action_id) REFERENCES resource_actions(id); -------------------------------------------------------------------------------- /src/etc/demo-nats/pubsub/consumer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/nats-io/nats.go" 10 | "github.com/nats-io/nats.go/jetstream" 11 | ) 12 | 13 | func main() { 14 | nc, err := nats.Connect(nats.DefaultURL) 15 | handleError(err) 16 | 17 | js, err := jetstream.New(nc) 18 | handleError(err) 19 | 20 | ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 21 | defer cancel() 22 | 23 | // get existing stream handle 24 | s, err := js.CreateOrUpdateStream(ctx, jetstream.StreamConfig{ 25 | Name: "HI_STREAM", 26 | Subjects: []string{"hi.*"}, 27 | }) 28 | handleError(err) 29 | 30 | // retrieve consumer handle from a stream 31 | cons, err := s.CreateOrUpdateConsumer(ctx, jetstream.ConsumerConfig{ 32 | Durable: "HI_CONSUMER", 33 | AckPolicy: jetstream.AckExplicitPolicy, 34 | }) 35 | handleError(err) 36 | 37 | log.Println("[publisher] Will consume from callback") 38 | 39 | cc, err := cons.Consume(func(msg jetstream.Msg) { 40 | log.Println("[consumer] Receive message via consume:", string(msg.Data())) 41 | msg.Ack() 42 | }) 43 | handleError(err) 44 | 45 | defer cc.Stop() 46 | 47 | waitC := make(chan int, 1) 48 | <-waitC 49 | } 50 | 51 | func handleError(err error) { 52 | if err != nil { 53 | panic(fmt.Sprintf("[consumer]: got error, error: %s", err)) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/concurrency/pipeline/example/e00/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/syafdia/go-exercise/src/concurrency/pipeline" 8 | "github.com/syafdia/go-exercise/src/concurrency/pipeline/example" 9 | ) 10 | 11 | func main() { 12 | N := 5 13 | startTime := time.Now() 14 | 15 | for i := 0; i < N; i++ { 16 | result := example.AddFoo(example.AddQuoute(example.Square(example.MultiplyTwo(i)))) 17 | fmt.Printf("Result: %s\n", result) 18 | } 19 | 20 | fmt.Printf("Elapsed time without concurrency: %s", time.Since(startTime)) // ~40 seconds 21 | 22 | outC := pipeline.New(func(inC chan interface{}) { 23 | defer close(inC) 24 | for i := 0; i < N; i++ { 25 | inC <- i 26 | } 27 | }). 28 | Pipe(func(in interface{}) (interface{}, error) { 29 | return example.MultiplyTwo(in.(int)), nil 30 | }). 31 | Pipe(func(in interface{}) (interface{}, error) { 32 | return example.Square(in.(int)), nil 33 | }). 34 | Pipe(func(in interface{}) (interface{}, error) { 35 | return example.AddQuoute(in.(int)), nil 36 | }). 37 | Pipe(func(in interface{}) (interface{}, error) { 38 | return example.AddFoo(in.(string)), nil 39 | }). 40 | Merge() 41 | 42 | startTimeC := time.Now() 43 | for result := range outC { 44 | fmt.Printf("Result: %s\n", result) 45 | } 46 | 47 | fmt.Printf("Elapsed time with concurrency: %s", time.Since(startTimeC)) // ~16 seconds 48 | } 49 | -------------------------------------------------------------------------------- /src/concurrency/pipeline/pipeline_bench_test.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func BenchmarkWithPipelineModule(b *testing.B) { 10 | outC := New(func(inC chan interface{}) { 11 | defer close(inC) 12 | for i := 0; i < b.N; i++ { 13 | inC <- i 14 | } 15 | }). 16 | Pipe(func(in interface{}) (interface{}, error) { 17 | return multiplyTwo(in.(int)), nil 18 | }). 19 | Pipe(func(in interface{}) (interface{}, error) { 20 | return square(in.(int)), nil 21 | }). 22 | Pipe(func(in interface{}) (interface{}, error) { 23 | return addQuoute(in.(int)), nil 24 | }). 25 | Pipe(func(in interface{}) (interface{}, error) { 26 | return addFoo(in.(string)), nil 27 | }). 28 | Merge() 29 | 30 | for range outC { 31 | // Do nothing, just for drain out channel 32 | } 33 | } 34 | 35 | func BenchmarkWithoutPipelineModule(b *testing.B) { 36 | for i := 0; i < b.N; i++ { 37 | addFoo(addQuoute(square(multiplyTwo(i)))) 38 | } 39 | } 40 | 41 | func multiplyTwo(v int) int { 42 | time.Sleep(100 * time.Millisecond) 43 | return v * 2 44 | } 45 | 46 | func square(v int) int { 47 | time.Sleep(200 * time.Millisecond) 48 | return v * v 49 | } 50 | 51 | func addQuoute(v int) string { 52 | time.Sleep(100 * time.Millisecond) 53 | return fmt.Sprintf("'%d'", v) 54 | } 55 | 56 | func addFoo(v string) string { 57 | time.Sleep(200 * time.Millisecond) 58 | return fmt.Sprintf("%s - Foo", v) 59 | } 60 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/clusters/development/infrastructure.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.toolkit.fluxcd.io/v1 2 | kind: Kustomization 3 | metadata: 4 | name: elastic-system 5 | namespace: flux-system 6 | spec: 7 | interval: 1m0s 8 | sourceRef: 9 | kind: GitRepository 10 | name: flux-system 11 | path: ./src/k8s/demo-flux/infrastructure/base/elastic-system 12 | prune: true 13 | --- 14 | # Temporarily disable this apps since thhis apps consume large amount of memory. 15 | # 16 | # apiVersion: kustomize.toolkit.fluxcd.io/v1 17 | # kind: Kustomization 18 | # metadata: 19 | # name: centralized-log 20 | # namespace: flux-system 21 | # spec: 22 | # interval: 1m0s 23 | # sourceRef: 24 | # kind: GitRepository 25 | # name: flux-system 26 | # path: ./src/k8s/demo-flux/infrastructure/base/centralized-log 27 | # prune: true 28 | # --- 29 | apiVersion: kustomize.toolkit.fluxcd.io/v1 30 | kind: Kustomization 31 | metadata: 32 | name: gloo-system 33 | namespace: flux-system 34 | spec: 35 | interval: 1m0s 36 | sourceRef: 37 | kind: GitRepository 38 | name: flux-system 39 | path: ./src/k8s/demo-flux/infrastructure/base/gloo-system 40 | prune: true 41 | --- 42 | apiVersion: kustomize.toolkit.fluxcd.io/v1 43 | kind: Kustomization 44 | metadata: 45 | name: flagger 46 | namespace: flux-system 47 | spec: 48 | interval: 1m0s 49 | sourceRef: 50 | kind: GitRepository 51 | name: flux-system 52 | path: ./src/k8s/demo-flux/infrastructure/base/flagger 53 | prune: true -------------------------------------------------------------------------------- /src/etc/demo-oauth2/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | services: 3 | 4 | hydra-postgres: 5 | image: 'postgres:15' 6 | environment: 7 | POSTGRES_USER: postgres 8 | POSTGRES_PASSWORD: postgres 9 | ports: 10 | - '5432:5432' 11 | tty: true 12 | volumes: 13 | - ./volumes/postgresql/var/lib/postgresql/data:/var/lib/postgresql/data 14 | 15 | hydra-migrate: 16 | image: 'oryd/hydra:v2.3.0' 17 | restart: on-failure 18 | command: 19 | migrate sql -e --yes 20 | environment: 21 | - DSN=postgres://postgres:postgres@hydra-postgres:5432/postgres?sslmode=disable&max_conns=20&max_idle_conns=4 22 | depends_on: 23 | - hydra-postgres 24 | 25 | hydra-core: 26 | image: 'oryd/hydra:v2.3.0' 27 | environment: 28 | - DSN=postgres://postgres:postgres@hydra-postgres:5432/postgres?sslmode=disable&max_conns=20&max_idle_conns=4 29 | - SECRETS_SYSTEM=my-secret-my-secret-my-secret 30 | - URLS_LOGIN=http://localhost:9001/auth/session/new # Sets the login endpoint of the User Login & Consent flow. 31 | - URLS_CONSENT=http://localhost:9001/auth/consent/new # Sets the consent endpoint of the User Login & Consent flow. 32 | - SERVE_PUBLIC_PORT=4444 33 | - SERVE_PUBLIC_HOST=0.0.0.0 34 | - SERVE_PUBLIC_CORS_ENABLED=true 35 | - SERVE_ADMIN_PORT=4445 36 | - LOG_LEVEL=debug 37 | ports: 38 | - '4444:4444' # Public port 39 | - '4445:4445' # Admin port 40 | - '5555:5555' # Port for debug app 41 | tty: true -------------------------------------------------------------------------------- /src/etc/XAAM/e2e/api_v1_authotrisation_check_test.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/dghubble/sling" 10 | ) 11 | 12 | func Test_PostAPIV1AuthorisationCheck_400(t *testing.T) { 13 | client := &http.Client{} 14 | req, _ := sling.New().Post("http://0.0.0.0:8080/api/v1/authorisation/check").Request() 15 | 16 | resp, _ := client.Do(req) 17 | defer resp.Body.Close() 18 | 19 | if !reflect.DeepEqual(resp.StatusCode, http.StatusBadRequest) { 20 | t.Errorf("http status is not 400, got %d", resp.StatusCode) 21 | } 22 | } 23 | 24 | func Test_PostAPIV1AuthorisationCheck_200(t *testing.T) { 25 | client := &http.Client{} 26 | req, _ := sling.New(). 27 | Post("http://0.0.0.0:8080/api/v1/authorisation/check"). 28 | BodyJSON(map[string]interface{}{ 29 | "actions": []string{"compliant"}, 30 | "resource": map[string]interface{}{ 31 | "kind": "VA", 32 | }, 33 | "principal": map[string]interface{}{ 34 | "kind": "BUSINESS_ID", 35 | "id": "abcd-0001-56789-asdf", 36 | }, 37 | }). 38 | Request() 39 | 40 | resp, _ := client.Do(req) 41 | defer resp.Body.Close() 42 | 43 | if !reflect.DeepEqual(resp.StatusCode, http.StatusOK) { 44 | t.Errorf("http status is not 200, got %d", resp.StatusCode) 45 | } 46 | 47 | rawBody, _ := ioutil.ReadAll(resp.Body) 48 | expectedBody := `{"results":[{"kind":"VA","actions":{"compliant":"EFFECT_ALLOWED"}}]}` 49 | 50 | if !reflect.DeepEqual(string(rawBody), expectedBody) { 51 | t.Errorf("response body is not valid,, got %s", expectedBody) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/user/repo.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jmoiron/sqlx" 7 | ) 8 | 9 | type UserRepo interface { 10 | Create(ctx context.Context, input CreateUserInput) (User, error) 11 | Update(ctx context.Context, input UpdateUserInput) (User, error) 12 | FindOneByID(ctx context.Context, id int64) (User, error) 13 | Destroy(ctx context.Context, id int64) error 14 | } 15 | 16 | type userRepo struct { 17 | db *sqlx.DB 18 | } 19 | 20 | func NewUserRepo(db *sqlx.DB) UserRepo { 21 | return &userRepo{db: db} 22 | } 23 | 24 | func (u *userRepo) Create(ctx context.Context, input CreateUserInput) (User, error) { 25 | result, err := u.db.ExecContext(ctx, 26 | `INSERT INTO users (email, first_name, last_name, gender) VALUES ($1, $2, $3, $4);`, 27 | input.Email, 28 | input.FirstName, 29 | input.LastName, 30 | input.Gender) 31 | if err != nil { 32 | return User{}, err 33 | } 34 | 35 | userID, err := result.LastInsertId() 36 | if err != nil { 37 | return User{}, err 38 | } 39 | 40 | return User{ 41 | ID: userID, 42 | Email: input.Email, 43 | FirstName: input.FirstName, 44 | LastName: input.LastName, 45 | Gender: input.Gender, 46 | }, nil 47 | } 48 | 49 | func (u *userRepo) Update(ctx context.Context, input UpdateUserInput) (User, error) { 50 | // TODO 51 | return User{}, nil 52 | } 53 | 54 | func (u *userRepo) FindOneByID(ctx context.Context, id int64) (User, error) { 55 | return User{}, nil 56 | } 57 | 58 | func (u *userRepo) Destroy(ctx context.Context, id int64) error { 59 | // TODO 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/delivery/auth_check_handler.go: -------------------------------------------------------------------------------- 1 | package delivery 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/syafdia/xaam/internal/di" 8 | "github.com/syafdia/xaam/internal/domain/entity" 9 | ae "github.com/syafdia/xaam/internal/domain/entity/auth" 10 | ) 11 | 12 | func PostAuthorisationHandler(useCaseModule di.UseCaseModule) gin.HandlerFunc { 13 | return func(c *gin.Context) { 14 | request := ae.CheckRequest{} 15 | 16 | err := c.BindJSON(&request) 17 | if err != nil { 18 | handleError(c, entity.WrapErr(http.StatusBadRequest, err)) 19 | return 20 | } 21 | 22 | business, err := useCaseModule.FindOneByBusinessIDUseCase.Execute(c, request.Principal.ID) 23 | if err != nil { 24 | handleError(c, err) 25 | return 26 | } 27 | 28 | resourceWithActions, err := useCaseModule.FindResourcesByComplianceUseCase.Execute(c, ae.FindResourcesByComplianceRequest{ 29 | IndustryID: business.IndustryID, 30 | LegalEntityID: business.LegalEntityID, 31 | Resources: []string{request.Resource.Kind}, 32 | }) 33 | if err != nil { 34 | handleError(c, err) 35 | return 36 | } 37 | 38 | response := ae.CheckResponse{} 39 | 40 | for resource, resourceActions := range resourceWithActions { 41 | actions := map[string]string{} 42 | for _, ra := range resourceActions { 43 | actions[ra.Name] = "EFFECT_ALLOWED" 44 | } 45 | 46 | response.Results = append(response.Results, ae.CheckResultResponse{ 47 | Kind: resource.Name, 48 | Actions: actions, 49 | }) 50 | } 51 | 52 | c.JSON(http.StatusOK, response) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/etc/demo-oauth2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/demo-oauth2 2 | 3 | go 1.23.4 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.10.0 7 | github.com/ory/hydra-client-go v1.11.8 8 | golang.org/x/oauth2 v0.28.0 9 | ) 10 | 11 | require ( 12 | github.com/bytedance/sonic v1.11.6 // indirect 13 | github.com/bytedance/sonic/loader v0.1.1 // indirect 14 | github.com/cloudwego/base64x v0.1.4 // indirect 15 | github.com/cloudwego/iasm v0.2.0 // indirect 16 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 17 | github.com/gin-contrib/sse v0.1.0 // indirect 18 | github.com/go-playground/locales v0.14.1 // indirect 19 | github.com/go-playground/universal-translator v0.18.1 // indirect 20 | github.com/go-playground/validator/v10 v10.20.0 // indirect 21 | github.com/goccy/go-json v0.10.2 // indirect 22 | github.com/json-iterator/go v1.1.12 // indirect 23 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 24 | github.com/leodido/go-urn v1.4.0 // indirect 25 | github.com/mattn/go-isatty v0.0.20 // indirect 26 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 27 | github.com/modern-go/reflect2 v1.0.2 // indirect 28 | github.com/pelletier/go-toml/v2 v2.2.2 // indirect 29 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 30 | github.com/ugorji/go/codec v1.2.12 // indirect 31 | golang.org/x/arch v0.8.0 // indirect 32 | golang.org/x/crypto v0.23.0 // indirect 33 | golang.org/x/net v0.25.0 // indirect 34 | golang.org/x/sys v0.20.0 // indirect 35 | golang.org/x/text v0.15.0 // indirect 36 | google.golang.org/protobuf v1.34.1 // indirect 37 | gopkg.in/yaml.v3 v3.0.1 // indirect 38 | ) 39 | -------------------------------------------------------------------------------- /src/etc/demo-redlock/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | 8 | redis "github.com/go-redis/redis/v8" 9 | demoredlock "github.com/syafdia/demo-redlock" 10 | ) 11 | 12 | func main() { 13 | log.SetFlags(log.Ltime) 14 | rc1 := redis.NewClient(&redis.Options{Addr: "0.0.0.0:7001"}) 15 | rc2 := redis.NewClient(&redis.Options{Addr: "0.0.0.0:7002"}) 16 | rc3 := redis.NewClient(&redis.Options{Addr: "0.0.0.0:7003"}) 17 | 18 | dlm := demoredlock.NewDLM([]*redis.Client{rc1, rc2, rc3}, 10*time.Second, 2*time.Second) 19 | 20 | withLockOnly(dlm) 21 | } 22 | 23 | func withLockAndUnlock(dlm *demoredlock.DLM) { 24 | ctx := context.Background() 25 | locker := dlm.NewLocker("this-is-a-key-002") 26 | 27 | if err := locker.Lock(ctx); err != nil { 28 | log.Fatal("[main] Failed when locking, err:", err) 29 | } 30 | 31 | // Perform operation. 32 | someOperation() 33 | 34 | if err := locker.Unlock(ctx); err != nil { 35 | log.Fatal("[main] Failed when unlocking, err:", err) 36 | } 37 | 38 | log.Println("[main] Done") 39 | } 40 | 41 | func withLockOnly(dlm *demoredlock.DLM) { 42 | ctx := context.Background() 43 | locker := dlm.NewLocker("this-is-a-key-002") 44 | 45 | if err := locker.Lock(ctx); err != nil { 46 | log.Fatal("[main] Failed when locking, err:", err) 47 | } 48 | 49 | // Perform operation. 50 | someOperation() 51 | 52 | // Don't unlock 53 | 54 | log.Println("[main] Done") 55 | } 56 | 57 | func someOperation() { 58 | log.Println("[someOperation] Process has been started") 59 | time.Sleep(1 * time.Second) 60 | log.Println("[someOperation] Process has been finished") 61 | } 62 | -------------------------------------------------------------------------------- /src/etc/go-calculator/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/syafdia/go-exercise/src/etc/go-calculator 2 | 3 | go 1.20 4 | 5 | require github.com/gin-gonic/gin v1.9.0 6 | 7 | require ( 8 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 9 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 10 | ) 11 | 12 | require ( 13 | github.com/bytedance/sonic v1.8.0 // indirect 14 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 15 | github.com/gin-contrib/sse v0.1.0 // indirect 16 | github.com/go-playground/locales v0.14.1 // indirect 17 | github.com/go-playground/universal-translator v0.18.1 // indirect 18 | github.com/go-playground/validator/v10 v10.11.2 // indirect 19 | github.com/goccy/go-json v0.10.0 // indirect 20 | github.com/gorilla/websocket v1.5.3 21 | github.com/json-iterator/go v1.1.12 // indirect 22 | github.com/klauspost/cpuid/v2 v2.0.9 // indirect 23 | github.com/leodido/go-urn v1.2.1 // indirect 24 | github.com/mattn/go-isatty v0.0.17 // indirect 25 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect 26 | github.com/modern-go/reflect2 v1.0.2 // indirect 27 | github.com/pelletier/go-toml/v2 v2.0.6 // indirect 28 | github.com/redis/go-redis/v9 v9.0.5 29 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 30 | github.com/ugorji/go/codec v1.2.9 // indirect 31 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect 32 | golang.org/x/crypto v0.5.0 // indirect 33 | golang.org/x/net v0.7.0 // indirect 34 | golang.org/x/sys v0.5.0 // indirect 35 | golang.org/x/text v0.7.0 // indirect 36 | google.golang.org/protobuf v1.28.1 // indirect 37 | gopkg.in/yaml.v3 v3.0.1 // indirect 38 | ) 39 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/domain/task/events.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/syafdia/demo-es/internal/domain" 7 | ) 8 | 9 | type TaskCreated struct { 10 | ID domain.ID `json:"-"` 11 | Timestamp time.Time `json:"-"` 12 | Title string 13 | Description string 14 | Status TaskStatus 15 | } 16 | 17 | func (t TaskCreated) AggregateID() domain.ID { 18 | return t.ID 19 | } 20 | 21 | func (t TaskCreated) OccuredAt() time.Time { 22 | return t.Timestamp 23 | } 24 | 25 | type TaskStatusChanged struct { 26 | ID domain.ID `json:"-"` 27 | Timestamp time.Time `json:"-"` 28 | Status TaskStatus 29 | } 30 | 31 | func (t TaskStatusChanged) AggregateID() domain.ID { 32 | return t.ID 33 | } 34 | 35 | func (t TaskStatusChanged) OccuredAt() time.Time { 36 | return t.Timestamp 37 | } 38 | 39 | type TaskTagAdded struct { 40 | ID domain.ID `json:"-"` 41 | Timestamp time.Time `json:"-"` 42 | Tag Tag 43 | } 44 | 45 | func (t TaskTagAdded) AggregateID() domain.ID { 46 | return t.ID 47 | } 48 | 49 | func (t TaskTagAdded) OccuredAt() time.Time { 50 | return t.Timestamp 51 | } 52 | 53 | type TaskUpvoted struct { 54 | ID domain.ID `json:"-"` 55 | Timestamp time.Time `json:"-"` 56 | } 57 | 58 | func (t TaskUpvoted) AggregateID() domain.ID { 59 | return t.ID 60 | } 61 | 62 | func (t TaskUpvoted) OccuredAt() time.Time { 63 | return t.Timestamp 64 | } 65 | 66 | type TaskDownvoted struct { 67 | ID domain.ID `json:"-"` 68 | Timestamp time.Time `json:"-"` 69 | } 70 | 71 | func (t TaskDownvoted) AggregateID() domain.ID { 72 | return t.ID 73 | } 74 | 75 | func (t TaskDownvoted) OccuredAt() time.Time { 76 | return t.Timestamp 77 | } 78 | -------------------------------------------------------------------------------- /src/etc/demo-oauth2/cmd/pokedex/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "net/http" 6 | 7 | "github.com/gin-gonic/gin" 8 | "github.com/syafdia/demo-oauth2/internal/pokedex" 9 | "golang.org/x/oauth2" 10 | ) 11 | 12 | func newOauth2Client() *oauth2.Config { 13 | return &oauth2.Config{ 14 | RedirectURL: "http://localhost:9002/callbacks", // Pokedex App callbacks URL 15 | 16 | // For testing purpose, we hardcoded the client_id & client_secret 17 | ClientID: "client-id-my-pokedex", 18 | ClientSecret: "client-secret-my-pokedex", 19 | 20 | Scopes: []string{"offline", "pokemon:all"}, 21 | Endpoint: oauth2.Endpoint{ 22 | AuthURL: "http://localhost:4444/oauth2/auth", 23 | TokenURL: "http://localhost:4444/oauth2/token", 24 | }, 25 | } 26 | } 27 | 28 | func setupHTMLTemplate(r *gin.Engine) { 29 | r.SetHTMLTemplate(template.Must(template.New("pokemons.list").Parse(` 30 | 31 | 32 | 33 | 34 | 35 | List Pokemon 36 | 37 | 38 |

Please Sign In First

39 |
40 | Go to this URL to Sign In: 41 |
42 | {{ .AuthCodeURL }} 43 | 44 | 45 | 46 | `))) 47 | } 48 | 49 | func main() { 50 | c := pokedex.NewClient(newOauth2Client()) 51 | 52 | h := pokedex.NewPokedexhandler(c) 53 | 54 | r := gin.Default() 55 | setupHTMLTemplate(r) 56 | 57 | r.GET("/ping", func(c *gin.Context) { 58 | c.JSON(http.StatusOK, gin.H{ 59 | "message": "pong", 60 | }) 61 | }) 62 | r.GET("/callbacks", h.OAuth2Callbacks) 63 | r.GET("/pokemons", h.ListPokemons) 64 | 65 | r.Run(":9002") 66 | } 67 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/di/di.go: -------------------------------------------------------------------------------- 1 | package di 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/jmoiron/sqlx" 9 | _ "github.com/lib/pq" 10 | "github.com/syafdia/xaam/internal/domain/repo" 11 | au "github.com/syafdia/xaam/internal/domain/usecase/auth" 12 | bu "github.com/syafdia/xaam/internal/domain/usecase/business" 13 | "github.com/syafdia/xaam/internal/source" 14 | ) 15 | 16 | type AppModule struct { 17 | DB *sqlx.DB 18 | } 19 | 20 | func GetAppModule() AppModule { 21 | dbConnection := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", 22 | os.Getenv("DB_USER"), 23 | os.Getenv("DB_PASSWORD"), 24 | os.Getenv("DB_NAME")) 25 | 26 | db, err := sqlx.Connect("postgres", dbConnection) 27 | if err != nil { 28 | log.Panic("failed initializing SQLX", err) 29 | } 30 | 31 | return AppModule{ 32 | DB: db, 33 | } 34 | } 35 | 36 | type RepoModule struct { 37 | PolicyRepo repo.PolicyRepo 38 | ResourceRepo repo.ResourceRepo 39 | BusinessRepo repo.BusinessRepo 40 | } 41 | 42 | func GetRepoModule(appModule AppModule) RepoModule { 43 | return RepoModule{ 44 | PolicyRepo: source.NewPolicyRepo(appModule.DB), 45 | ResourceRepo: source.NewResourceRepo(appModule.DB), 46 | BusinessRepo: source.NewBusinessRepo(), 47 | } 48 | } 49 | 50 | type UseCaseModule struct { 51 | FindResourcesByComplianceUseCase au.FindResourcesByComplianceUseCase 52 | FindOneByBusinessIDUseCase bu.FindOneByBusinessIDUseCase 53 | } 54 | 55 | func GetUseCaseModule(repoModule RepoModule) UseCaseModule { 56 | return UseCaseModule{ 57 | FindResourcesByComplianceUseCase: au.NewFindResourcesByComplianceUseCase( 58 | repoModule.PolicyRepo, 59 | repoModule.ResourceRepo, 60 | ), 61 | FindOneByBusinessIDUseCase: bu.NewFindOneByBusinessIDUseCase(repoModule.BusinessRepo), 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/user/usecase_mock_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: internal/user/usecase.go 3 | 4 | // Package user is a generated GoMock package. 5 | package user 6 | 7 | import ( 8 | context "context" 9 | reflect "reflect" 10 | 11 | gomock "github.com/golang/mock/gomock" 12 | ) 13 | 14 | // MockRegisterUserUseCase is a mock of RegisterUserUseCase interface. 15 | type MockRegisterUserUseCase struct { 16 | ctrl *gomock.Controller 17 | recorder *MockRegisterUserUseCaseMockRecorder 18 | } 19 | 20 | // MockRegisterUserUseCaseMockRecorder is the mock recorder for MockRegisterUserUseCase. 21 | type MockRegisterUserUseCaseMockRecorder struct { 22 | mock *MockRegisterUserUseCase 23 | } 24 | 25 | // NewMockRegisterUserUseCase creates a new mock instance. 26 | func NewMockRegisterUserUseCase(ctrl *gomock.Controller) *MockRegisterUserUseCase { 27 | mock := &MockRegisterUserUseCase{ctrl: ctrl} 28 | mock.recorder = &MockRegisterUserUseCaseMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use. 33 | func (m *MockRegisterUserUseCase) EXPECT() *MockRegisterUserUseCaseMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // Execute mocks base method. 38 | func (m *MockRegisterUserUseCase) Execute(ctx context.Context, input CreateUserInput) (User, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "Execute", ctx, input) 41 | ret0, _ := ret[0].(User) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // Execute indicates an expected call of Execute. 47 | func (mr *MockRegisterUserUseCaseMockRecorder) Execute(ctx, input interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockRegisterUserUseCase)(nil).Execute), ctx, input) 50 | } 51 | -------------------------------------------------------------------------------- /src/etc/XAAM/files/sql/0002.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO resources(name, created_at, updated_at) 2 | VALUES 3 | ('Credit Card', NOW(), NULL), 4 | ('Debit Cards', NOW(), NULL), 5 | ('VA', NOW(), NULL), 6 | ('Retail Outlet', NOW(), NULL), 7 | ('Wallets', NOW(), NULL), 8 | ('Cardless Credit', NOW(), NULL), 9 | ('Local Disbursement' , NOW(), NULL), 10 | ('Cross-border remittance', NOW(), NULL), 11 | ('Escrow', NOW(), NULL); 12 | 13 | INSERT INTO resource_actions(name, resource_id, created_at, updated_at) 14 | VALUES 15 | ('compliant', 1, NOW(), NULL), 16 | ('compliant', 2, NOW(), NULL), 17 | ('compliant', 3, NOW(), NULL), 18 | ('compliant', 4, NOW(), NULL), 19 | ('compliant', 5, NOW(), NULL), 20 | ('compliant', 6, NOW(), NULL), 21 | ('compliant', 7, NOW(), NULL), 22 | ('compliant', 8, NOW(), NULL), 23 | ('compliant', 9, NOW(), NULL) 24 | ('read', 1, NOW(), NULL); 25 | 26 | INSERT INTO target_resource_actions(resource_action_id, target_id, target_type, created_at, updated_at) 27 | VALUES 28 | (2, 1, 'legal_entity', NOW(), NULL), 29 | (3, 1, 'legal_entity', NOW(), NULL), 30 | (4, 1, 'legal_entity', NOW(), NULL), 31 | (5, 1, 'legal_entity', NOW(), NULL); 32 | 33 | INSERT INTO target_resource_actions(resource_action_id, target_id, target_type, created_at, updated_at) 34 | VALUES 35 | (7, 2, 'legal_entity', NOW(), NULL), 36 | (8, 2, 'legal_entity', NOW(), NULL), 37 | (9, 2, 'legal_entity', NOW(), NULL); 38 | 39 | INSERT INTO target_resource_actions(resource_action_id, target_id, target_type, created_at, updated_at) 40 | VALUES 41 | (2, 1, 'industry', NOW(), NULL), 42 | (3, 1, 'industry', NOW(), NULL), 43 | (4, 1, 'industry', NOW(), NULL), 44 | (5, 1, 'industry', NOW(), NULL), 45 | (7, 1, 'industry', NOW(), NULL), 46 | (8, 1, 'industry', NOW(), NULL), 47 | (9, 1, 'industry', NOW(), NULL); 48 | 49 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/source/resource_repo.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/jmoiron/sqlx" 7 | e "github.com/syafdia/xaam/internal/domain/entity" 8 | "github.com/syafdia/xaam/internal/domain/repo" 9 | ) 10 | 11 | const ( 12 | sqlFindMultipleByIDs = ` 13 | SELECT 14 | r.id, 15 | r.name 16 | FROM resources AS r 17 | WHERE r.id IN (?); 18 | ` 19 | 20 | sqlFindMultipleByNames = ` 21 | SELECT 22 | r.id, 23 | r.name 24 | FROM resources AS r 25 | WHERE r.name IN (?); 26 | ` 27 | ) 28 | 29 | type resourceRepo struct { 30 | db *sqlx.DB 31 | } 32 | 33 | func NewResourceRepo(db *sqlx.DB) repo.ResourceRepo { 34 | return &resourceRepo{db: db} 35 | } 36 | 37 | func (r *resourceRepo) FindMultipleByIDs(ctx context.Context, ids []int64) (map[int64]e.Resource, error) { 38 | query, args, err := sqlx.In(sqlFindMultipleByIDs, ids) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | query = r.db.Rebind(query) 44 | results := []e.Resource{} 45 | 46 | err = r.db.SelectContext(ctx, &results, query, args...) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | idWithResource := map[int64]e.Resource{} 52 | for _, result := range results { 53 | idWithResource[result.ID] = result 54 | } 55 | 56 | return idWithResource, nil 57 | } 58 | 59 | func (r *resourceRepo) FindMultipleByNames(ctx context.Context, names []string) (map[string]e.Resource, error) { 60 | query, args, err := sqlx.In(sqlFindMultipleByNames, names) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | query = r.db.Rebind(query) 66 | results := []e.Resource{} 67 | 68 | err = r.db.SelectContext(ctx, &results, query, args...) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | nameWithResource := map[string]e.Resource{} 74 | for _, result := range results { 75 | nameWithResource[result.Name] = result 76 | } 77 | 78 | return nameWithResource, nil 79 | } 80 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/mock/domain/repo/business_repo.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: internal/domain/repo/business_repo.go 3 | 4 | // Package mock_repo is a generated GoMock package. 5 | package mock_repo 6 | 7 | import ( 8 | context "context" 9 | gomock "github.com/golang/mock/gomock" 10 | business "github.com/syafdia/xaam/internal/domain/entity/business" 11 | reflect "reflect" 12 | ) 13 | 14 | // MockBusinessRepo is a mock of BusinessRepo interface 15 | type MockBusinessRepo struct { 16 | ctrl *gomock.Controller 17 | recorder *MockBusinessRepoMockRecorder 18 | } 19 | 20 | // MockBusinessRepoMockRecorder is the mock recorder for MockBusinessRepo 21 | type MockBusinessRepoMockRecorder struct { 22 | mock *MockBusinessRepo 23 | } 24 | 25 | // NewMockBusinessRepo creates a new mock instance 26 | func NewMockBusinessRepo(ctrl *gomock.Controller) *MockBusinessRepo { 27 | mock := &MockBusinessRepo{ctrl: ctrl} 28 | mock.recorder = &MockBusinessRepoMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use 33 | func (m *MockBusinessRepo) EXPECT() *MockBusinessRepoMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // FindOneByBusinessID mocks base method 38 | func (m *MockBusinessRepo) FindOneByBusinessID(ctx context.Context, businessID string) (business.GetOneResponse, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "FindOneByBusinessID", ctx, businessID) 41 | ret0, _ := ret[0].(business.GetOneResponse) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // FindOneByBusinessID indicates an expected call of FindOneByBusinessID 47 | func (mr *MockBusinessRepoMockRecorder) FindOneByBusinessID(ctx, businessID interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByBusinessID", reflect.TypeOf((*MockBusinessRepo)(nil).FindOneByBusinessID), ctx, businessID) 50 | } 51 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/internal/delivery/kong/key_checker.go: -------------------------------------------------------------------------------- 1 | package delivery 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/Kong/go-pdk" 8 | "github.com/syafdia/demo-kong-plugin/internal/di" 9 | "github.com/syafdia/demo-kong-plugin/internal/domain/entity/keychecker" 10 | ) 11 | 12 | // KeyCheckerConfig will hold all Kong configuration data 13 | // and injected use case module. 14 | type KeyCheckerConfig struct { 15 | APIKey string `json:"api_key"` 16 | useCaseModule *di.UseCaseModule 17 | } 18 | 19 | // NewKeyChecker will create an instance of KeyCheckerConfig. 20 | func NewKeyChecker() interface{} { 21 | return &KeyCheckerConfig{useCaseModule: di.GetUseCaseModule()} 22 | } 23 | 24 | func (conf *KeyCheckerConfig) Access(kong *pdk.PDK) { 25 | log.Println("[KeyChecker] Got request") 26 | key, err := kong.Request.GetQueryArg("key") 27 | if err != nil { 28 | kong.Log.Err(err.Error()) 29 | } 30 | 31 | ctx := context.Background() 32 | respHeaders := make(map[string][]string) 33 | respHeaders["Content-Type"] = append(respHeaders["Content-Type"], "application/json") 34 | 35 | err = conf.useCaseModule.ValidateKeyUseCase.Execute(ctx, keychecker.ValidateKeyInput{ 36 | GivenKey: key, 37 | ValidKey: conf.APIKey, 38 | }) 39 | if err != nil { 40 | switch err { 41 | case keychecker.ErrKeyEmpty, keychecker.ErrKeyNotValid: 42 | kong.Response.Exit(401, err.Error(), respHeaders) 43 | 44 | default: 45 | kong.Response.Exit(500, err.Error(), respHeaders) 46 | } 47 | 48 | return 49 | } 50 | 51 | token, err := conf.useCaseModule.GetTokenUseCase.Execute(ctx, key) 52 | if err != nil { 53 | kong.Response.Exit(500, err.Error(), respHeaders) 54 | return 55 | } 56 | 57 | kong.Response.SetHeader("X-Exchange-Token", token) 58 | } 59 | 60 | func (conf *KeyCheckerConfig) Response(kong *pdk.PDK) { 61 | status, err := kong.Response.GetStatus() 62 | if err != nil { 63 | kong.Log.Err(err.Error()) 64 | } else { 65 | log.Println("[KeyChecker] Got response", status) 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/mock/domain/repo/policy_repo.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: internal/domain/repo/resource_action_repo.go 3 | 4 | // Package mock_repo is a generated GoMock package. 5 | package mock_repo 6 | 7 | import ( 8 | context "context" 9 | gomock "github.com/golang/mock/gomock" 10 | entity "github.com/syafdia/xaam/internal/domain/entity" 11 | reflect "reflect" 12 | ) 13 | 14 | // MockPolicyRepo is a mock of PolicyRepo interface 15 | type MockPolicyRepo struct { 16 | ctrl *gomock.Controller 17 | recorder *MockPolicyRepoMockRecorder 18 | } 19 | 20 | // MockPolicyRepoMockRecorder is the mock recorder for MockPolicyRepo 21 | type MockPolicyRepoMockRecorder struct { 22 | mock *MockPolicyRepo 23 | } 24 | 25 | // NewMockPolicyRepo creates a new mock instance 26 | func NewMockPolicyRepo(ctrl *gomock.Controller) *MockPolicyRepo { 27 | mock := &MockPolicyRepo{ctrl: ctrl} 28 | mock.recorder = &MockPolicyRepoMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use 33 | func (m *MockPolicyRepo) EXPECT() *MockPolicyRepoMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // FindMultipleByIDandTargetType mocks base method 38 | func (m *MockPolicyRepo) FindMultipleByIDandTargetType(ctx context.Context, targetID int64, targetType entity.TargetType) ([]entity.Action, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "FindMultipleByIDandTargetType", ctx, targetID, targetType) 41 | ret0, _ := ret[0].([]entity.Action) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // FindMultipleByIDandTargetType indicates an expected call of FindMultipleByIDandTargetType 47 | func (mr *MockPolicyRepoMockRecorder) FindMultipleByIDandTargetType(ctx, targetID, targetType interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMultipleByIDandTargetType", reflect.TypeOf((*MockPolicyRepo)(nil).FindMultipleByIDandTargetType), ctx, targetID, targetType) 50 | } 51 | -------------------------------------------------------------------------------- /src/datastructure/linkedlist/linkedlist.go: -------------------------------------------------------------------------------- 1 | package linkedlist 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/syafdia/go-exercise/src/datastructure/types" 7 | ) 8 | 9 | type LinkedList interface { 10 | Head() types.T 11 | Tail() LinkedList 12 | Map(mapper func(t types.T) types.U) LinkedList 13 | Filter(filterer func(t types.T) bool) LinkedList 14 | Reduce(initial types.U, reducer func(acc types.U, v types.T) types.U) types.U 15 | String() string 16 | Size() int 17 | } 18 | 19 | type node struct { 20 | head types.T 21 | tail LinkedList 22 | } 23 | 24 | func New(values ...types.T) LinkedList { 25 | totVals := len(values) 26 | 27 | if totVals == 0 { 28 | return node{} 29 | } 30 | 31 | head := values[0] 32 | 33 | if totVals == 1 { 34 | return node{head: head, tail: nil} 35 | } 36 | 37 | return node{head: head, tail: New(values[1:]...)} 38 | } 39 | 40 | func (n node) Head() types.T { 41 | return n.head 42 | } 43 | 44 | func (n node) Tail() LinkedList { 45 | return n.tail 46 | } 47 | 48 | func (n node) Map(mapper func(t types.T) types.U) LinkedList { 49 | if n.tail == nil { 50 | return &node{ 51 | head: mapper(n.head), 52 | tail: nil, 53 | } 54 | } 55 | 56 | return &node{ 57 | head: mapper(n.head), 58 | tail: n.tail.Map(mapper), 59 | } 60 | } 61 | 62 | func (n node) Filter(filterer func(t types.T) bool) LinkedList { 63 | if filterer(n.head) { 64 | if n.tail == nil { 65 | return &node{n.head, nil} 66 | } 67 | 68 | return &node{n.head, n.tail.Filter(filterer)} 69 | } 70 | 71 | if n.tail == nil { 72 | return nil 73 | } 74 | 75 | return n.tail.Filter(filterer) 76 | } 77 | 78 | func (n node) Reduce(initial types.U, reducer func(acc types.U, v types.T) types.U) types.U { 79 | if n.tail == nil { 80 | return reducer(initial, n.head) 81 | } 82 | 83 | return n.tail.Reduce(reducer(initial, n.head), reducer) 84 | } 85 | 86 | func (n node) String() string { 87 | return fmt.Sprintf("(%v, %v)", n.head, n.tail) 88 | } 89 | 90 | func (n node) Size() int { 91 | return n.Reduce(0, func(acc types.U, _ types.T) types.U { 92 | return acc.(int) + 1 93 | }).(int) 94 | } 95 | -------------------------------------------------------------------------------- /src/etc/demo-oauth2/cmd/auth/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "net/http" 6 | 7 | "github.com/gin-gonic/gin" 8 | "github.com/syafdia/demo-oauth2/internal/auth" 9 | ) 10 | 11 | type withHeader struct { 12 | http.Header 13 | rt http.RoundTripper 14 | } 15 | 16 | func WithHeader(rt http.RoundTripper) withHeader { 17 | if rt == nil { 18 | rt = http.DefaultTransport 19 | } 20 | 21 | return withHeader{Header: make(http.Header), rt: rt} 22 | } 23 | 24 | func (h withHeader) RoundTrip(req *http.Request) (*http.Response, error) { 25 | for k, v := range h.Header { 26 | req.Header[k] = v 27 | } 28 | 29 | return h.rt.RoundTrip(req) 30 | } 31 | 32 | func setupHTMLTemplate(r *gin.Engine) { 33 | r.SetHTMLTemplate(template.Must(template.New("session.new").Parse(` 34 | 35 | 36 | 37 | 38 | 39 | Sign In 40 | 41 | 42 |

Please Sign In

43 |
44 |
45 |
46 | 47 | 48 |
49 |
50 |
51 | 52 | 53 |
54 |
55 | 56 | 57 |
58 | 59 | 60 | `))) 61 | } 62 | 63 | func main() { 64 | rp := auth.NewRepo() 65 | cl := auth.NewClient() 66 | 67 | h := auth.NewAuthHandler(rp, cl) 68 | 69 | r := gin.Default() 70 | setupHTMLTemplate(r) 71 | 72 | r.GET("/ping", func(c *gin.Context) { 73 | c.JSON(http.StatusOK, gin.H{ 74 | "message": "pong", 75 | }) 76 | }) 77 | 78 | r.GET("auth/session/new", h.NewSession) 79 | r.POST("auth/session", h.CreateSession) 80 | r.GET("auth/consent/new", h.NewConsent) 81 | r.POST("auth/consent", h.CreateConsent) 82 | 83 | r.Run(":9001") 84 | } 85 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/infrastructure/repository/task_repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "reflect" 8 | "time" 9 | 10 | "github.com/syafdia/demo-es/internal/domain" 11 | "github.com/syafdia/demo-es/internal/domain/task" 12 | ) 13 | 14 | type TaskRepository struct { 15 | idWithEvents map[domain.ID][]domain.Event 16 | events []domain.Event 17 | } 18 | 19 | func NewTaskRepository() *TaskRepository { 20 | return &TaskRepository{ 21 | idWithEvents: map[domain.ID][]domain.Event{}, 22 | } 23 | } 24 | 25 | func (tr *TaskRepository) Store(ctx context.Context, task task.Task) error { 26 | tr.events = append(tr.events, task.GetEvents()...) 27 | 28 | // f, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) 29 | // if err != nil { 30 | // panic(err) 31 | // } 32 | 33 | // defer f.Close() 34 | 35 | // tr.allEvents(ctx, func(eventName, aggregateID string, payload any) { 36 | // row := fmt.Sprintf("event_name: %s, aggregate_id: %s, payload: %+v\n", eventName, aggregateID, payload) 37 | // if _, err := f.WriteString(row); err != nil { 38 | // panic(err) 39 | // } 40 | // }) 41 | 42 | return nil 43 | } 44 | 45 | var fileName = fmt.Sprintf("./tmp/storage-%d.txt", time.Now().Unix()) 46 | 47 | func (tr *TaskRepository) Find(ctx context.Context, id domain.ID) (task.Task, error) { 48 | task := task.Task{} 49 | 50 | for _, event := range tr.events { 51 | if event.AggregateID() != id { 52 | continue 53 | } 54 | 55 | task.ApplyEvent(event) 56 | } 57 | 58 | return task, nil 59 | } 60 | 61 | func (tr *TaskRepository) Peek(ctx context.Context) { 62 | fmt.Println("Stored events:\n") 63 | tr.allEvents(ctx, func(eventName, aggregateID string, at time.Time, payload any) { 64 | jsonPayload, err := json.Marshal(payload) 65 | if err != nil { 66 | panic(err) 67 | } 68 | 69 | fmt.Printf( 70 | "event_name: %s, aggregate_id: %s, occured_at: %s, payload: %+v\n\n", 71 | eventName, aggregateID, at.Format(time.RFC3339), string(jsonPayload), 72 | ) 73 | }) 74 | 75 | } 76 | 77 | func (tr *TaskRepository) allEvents(ctx context.Context, fn func(string, string, time.Time, any)) { 78 | for _, event := range tr.events { 79 | tEvent := reflect.TypeOf(event) 80 | fn(tEvent.Name(), string(event.AggregateID()), event.OccuredAt(), event) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/concurrency/pipeline/example/e03/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/syafdia/go-exercise/src/concurrency/pipeline/example" 8 | ) 9 | 10 | func main() { 11 | // single() 12 | multiple() 13 | } 14 | 15 | func single() { 16 | startTime := time.Now() 17 | 18 | valC := addFoo(addQuoute(square(multiplyTwo(generateInput(3))))) 19 | 20 | fmt.Printf("Result: %s\n", <-valC) 21 | 22 | fmt.Printf("Elapsed time without concurrency: %s", time.Since(startTime)) 23 | } 24 | 25 | func multiple() { 26 | startTime := time.Now() 27 | 28 | input1 := 3 29 | input2 := 4 30 | input3 := 5 31 | 32 | valC1 := addFoo(addQuoute(square(multiplyTwo(generateInput(3))))) 33 | valC2 := addFoo(addQuoute(square(multiplyTwo(generateInput(4))))) 34 | valC3 := addFoo(addQuoute(square(multiplyTwo(generateInput(5))))) 35 | 36 | fmt.Printf("Input: %d, Result: %s\n", input1, <-valC1) 37 | fmt.Printf("Input: %d, Result: %s\n", input2, <-valC2) 38 | fmt.Printf("Input: %d, Result: %s\n", input3, <-valC3) 39 | 40 | fmt.Printf("Elapsed time without concurrency: %s", time.Since(startTime)) 41 | } 42 | 43 | func generateInput(N int) chan int { 44 | resC := make(chan int) 45 | 46 | go func() { 47 | resC <- N 48 | close(resC) 49 | }() 50 | 51 | return resC 52 | } 53 | 54 | func multiplyTwo(valC <-chan int) chan int { 55 | resC := make(chan int) 56 | 57 | go func() { 58 | for val := range valC { 59 | fmt.Printf("multiplyTwo: %v\n", val) 60 | resC <- example.MultiplyTwo(val) 61 | } 62 | 63 | close(resC) 64 | }() 65 | 66 | return resC 67 | } 68 | 69 | func square(valC <-chan int) chan int { 70 | resC := make(chan int) 71 | 72 | go func() { 73 | for val := range valC { 74 | fmt.Printf("square: %v\n", val) 75 | resC <- example.Square(val) 76 | } 77 | 78 | close(resC) 79 | }() 80 | 81 | return resC 82 | } 83 | 84 | func addQuoute(valC <-chan int) chan string { 85 | resC := make(chan string) 86 | 87 | go func() { 88 | for val := range valC { 89 | fmt.Printf("addQuoute: %v\n", val) 90 | resC <- example.AddQuoute(val) 91 | } 92 | 93 | close(resC) 94 | }() 95 | 96 | return resC 97 | } 98 | 99 | func addFoo(valC <-chan string) chan string { 100 | resC := make(chan string) 101 | 102 | go func() { 103 | for val := range valC { 104 | fmt.Printf("addFoo: %v\n", val) 105 | resC <- example.AddFoo(val) 106 | } 107 | 108 | close(resC) 109 | }() 110 | 111 | return resC 112 | } 113 | -------------------------------------------------------------------------------- /src/datastructure/queue/queue_test.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/syafdia/go-exercise/src/datastructure/types" 8 | ) 9 | 10 | func Test_queue_Enqueue(t *testing.T) { 11 | type fields struct { 12 | values []types.T 13 | } 14 | type args struct { 15 | v types.T 16 | } 17 | tests := []struct { 18 | name string 19 | fields fields 20 | args args 21 | want []types.T 22 | }{ 23 | { 24 | name: "success adding to queue", 25 | fields: fields{ 26 | values: []types.T{1, 2, 3}, 27 | }, 28 | args: args{ 29 | v: 4, 30 | }, 31 | want: []types.T{1, 2, 3, 4}, 32 | }, 33 | } 34 | for _, tt := range tests { 35 | t.Run(tt.name, func(t *testing.T) { 36 | q := queue{ 37 | values: tt.fields.values, 38 | } 39 | q.Enqueue(tt.args.v) 40 | 41 | if !reflect.DeepEqual(q.values, tt.want) { 42 | t.Errorf("q.values = %v, want %v", q.values, tt.want) 43 | } 44 | }) 45 | } 46 | } 47 | 48 | func Test_queue_Dequeue(t *testing.T) { 49 | type fields struct { 50 | values []types.T 51 | } 52 | tests := []struct { 53 | name string 54 | fields fields 55 | want types.T 56 | want2 []types.T 57 | }{ 58 | { 59 | name: "success dequeueing from queue", 60 | fields: fields{ 61 | values: []types.T{1, 2, 3}, 62 | }, 63 | want: 1, 64 | want2: []types.T{2, 3}, 65 | }, 66 | } 67 | for _, tt := range tests { 68 | t.Run(tt.name, func(t *testing.T) { 69 | q := &queue{ 70 | values: tt.fields.values, 71 | } 72 | if got := q.Dequeue(); !reflect.DeepEqual(got, tt.want) { 73 | t.Errorf("queue.Dequeue() = %v, want %v", got, tt.want) 74 | } 75 | 76 | if !reflect.DeepEqual(q.values, tt.want2) { 77 | t.Errorf("q.values = %v, want %v", q.values, tt.want2) 78 | } 79 | }) 80 | } 81 | } 82 | 83 | func Test_queue_Size(t *testing.T) { 84 | type fields struct { 85 | values []types.T 86 | } 87 | tests := []struct { 88 | name string 89 | fields fields 90 | want int 91 | }{ 92 | { 93 | name: "success counting queue", 94 | fields: fields{ 95 | values: []types.T{1, 2, 3, 4, 5}, 96 | }, 97 | want: 5, 98 | }, 99 | } 100 | for _, tt := range tests { 101 | t.Run(tt.name, func(t *testing.T) { 102 | q := &queue{ 103 | values: tt.fields.values, 104 | } 105 | if got := q.Size(); got != tt.want { 106 | t.Errorf("queue.Size() = %v, want %v", got, tt.want) 107 | } 108 | }) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/user/repo_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "reflect" 7 | "regexp" 8 | "testing" 9 | 10 | "github.com/DATA-DOG/go-sqlmock" 11 | "github.com/jmoiron/sqlx" 12 | ) 13 | 14 | func Test_userRepo_Create(t *testing.T) { 15 | type fields struct { 16 | db *sqlx.DB 17 | } 18 | type args struct { 19 | ctx context.Context 20 | input CreateUserInput 21 | } 22 | 23 | tests := []struct { 24 | name string 25 | args args 26 | beforeTest func(sqlmock.Sqlmock) 27 | want User 28 | wantErr bool 29 | }{ 30 | { 31 | name: "fail create user", 32 | args: args{ 33 | ctx: context.TODO(), 34 | input: CreateUserInput{Email: "jane@example.com", FirstName: "Jane", LastName: "Doe", Gender: GenderFemale}, 35 | }, 36 | beforeTest: func(mockSQL sqlmock.Sqlmock) { 37 | mockSQL. 38 | ExpectQuery(regexp.QuoteMeta(` 39 | INSERT INTO users (email, first_name, last_name, gender) 40 | VALUES ($1, $2, $3, $4);`, 41 | )). 42 | WithArgs("jane@example.com", "Jane", "Doe", GenderFemale). 43 | WillReturnError(errors.New("whoops, error")) 44 | }, 45 | wantErr: true, 46 | }, 47 | { 48 | name: "success create user", 49 | args: args{ 50 | ctx: context.TODO(), 51 | input: CreateUserInput{Email: "john@example.com", FirstName: "John", LastName: "Doe", Gender: GenderMale}, 52 | }, 53 | beforeTest: func(mockSQL sqlmock.Sqlmock) { 54 | mockSQL. 55 | ExpectExec(regexp.QuoteMeta(` 56 | INSERT INTO users (email, first_name, last_name, gender) 57 | VALUES ($1, $2, $3, $4);`, 58 | )). 59 | WithArgs("john@example.com", "John", "Doe", GenderMale). 60 | WillReturnResult(sqlmock.NewResult(1, 1)) 61 | }, 62 | want: User{ID: 1, Email: "john@example.com", FirstName: "John", LastName: "Doe", Gender: GenderMale}, 63 | }, 64 | } 65 | for _, tt := range tests { 66 | t.Run(tt.name, func(t *testing.T) { 67 | mockDB, mockSQL, _ := sqlmock.New() 68 | defer mockDB.Close() 69 | 70 | db := sqlx.NewDb(mockDB, "sqlmock") 71 | 72 | u := &userRepo{ 73 | db: db, 74 | } 75 | 76 | if tt.beforeTest != nil { 77 | tt.beforeTest(mockSQL) 78 | } 79 | 80 | got, err := u.Create(tt.args.ctx, tt.args.input) 81 | if (err != nil) != tt.wantErr { 82 | t.Errorf("userRepo.Create() error = %v, wantErr %v", err, tt.wantErr) 83 | return 84 | } 85 | if !reflect.DeepEqual(got, tt.want) { 86 | t.Errorf("userRepo.Create() = %v, want %v", got, tt.want) 87 | } 88 | }) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/mock/domain/repo/resource_repo.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: internal/domain/repo/resource_repo.go 3 | 4 | // Package mock_repo is a generated GoMock package. 5 | package mock_repo 6 | 7 | import ( 8 | context "context" 9 | gomock "github.com/golang/mock/gomock" 10 | entity "github.com/syafdia/xaam/internal/domain/entity" 11 | reflect "reflect" 12 | ) 13 | 14 | // MockResourceRepo is a mock of ResourceRepo interface 15 | type MockResourceRepo struct { 16 | ctrl *gomock.Controller 17 | recorder *MockResourceRepoMockRecorder 18 | } 19 | 20 | // MockResourceRepoMockRecorder is the mock recorder for MockResourceRepo 21 | type MockResourceRepoMockRecorder struct { 22 | mock *MockResourceRepo 23 | } 24 | 25 | // NewMockResourceRepo creates a new mock instance 26 | func NewMockResourceRepo(ctrl *gomock.Controller) *MockResourceRepo { 27 | mock := &MockResourceRepo{ctrl: ctrl} 28 | mock.recorder = &MockResourceRepoMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use 33 | func (m *MockResourceRepo) EXPECT() *MockResourceRepoMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // FindMultipleByIDs mocks base method 38 | func (m *MockResourceRepo) FindMultipleByIDs(ctx context.Context, ids []int64) (map[int64]entity.Resource, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "FindMultipleByIDs", ctx, ids) 41 | ret0, _ := ret[0].(map[int64]entity.Resource) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // FindMultipleByIDs indicates an expected call of FindMultipleByIDs 47 | func (mr *MockResourceRepoMockRecorder) FindMultipleByIDs(ctx, ids interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMultipleByIDs", reflect.TypeOf((*MockResourceRepo)(nil).FindMultipleByIDs), ctx, ids) 50 | } 51 | 52 | // FindMultipleByNames mocks base method 53 | func (m *MockResourceRepo) FindMultipleByNames(ctx context.Context, names []string) (map[string]entity.Resource, error) { 54 | m.ctrl.T.Helper() 55 | ret := m.ctrl.Call(m, "FindMultipleByNames", ctx, names) 56 | ret0, _ := ret[0].(map[string]entity.Resource) 57 | ret1, _ := ret[1].(error) 58 | return ret0, ret1 59 | } 60 | 61 | // FindMultipleByNames indicates an expected call of FindMultipleByNames 62 | func (mr *MockResourceRepoMockRecorder) FindMultipleByNames(ctx, names interface{}) *gomock.Call { 63 | mr.mock.ctrl.T.Helper() 64 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMultipleByNames", reflect.TypeOf((*MockResourceRepo)(nil).FindMultipleByNames), ctx, names) 65 | } 66 | -------------------------------------------------------------------------------- /src/etc/demo-kong-plugin/go.sum: -------------------------------------------------------------------------------- 1 | github.com/Kong/go-pdk v0.6.0 h1:KZZKGYK5NbTIMQa5P1IRldMvA5/LJEoHhrsOQlVCPHo= 2 | github.com/Kong/go-pdk v0.6.0/go.mod h1:SrAne3YN4a7pNrlJ6Fb6xwa6kOhV2vDY/mqpb3X0fs4= 3 | github.com/Kong/go-plugins v0.4.0 h1:PHp7sRxHBQhnXk2ulzoN4fJe6R7MhKE3SnsrjroRIqE= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= 6 | github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= 7 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 8 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 9 | github.com/githubnemo/CompileDaemon v1.2.1 h1:yEJ28U3sIsuhCtpqN2Pb5XL8G+hpg7KUNfTGvL7GxL0= 10 | github.com/githubnemo/CompileDaemon v1.2.1/go.mod h1:lE3EXX1td33uhlkFLp+ImWY9qBaoRcDeA3neh4m8ic0= 11 | github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= 12 | github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 13 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 14 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 15 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 16 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 17 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 18 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 19 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 20 | github.com/ugorji/go v1.2.1 h1:dz+JxTe7GZQdErTo7SREc1jQj/hFP1k7jyIAwODoW+k= 21 | github.com/ugorji/go v1.2.1/go.mod h1:cSVypSfTLm2o9fKxXvQgn3rMmkPXovcWor6Qn5tbFmI= 22 | github.com/ugorji/go/codec v1.2.1 h1:/TRfW3XKkvWvmAYyCUaQlhoCDGjcvNR8xVVA/l5p/jQ= 23 | github.com/ugorji/go/codec v1.2.1/go.mod h1:s/WxCRi46t8rA+fowL40EnmD7ec0XhR7ZypxeBNdzsM= 24 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 25 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 26 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= 27 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 28 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 29 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 30 | -------------------------------------------------------------------------------- /src/etc/clean-arch-ddd-cqrs-es/internal/domain/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "fmt" 7 | "regexp" 8 | "time" 9 | 10 | "github.com/syafdia/clean-arch-ddd-cqrs-es/internal/domain" 11 | ) 12 | 13 | type UserID int64 14 | 15 | type User struct { 16 | ID UserID 17 | Email Email 18 | HashedPassword string 19 | CreatedAt time.Time 20 | UpdatedAt time.Time 21 | } 22 | 23 | func NewUser( 24 | email Email, 25 | password Password, 26 | createdAt time.Time, 27 | ) (*User, error) { 28 | err := email.Validate() 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | err = password.Validate() 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | hashedPassword, err := password.Hash() 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | return &User{ 44 | Email: email, 45 | HashedPassword: hashedPassword, 46 | CreatedAt: createdAt, 47 | }, nil 48 | } 49 | 50 | func (u *User) UpdateEmail(newEmail Email, updatedAt time.Time) error { 51 | err := newEmail.Validate() 52 | if err != nil { 53 | return err 54 | } 55 | 56 | u.Email = newEmail 57 | u.UpdatedAt = updatedAt 58 | 59 | return nil 60 | } 61 | 62 | func (u *User) UpdatePassword(newPassword Password, updatedAt time.Time) error { 63 | err := newPassword.Validate() 64 | if err != nil { 65 | return err 66 | } 67 | 68 | hashedPassword, err := newPassword.Hash() 69 | if err != nil { 70 | return err 71 | } 72 | 73 | u.HashedPassword = hashedPassword 74 | u.UpdatedAt = updatedAt 75 | 76 | return nil 77 | } 78 | 79 | type Password string 80 | 81 | func (p Password) Validate() error { 82 | if len(p) < 8 { 83 | return domain.NewValidationError(ErrCodeInvalidPasswordLength, "must be at least 6 characters") 84 | } 85 | 86 | if len(p) > 16 { 87 | return domain.NewValidationError(ErrCodeInvalidPasswordLength, "the maximum value is 16 characters") 88 | } 89 | 90 | return nil 91 | } 92 | 93 | func (p Password) Hash() (string, error) { 94 | h := sha256.New() 95 | _, err := h.Write([]byte(p)) 96 | if err != nil { 97 | return "", fmt.Errorf("password: failed hashing password, %w", err) 98 | } 99 | 100 | return hex.EncodeToString(h.Sum(nil)), nil 101 | } 102 | 103 | type Email string 104 | 105 | var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`) 106 | 107 | func (e Email) Validate() error { 108 | ok := emailRegex.MatchString(string(e)) 109 | if !ok { 110 | return domain.NewValidationError(ErrCodeInvalidEmailFormat, "value is not valid") 111 | } 112 | 113 | return nil 114 | } 115 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/application/command_handler.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/syafdia/demo-es/internal/domain" 8 | "github.com/syafdia/demo-es/internal/domain/communication" 9 | "github.com/syafdia/demo-es/internal/domain/task" 10 | ) 11 | 12 | type CreateTaskCommand struct { 13 | TaskTitle string 14 | TaskDescription string 15 | } 16 | 17 | type UpdateTaskStatusCommand struct { 18 | TaskID domain.ID 19 | TaskStatus task.TaskStatus 20 | } 21 | 22 | type UpvoteTaskCommand struct { 23 | TaskID domain.ID 24 | } 25 | 26 | type CommandHandler struct { 27 | taskRepository task.TaskRepository 28 | emailService communication.EmailService 29 | } 30 | 31 | func NewCommandHandler( 32 | taskRepository task.TaskRepository, 33 | emailService communication.EmailService, 34 | ) *CommandHandler { 35 | return &CommandHandler{ 36 | taskRepository: taskRepository, 37 | } 38 | } 39 | 40 | func (c *CommandHandler) CreateTask(ctx context.Context, cmd CreateTaskCommand) error { 41 | t, err := task.NewTask(cmd.TaskTitle, cmd.TaskDescription) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | err = c.taskRepository.Store(ctx, t) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | return nil 52 | } 53 | 54 | func (c *CommandHandler) UpdateTaskStatus(ctx context.Context, cmd UpdateTaskStatusCommand) error { 55 | t, err := c.taskRepository.Find(ctx, cmd.TaskID) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | err = t.ChangeStatus(cmd.TaskStatus) 61 | if err != nil { 62 | return err 63 | } 64 | 65 | err = c.taskRepository.Store(ctx, t) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | if t.Status == task.TaskStatusCompleted { 71 | c.emailService.SendEmail(ctx, fmt.Sprintf("Task %s has been completed", t.Title)) 72 | } 73 | 74 | return nil 75 | } 76 | 77 | func (c *CommandHandler) UpvoteTask(ctx context.Context, cmd UpvoteTaskCommand) error { 78 | t, err := c.taskRepository.Find(ctx, cmd.TaskID) 79 | if err != nil { 80 | return err 81 | } 82 | 83 | err = t.Upvote() 84 | if err != nil { 85 | return err 86 | } 87 | 88 | err = c.taskRepository.Store(ctx, t) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | return nil 94 | } 95 | 96 | func (c *CommandHandler) DownvoteTask(ctx context.Context, cmd UpvoteTaskCommand) error { 97 | t, err := c.taskRepository.Find(ctx, cmd.TaskID) 98 | if err != nil { 99 | return err 100 | } 101 | 102 | err = t.Downvote() 103 | if err != nil { 104 | return err 105 | } 106 | 107 | err = c.taskRepository.Store(ctx, t) 108 | if err != nil { 109 | return err 110 | } 111 | 112 | return nil 113 | } 114 | -------------------------------------------------------------------------------- /src/concurrency/pipeline/example/e04/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "sync" 7 | "time" 8 | 9 | "github.com/syafdia/go-exercise/src/concurrency/pipeline/example" 10 | ) 11 | 12 | func main() { 13 | Ns := []int{3, 4, 5} 14 | startTime := time.Now() 15 | 16 | fmt.Printf("Multiple input: %d\n", Ns) 17 | 18 | valCs := withFanOut(Ns) 19 | valC := withFanIn(valCs) 20 | 21 | vals := []string{} 22 | for val := range valC { 23 | vals = append(vals, val) 24 | } 25 | 26 | result := strings.Join(vals, " | ") 27 | 28 | fmt.Printf("All results: %s\n", result) 29 | fmt.Printf("Elapsed time without concurrency: %s", time.Since(startTime)) 30 | } 31 | 32 | // 1 -> N 33 | func withFanOut(Ns []int) []chan string { 34 | outCs := []chan string{} 35 | for _, N := range Ns { 36 | outCs = append(outCs, addFoo(addQuoute(square(multiplyTwo(generateInput(N)))))) 37 | } 38 | 39 | return outCs 40 | } 41 | 42 | // 3 -> 1 43 | func withFanIn(valCs []chan string) chan string { 44 | var wg sync.WaitGroup 45 | 46 | outC := make(chan string) 47 | 48 | for _, valC := range valCs { 49 | wg.Add(1) 50 | 51 | go func(vC chan string) { 52 | for val := range vC { 53 | outC <- val 54 | } 55 | 56 | wg.Done() 57 | }(valC) 58 | } 59 | 60 | go func() { 61 | wg.Wait() 62 | close(outC) 63 | }() 64 | 65 | return outC 66 | } 67 | 68 | func generateInput(N int) chan int { 69 | resC := make(chan int) 70 | 71 | go func() { 72 | resC <- N 73 | close(resC) 74 | }() 75 | 76 | return resC 77 | } 78 | 79 | func multiplyTwo(valC <-chan int) chan int { 80 | resC := make(chan int) 81 | 82 | go func() { 83 | for val := range valC { 84 | fmt.Printf("multiplyTwo: %v\n", val) 85 | resC <- example.MultiplyTwo(val) 86 | } 87 | 88 | close(resC) 89 | }() 90 | 91 | return resC 92 | } 93 | 94 | func square(valC <-chan int) chan int { 95 | resC := make(chan int) 96 | 97 | go func() { 98 | for val := range valC { 99 | fmt.Printf("square: %v\n", val) 100 | resC <- example.Square(val) 101 | } 102 | 103 | close(resC) 104 | }() 105 | 106 | return resC 107 | } 108 | 109 | func addQuoute(valC <-chan int) chan string { 110 | resC := make(chan string) 111 | 112 | go func() { 113 | for val := range valC { 114 | fmt.Printf("addQuoute: %v\n", val) 115 | resC <- example.AddQuoute(val) 116 | } 117 | 118 | close(resC) 119 | }() 120 | 121 | return resC 122 | } 123 | 124 | func addFoo(valC <-chan string) chan string { 125 | resC := make(chan string) 126 | 127 | go func() { 128 | for val := range valC { 129 | fmt.Printf("addFoo: %v\n", val) 130 | resC <- example.AddFoo(val) 131 | } 132 | 133 | close(resC) 134 | }() 135 | 136 | return resC 137 | } 138 | -------------------------------------------------------------------------------- /src/etc/kafka/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 3 | github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= 4 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 5 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 6 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 7 | github.com/klauspost/compress v1.9.8 h1:VMAMUUOh+gaxKTMk+zqbjsSjsIcUcL/LF4o63i82QyA= 8 | github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 9 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 10 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 11 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 12 | github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDmguYK6iH1A= 13 | github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 15 | github.com/segmentio/kafka-go v0.4.25 h1:QVx9yz12syKBFkxR+dVDDwTO0ItHgnjjhIdBfqizj+8= 16 | github.com/segmentio/kafka-go v0.4.25/go.mod h1:XzMcoMjSzDGHcIwpWUI7GB43iKZ2fTVmryPSGLf/MPg= 17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 18 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 19 | github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= 20 | github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= 21 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 22 | golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 23 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 24 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 25 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 26 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 27 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 28 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 29 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 30 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/cmd/todo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "fmt" 7 | "os" 8 | "strings" 9 | 10 | "github.com/syafdia/demo-es/internal/application" 11 | "github.com/syafdia/demo-es/internal/domain" 12 | "github.com/syafdia/demo-es/internal/domain/task" 13 | "github.com/syafdia/demo-es/internal/infrastructure/repository" 14 | "github.com/syafdia/demo-es/internal/infrastructure/service" 15 | ) 16 | 17 | func main() { 18 | 19 | taskRepository := repository.NewTaskRepository() 20 | emailService := service.NewEmailService() 21 | cmdHandler := application.NewCommandHandler(taskRepository, emailService) 22 | 23 | ctx := context.Background() 24 | 25 | for { 26 | opt := p(` 27 | Available option 28 | [ct] Create task 29 | [ut] Update task status 30 | [dv] Downvote task 31 | [uv] Upvote task 32 | [vt] View task 33 | [ve] View all events 34 | Enter option: `) 35 | 36 | switch strings.TrimSpace(opt) { 37 | case "ct": 38 | title := p("[ct] Enter task title:") 39 | description := p("[ct] Enter task description:") 40 | 41 | err := cmdHandler.CreateTask(ctx, application.CreateTaskCommand{ 42 | TaskTitle: title, 43 | TaskDescription: description, 44 | }) 45 | handleError(err) 46 | 47 | case "ut": 48 | taskID := p("[ut] Enter task ID:") 49 | taskStatus := p("[ut] Enter status (NOT_STARTED, IN_PROGRESS, IN_REVIEW, COMPLETED):") 50 | 51 | err := cmdHandler.UpdateTaskStatus(ctx, application.UpdateTaskStatusCommand{ 52 | TaskID: domain.ID(taskID), 53 | TaskStatus: task.TaskStatus(taskStatus), 54 | }) 55 | handleError(err) 56 | 57 | case "uv": 58 | taskID := p("[uv] Enter task ID:") 59 | 60 | err := cmdHandler.UpvoteTask(ctx, application.UpvoteTaskCommand{ 61 | TaskID: domain.ID(taskID), 62 | }) 63 | handleError(err) 64 | 65 | case "dv": 66 | taskID := p("[dv] Enter task ID:") 67 | 68 | err := cmdHandler.DownvoteTask(ctx, application.UpvoteTaskCommand{ 69 | TaskID: domain.ID(taskID), 70 | }) 71 | handleError(err) 72 | 73 | case "vt": 74 | taskID := p("[vt] Enter task ID:") 75 | 76 | t, err := taskRepository.Find(ctx, domain.ID(taskID)) 77 | handleError(err) 78 | 79 | fmt.Printf("Current state of task: %+v\n", t) 80 | 81 | case "ve": 82 | taskRepository.Peek(ctx) 83 | } 84 | 85 | // fmt.Print("Enter your city: ") 86 | // city, _ := reader.ReadString('\n') 87 | // fmt.Print("You live in " + city) 88 | } 89 | } 90 | 91 | func p(message string) string { 92 | fmt.Print(message + " ") 93 | result, err := bufio.NewReader(os.Stdin).ReadString('\n') 94 | if err != nil { 95 | panic(err) 96 | } 97 | 98 | return strings.TrimSpace(result) 99 | } 100 | 101 | func handleError(err error) { 102 | if err != nil { 103 | fmt.Println("Got error,", err) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/k8s/demo-flux/infrastructure/base/centralized-log/beat.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: filebeat 5 | namespace: default 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRole 9 | metadata: 10 | name: filebeat 11 | rules: 12 | - apiGroups: [""] # "" indicates the core API group 13 | resources: 14 | - namespaces 15 | - pods 16 | - nodes 17 | verbs: 18 | - get 19 | - watch 20 | - list 21 | - apiGroups: ["apps"] 22 | resources: 23 | - replicasets 24 | verbs: 25 | - get 26 | - list 27 | - watch 28 | - apiGroups: ["batch"] 29 | resources: 30 | - jobs 31 | verbs: 32 | - get 33 | - list 34 | - watch 35 | --- 36 | apiVersion: rbac.authorization.k8s.io/v1 37 | kind: ClusterRoleBinding 38 | metadata: 39 | name: filebeat 40 | subjects: 41 | - kind: ServiceAccount 42 | name: filebeat 43 | namespace: default 44 | roleRef: 45 | kind: ClusterRole 46 | name: filebeat 47 | apiGroup: rbac.authorization.k8s.io 48 | --- 49 | apiVersion: beat.k8s.elastic.co/v1beta1 50 | kind: Beat 51 | metadata: 52 | name: centralized-log-beat 53 | namespace: centralized-log 54 | spec: 55 | type: filebeat 56 | version: 8.9.1 57 | elasticsearchRef: 58 | name: centralized-log-es 59 | kibanaRef: 60 | name: centralized-log-kb 61 | config: 62 | filebeat: 63 | autodiscover: 64 | providers: 65 | - type: kubernetes 66 | node: ${NODE_NAME} 67 | hints: 68 | enabled: true 69 | default_config: 70 | type: container 71 | paths: 72 | - /var/log/containers/*${data.kubernetes.container.id}.log 73 | processors: 74 | - add_cloud_metadata: {} 75 | - add_host_metadata: {} 76 | daemonSet: 77 | podTemplate: 78 | spec: 79 | serviceAccountName: filebeat 80 | automountServiceAccountToken: true 81 | terminationGracePeriodSeconds: 30 82 | dnsPolicy: ClusterFirstWithHostNet 83 | hostNetwork: true # Allows to provide richer host metadata 84 | containers: 85 | - name: filebeat 86 | securityContext: 87 | runAsUser: 0 88 | volumeMounts: 89 | - name: varlogcontainers 90 | mountPath: /var/log/containers 91 | - name: varlogpods 92 | mountPath: /var/log/pods 93 | - name: varlibdockercontainers 94 | mountPath: /var/lib/docker/containers 95 | env: 96 | - name: NODE_NAME 97 | valueFrom: 98 | fieldRef: 99 | fieldPath: spec.nodeName 100 | volumes: 101 | - name: varlogcontainers 102 | hostPath: 103 | path: /var/log/containers 104 | - name: varlogpods 105 | hostPath: 106 | path: /var/log/pods 107 | - name: varlibdockercontainers 108 | hostPath: 109 | path: /var/lib/docker/containers -------------------------------------------------------------------------------- /src/datastructure/linkedlist/linkedlist_test.go: -------------------------------------------------------------------------------- 1 | package linkedlist 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/syafdia/go-exercise/src/datastructure/types" 8 | ) 9 | 10 | func Test_node_Head(t *testing.T) { 11 | type fields struct { 12 | head types.T 13 | tail LinkedList 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | want types.T 19 | }{ 20 | { 21 | name: "success return head", 22 | fields: fields{ 23 | head: 1, 24 | tail: nil, 25 | }, 26 | want: 1, 27 | }, 28 | } 29 | for _, tt := range tests { 30 | t.Run(tt.name, func(t *testing.T) { 31 | n := node{ 32 | head: tt.fields.head, 33 | tail: tt.fields.tail, 34 | } 35 | if got := n.Head(); !reflect.DeepEqual(got, tt.want) { 36 | t.Errorf("node.Head() = %v, want %v", got, tt.want) 37 | } 38 | }) 39 | } 40 | } 41 | 42 | func Test_node_Tail(t *testing.T) { 43 | type fields struct { 44 | head types.T 45 | tail LinkedList 46 | } 47 | tests := []struct { 48 | name string 49 | fields fields 50 | want LinkedList 51 | }{ 52 | { 53 | name: "success return non nil tail", 54 | fields: fields{ 55 | head: 1, 56 | tail: node{ 57 | head: 2, 58 | tail: nil, 59 | }, 60 | }, 61 | want: node{ 62 | head: 2, 63 | tail: nil, 64 | }, 65 | }, 66 | { 67 | name: "success return nil tail", 68 | fields: fields{ 69 | head: 1, 70 | tail: nil, 71 | }, 72 | want: nil, 73 | }, 74 | } 75 | for _, tt := range tests { 76 | t.Run(tt.name, func(t *testing.T) { 77 | n := node{ 78 | head: tt.fields.head, 79 | tail: tt.fields.tail, 80 | } 81 | if got := n.Tail(); !reflect.DeepEqual(got, tt.want) { 82 | t.Errorf("node.Tail() = %v, want %v", got, tt.want) 83 | } 84 | }) 85 | } 86 | } 87 | 88 | func Test_node_Map(t *testing.T) { 89 | type fields struct { 90 | head types.T 91 | tail LinkedList 92 | } 93 | type args struct { 94 | mapper func(t types.T) types.U 95 | } 96 | tests := []struct { 97 | name string 98 | fields fields 99 | args args 100 | want LinkedList 101 | }{ 102 | // { 103 | // name: "success mapping data", 104 | // fields: fields{ 105 | // head: 1, 106 | // tail: node{ 107 | // head: 2, 108 | // tail: nil, 109 | // }, 110 | // }, 111 | // args: args{ 112 | // mapper: func(v types.T) types.U { 113 | // return v.(int) * 10 114 | // }, 115 | // }, 116 | // want: node{ 117 | // head: 10, 118 | // tail: node{ 119 | // head: 20, 120 | // tail: nil, 121 | // }, 122 | // }, 123 | // }, 124 | } 125 | for _, tt := range tests { 126 | t.Run(tt.name, func(t *testing.T) { 127 | n := node{ 128 | head: tt.fields.head, 129 | tail: tt.fields.tail, 130 | } 131 | if got := n.Map(tt.args.mapper); !reflect.DeepEqual(got, tt.want) { 132 | t.Errorf("node.Map() = %v, want %v", got, tt.want) 133 | } 134 | }) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/user/usecase_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/golang/mock/gomock" 10 | ) 11 | 12 | func Test_registerUserUseCase_Execute(t *testing.T) { 13 | ctrl := gomock.NewController(t) 14 | defer ctrl.Finish() 15 | 16 | c := NewMockUserRepo(ctrl) 17 | fmt.Println(c) 18 | 19 | type args struct { 20 | ctx context.Context 21 | input CreateUserInput 22 | } 23 | tests := []struct { 24 | name string 25 | args args 26 | beforeTest func(userRepo *MockUserRepo) 27 | want User 28 | wantErr bool 29 | }{ 30 | { 31 | name: "success creating new user", 32 | args: args{ 33 | ctx: context.TODO(), 34 | input: CreateUserInput{ 35 | Email: "john.doe@example.com", 36 | FirstName: "John", 37 | LastName: "Doe", 38 | Gender: GenderMale, 39 | }, 40 | }, 41 | beforeTest: func(userRepo *MockUserRepo) { 42 | userRepo.EXPECT(). 43 | Create( 44 | context.TODO(), 45 | CreateUserInput{ 46 | Email: "john.doe@example.com", 47 | FirstName: "John", 48 | LastName: "Doe", 49 | Gender: GenderMale, 50 | }, 51 | ). 52 | Return( 53 | User{ 54 | ID: 1, 55 | Email: "john.doe@example.com", 56 | FirstName: "John", 57 | LastName: "Doe", 58 | Gender: GenderMale, 59 | }, 60 | nil, 61 | ) 62 | }, 63 | want: User{ 64 | ID: 1, 65 | Email: "john.doe@example.com", 66 | FirstName: "John", 67 | LastName: "Doe", 68 | Gender: GenderMale, 69 | }, 70 | }, 71 | // Next test cases. 72 | // ... 73 | // ... 74 | } 75 | for _, tt := range tests { 76 | t.Run(tt.name, func(t *testing.T) { 77 | mockUserRepo := NewMockUserRepo(ctrl) 78 | 79 | w := ®isterUserUseCase{ 80 | userRepo: mockUserRepo, 81 | } 82 | 83 | if tt.beforeTest != nil { 84 | tt.beforeTest(mockUserRepo) 85 | } 86 | 87 | got, err := w.Execute(tt.args.ctx, tt.args.input) 88 | if (err != nil) != tt.wantErr { 89 | t.Errorf("registerUserUseCase.Execute() error = %v, wantErr %v", err, tt.wantErr) 90 | return 91 | } 92 | if !reflect.DeepEqual(got, tt.want) { 93 | t.Errorf("registerUserUseCase.Execute() = %v, want %v", got, tt.want) 94 | } 95 | }) 96 | } 97 | } 98 | 99 | func TestNewRegisterUserUseCase(t *testing.T) { 100 | type args struct { 101 | userRepo UserRepo 102 | } 103 | tests := []struct { 104 | name string 105 | args args 106 | want RegisterUserUseCase 107 | }{ 108 | { 109 | name: "success creating RegisterUserUseCase instance", 110 | args: args{}, 111 | want: NewRegisterUserUseCase(nil), 112 | }, 113 | } 114 | for _, tt := range tests { 115 | t.Run(tt.name, func(t *testing.T) { 116 | if got := NewRegisterUserUseCase(tt.args.userRepo); !reflect.DeepEqual(got, tt.want) { 117 | t.Errorf("NewRegisterUserUseCase() = %v, want %v", got, tt.want) 118 | } 119 | }) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/user/delivery_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "net/http" 7 | "net/http/httptest" 8 | reflect "reflect" 9 | "strings" 10 | "testing" 11 | 12 | gomock "github.com/golang/mock/gomock" 13 | ) 14 | 15 | func Test_userDelivery_Register(t *testing.T) { 16 | ctrl := gomock.NewController(t) 17 | defer ctrl.Finish() 18 | 19 | type args struct { 20 | w http.ResponseWriter 21 | r *http.Request 22 | } 23 | tests := []struct { 24 | name string 25 | args args 26 | beforeTest func(registerUserUseCase *MockRegisterUserUseCase) 27 | wantStatusCode int 28 | wantHeader http.Header 29 | wantBody string 30 | }{ 31 | { 32 | name: "success registering new user", 33 | args: args{ 34 | w: httptest.NewRecorder(), 35 | r: httptest.NewRequest( 36 | http.MethodPost, 37 | "http://0.0.0.0/users", 38 | strings.NewReader(` 39 | { 40 | "first_name": "John", 41 | "last_name": "Doe", 42 | "email": "john.doe@example.com", 43 | "gender": "male" 44 | } 45 | `), 46 | ), 47 | }, 48 | beforeTest: func(registerUserUseCase *MockRegisterUserUseCase) { 49 | registerUserUseCase.EXPECT(). 50 | Execute( 51 | context.TODO(), 52 | CreateUserInput{ 53 | Email: "john.doe@example.com", 54 | FirstName: "John", 55 | LastName: "Doe", 56 | Gender: GenderMale, 57 | }, 58 | ). 59 | Return( 60 | User{ 61 | ID: 1, 62 | Email: "john.doe@example.com", 63 | FirstName: "John", 64 | LastName: "Doe", 65 | Gender: GenderMale, 66 | }, 67 | nil, 68 | ) 69 | }, 70 | wantStatusCode: http.StatusCreated, 71 | wantHeader: http.Header{"Content-Type": {"application/json"}}, 72 | wantBody: `{"id":1,"email":"john.doe@example.com","first_name":"John","last_name":"Doe","gender":"male"}`, 73 | }, 74 | } 75 | for _, tt := range tests { 76 | t.Run(tt.name, func(t *testing.T) { 77 | mockRegisterUserUC := NewMockRegisterUserUseCase(ctrl) 78 | 79 | u := &userDelivery{ 80 | registerUserUseCase: mockRegisterUserUC, 81 | } 82 | 83 | if tt.beforeTest != nil { 84 | tt.beforeTest(mockRegisterUserUC) 85 | } 86 | 87 | u.Register(tt.args.w, tt.args.r) 88 | 89 | rec := tt.args.w.(*httptest.ResponseRecorder) 90 | res := rec.Result() 91 | 92 | if !reflect.DeepEqual(res.StatusCode, tt.wantStatusCode) { 93 | t.Errorf("userDelivery.Register() = %v, want %v", res.StatusCode, tt.wantStatusCode) 94 | } 95 | 96 | if !reflect.DeepEqual(res.Header, tt.wantHeader) { 97 | t.Errorf("userDelivery.Register() = %v, want %v", res.Header, tt.wantHeader) 98 | } 99 | 100 | bodyBuffer := new(bytes.Buffer) 101 | bodyBuffer.ReadFrom(res.Body) 102 | body := strings.TrimSpace(bodyBuffer.String()) 103 | 104 | if !reflect.DeepEqual(body, tt.wantBody) { 105 | t.Errorf("userDelivery.Register() = %s, want %s", bodyBuffer.String(), tt.wantBody) 106 | } 107 | }) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/go.sum: -------------------------------------------------------------------------------- 1 | github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= 2 | github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= 3 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 4 | github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= 5 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 6 | github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w= 7 | github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= 8 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 9 | github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 10 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 11 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 12 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 13 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 14 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 15 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 16 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 17 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 18 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 19 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 20 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 21 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 23 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 24 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 25 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 26 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 27 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 28 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 29 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 30 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 31 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 32 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 33 | -------------------------------------------------------------------------------- /src/etc/demo-redlock/redlock.go: -------------------------------------------------------------------------------- 1 | package demoredlock 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "math/rand" 7 | "time" 8 | 9 | redis "github.com/go-redis/redis/v8" 10 | ) 11 | 12 | var ( 13 | ErrAcquireResource = errors.New("locker: failed acquiring resource") 14 | ErrReleaseResource = errors.New("locker: failed releasing resource") 15 | ) 16 | 17 | var scriptLock = ` 18 | if redis.call("EXISTS", KEYS[1]) == 1 then 19 | return 0 20 | end 21 | 22 | return redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2]) 23 | ` 24 | 25 | var scriptUnlock = ` 26 | if redis.call("GET", KEYS[1]) == ARGV[1] then 27 | return redis.call("DEL", KEYS[1]) 28 | else 29 | return 0 30 | end 31 | ` 32 | 33 | type Locker interface { 34 | Lock(ctx context.Context) error 35 | Unlock(ctx context.Context) error 36 | } 37 | 38 | type DLM struct { 39 | redisClients []*redis.Client 40 | quorum int 41 | expiration time.Duration 42 | drift time.Duration 43 | } 44 | 45 | func NewDLM(redisClients []*redis.Client, expiration time.Duration, drift time.Duration) *DLM { 46 | return &DLM{ 47 | redisClients: redisClients, 48 | expiration: expiration, 49 | drift: drift, 50 | quorum: len(redisClients)/2 + 1, 51 | } 52 | } 53 | 54 | func (dlm *DLM) NewLocker(name string) Locker { 55 | return newLocker(dlm, name) 56 | } 57 | 58 | type locker struct { 59 | redisClients []*redis.Client 60 | expiration time.Duration 61 | drift time.Duration 62 | quorum int 63 | name string 64 | value string 65 | } 66 | 67 | func newLocker(dlm *DLM, name string) Locker { 68 | return &locker{ 69 | redisClients: dlm.redisClients, 70 | quorum: dlm.quorum, 71 | name: name, 72 | value: generateRandomString(), 73 | expiration: dlm.expiration, 74 | drift: dlm.drift, 75 | } 76 | } 77 | 78 | func (l *locker) Lock(ctx context.Context) error { 79 | totalSuccess := 0 80 | 81 | for _, rc := range l.redisClients { 82 | start := time.Now() 83 | 84 | status, err := rc.Eval(ctx, scriptLock, []string{l.name}, l.value, l.expiration.Milliseconds()).Result() 85 | if err != nil { 86 | return err 87 | } 88 | 89 | ok := status == "OK" 90 | 91 | now := time.Now() 92 | isTimeValid := (l.expiration - (now.Sub(start) - l.drift)) > 0 93 | 94 | if ok && isTimeValid { 95 | totalSuccess++ 96 | } 97 | } 98 | 99 | if totalSuccess < l.quorum { 100 | l.Unlock(ctx) 101 | return ErrAcquireResource 102 | } 103 | 104 | return nil 105 | } 106 | 107 | func (l *locker) Unlock(ctx context.Context) error { 108 | totalSuccess := 0 109 | 110 | for _, rc := range l.redisClients { 111 | 112 | status, err := rc.Eval(ctx, scriptUnlock, []string{l.name}, l.value).Result() 113 | if err != nil { 114 | return err 115 | } 116 | 117 | if status != int64(0) { 118 | totalSuccess++ 119 | } 120 | } 121 | 122 | if totalSuccess < l.quorum { 123 | return ErrReleaseResource 124 | } 125 | 126 | return nil 127 | } 128 | 129 | func generateRandomString() string { 130 | var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 131 | b := make([]rune, 132 | time.Now().Unix()%64) 133 | for i := range b { 134 | b[i] = letterRunes[rand.Intn(len(letterRunes))] 135 | } 136 | return string(b) 137 | } 138 | -------------------------------------------------------------------------------- /src/etc/demo-unit-test/internal/user/repo_mock_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: internal/user/repo.go 3 | 4 | // Package user is a generated GoMock package. 5 | package user 6 | 7 | import ( 8 | context "context" 9 | reflect "reflect" 10 | 11 | gomock "github.com/golang/mock/gomock" 12 | ) 13 | 14 | // MockUserRepo is a mock of UserRepo interface. 15 | type MockUserRepo struct { 16 | ctrl *gomock.Controller 17 | recorder *MockUserRepoMockRecorder 18 | } 19 | 20 | // MockUserRepoMockRecorder is the mock recorder for MockUserRepo. 21 | type MockUserRepoMockRecorder struct { 22 | mock *MockUserRepo 23 | } 24 | 25 | // NewMockUserRepo creates a new mock instance. 26 | func NewMockUserRepo(ctrl *gomock.Controller) *MockUserRepo { 27 | mock := &MockUserRepo{ctrl: ctrl} 28 | mock.recorder = &MockUserRepoMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use. 33 | func (m *MockUserRepo) EXPECT() *MockUserRepoMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // Create mocks base method. 38 | func (m *MockUserRepo) Create(ctx context.Context, input CreateUserInput) (User, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "Create", ctx, input) 41 | ret0, _ := ret[0].(User) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // Create indicates an expected call of Create. 47 | func (mr *MockUserRepoMockRecorder) Create(ctx, input interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockUserRepo)(nil).Create), ctx, input) 50 | } 51 | 52 | // Destroy mocks base method. 53 | func (m *MockUserRepo) Destroy(ctx context.Context, id int64) error { 54 | m.ctrl.T.Helper() 55 | ret := m.ctrl.Call(m, "Destroy", ctx, id) 56 | ret0, _ := ret[0].(error) 57 | return ret0 58 | } 59 | 60 | // Destroy indicates an expected call of Destroy. 61 | func (mr *MockUserRepoMockRecorder) Destroy(ctx, id interface{}) *gomock.Call { 62 | mr.mock.ctrl.T.Helper() 63 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Destroy", reflect.TypeOf((*MockUserRepo)(nil).Destroy), ctx, id) 64 | } 65 | 66 | // FindOneByID mocks base method. 67 | func (m *MockUserRepo) FindOneByID(ctx context.Context, id int64) (User, error) { 68 | m.ctrl.T.Helper() 69 | ret := m.ctrl.Call(m, "FindOneByID", ctx, id) 70 | ret0, _ := ret[0].(User) 71 | ret1, _ := ret[1].(error) 72 | return ret0, ret1 73 | } 74 | 75 | // FindOneByID indicates an expected call of FindOneByID. 76 | func (mr *MockUserRepoMockRecorder) FindOneByID(ctx, id interface{}) *gomock.Call { 77 | mr.mock.ctrl.T.Helper() 78 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOneByID", reflect.TypeOf((*MockUserRepo)(nil).FindOneByID), ctx, id) 79 | } 80 | 81 | // Update mocks base method. 82 | func (m *MockUserRepo) Update(ctx context.Context, input UpdateUserInput) (User, error) { 83 | m.ctrl.T.Helper() 84 | ret := m.ctrl.Call(m, "Update", ctx, input) 85 | ret0, _ := ret[0].(User) 86 | ret1, _ := ret[1].(error) 87 | return ret0, ret1 88 | } 89 | 90 | // Update indicates an expected call of Update. 91 | func (mr *MockUserRepoMockRecorder) Update(ctx, input interface{}) *gomock.Call { 92 | mr.mock.ctrl.T.Helper() 93 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockUserRepo)(nil).Update), ctx, input) 94 | } 95 | -------------------------------------------------------------------------------- /src/etc/clean-arch-ddd-cqrs-es/internal/domain/task/task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/syafdia/clean-arch-ddd-cqrs-es/internal/domain" 8 | "github.com/syafdia/clean-arch-ddd-cqrs-es/internal/domain/user" 9 | ) 10 | 11 | type TaskID int64 12 | 13 | type TaskStatus string 14 | 15 | const ( 16 | TaskStatusNotStarted TaskStatus = "NOT_STARTED" 17 | TaskStatusInProgress TaskStatus = "IN_PROGRESS" 18 | TaskStatusInReview TaskStatus = "IN_REVIEW" 19 | TaskStatusCompleted TaskStatus = "COMPLETED" 20 | ) 21 | 22 | type Task struct { 23 | ID TaskID 24 | AuthorID user.UserID 25 | AssigneeID user.UserID 26 | Title Title 27 | Description Description 28 | Status TaskStatus 29 | Tags []Tag 30 | CreatedAt time.Time 31 | UpdatedAt time.Time 32 | } 33 | 34 | func NewTask( 35 | authorID user.UserID, 36 | title Title, 37 | description Description, 38 | createdAt time.Time, 39 | ) (*Task, error) { 40 | err := title.Validate() 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | err = description.Validate() 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | return &Task{ 51 | Title: title, 52 | Description: description, 53 | CreatedAt: createdAt, 54 | }, nil 55 | 56 | } 57 | 58 | func (t *Task) ChangeStatus(newStatus TaskStatus) error { 59 | // TODO 60 | return nil 61 | } 62 | 63 | func (t *Task) AddTag(newTag Tag, updatedAt time.Time) error { 64 | for _, tag := range t.Tags { 65 | if tag.ID == newTag.ID { 66 | return domain.NewValidationError( 67 | ErrCodeTagAlreadyExists, 68 | fmt.Sprintf("tag %s already exists on this task", newTag.Name)) 69 | } 70 | } 71 | 72 | t.Tags = append(t.Tags, newTag) 73 | t.UpdatedAt = updatedAt 74 | 75 | return nil 76 | } 77 | 78 | func (t *Task) RemoveTag(newTag Tag) error { 79 | // TODO 80 | return domain.ErrNotImplemented 81 | } 82 | 83 | type Title string 84 | 85 | func (t Title) Validate() error { 86 | if len(t) < 4 { 87 | return domain.NewValidationError(ErrCodeInvalidTitleLength, "must be at least 6 characters") 88 | } 89 | 90 | if len(t) > 32 { 91 | return domain.NewValidationError(ErrCodeInvalidTitleLength, "the maximum value is 32 characters") 92 | } 93 | 94 | return nil 95 | } 96 | 97 | type Description string 98 | 99 | func (d Description) Validate() error { 100 | if len(d) > 256 { 101 | return domain.NewValidationError(ErrCodeInvalidDescriptionLength, "the maximum value is 256 characters") 102 | } 103 | 104 | return nil 105 | } 106 | 107 | type TagID int64 108 | 109 | type Tag struct { 110 | ID TagID 111 | Name TagName 112 | CreatedAt time.Time 113 | } 114 | 115 | func NewTag(name TagName, createdAt time.Time) (*Tag, error) { 116 | err := name.Validate() 117 | if err != nil { 118 | return nil, err 119 | } 120 | 121 | return &Tag{ 122 | Name: name, 123 | CreatedAt: createdAt, 124 | }, nil 125 | } 126 | 127 | type TagName string 128 | 129 | func (tn TagName) Validate() error { 130 | if len(tn) < 2 { 131 | return domain.NewValidationError(ErrCodeInvalidTagNameLength, "must be at least 2 characters") 132 | } 133 | 134 | if len(tn) > 8 { 135 | return domain.NewValidationError(ErrCodeInvalidTagNameLength, "the maximum value is 8 characters") 136 | } 137 | 138 | return nil 139 | } 140 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/usecase/auth/find_resources_by_compliance_use_case.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | e "github.com/syafdia/xaam/internal/domain/entity" 8 | ae "github.com/syafdia/xaam/internal/domain/entity/auth" 9 | "github.com/syafdia/xaam/internal/domain/repo" 10 | "github.com/syafdia/xaam/pkg/slices" 11 | ) 12 | 13 | type FindResourcesByComplianceUseCase interface { 14 | Execute(ctx context.Context, request ae.FindResourcesByComplianceRequest) (map[e.Resource][]e.Action, error) 15 | } 16 | 17 | type findResourcesByComplianceUC struct { 18 | PolicyRepo repo.PolicyRepo 19 | ResourceRepo repo.ResourceRepo 20 | } 21 | 22 | func NewFindResourcesByComplianceUseCase( 23 | PolicyRepo repo.PolicyRepo, 24 | resourceRepo repo.ResourceRepo, 25 | ) FindResourcesByComplianceUseCase { 26 | return &findResourcesByComplianceUC{ 27 | PolicyRepo: PolicyRepo, 28 | ResourceRepo: resourceRepo, 29 | } 30 | } 31 | 32 | func (f *findResourcesByComplianceUC) Execute( 33 | ctx context.Context, 34 | request ae.FindResourcesByComplianceRequest, 35 | ) (map[e.Resource][]e.Action, error) { 36 | actionIndustries, err := f.PolicyRepo.FindMultipleByIDandTargetType(ctx, request.IndustryID, e.TargetTypeIndustry) 37 | if err != nil { 38 | return nil, e.WrapErr(http.StatusInternalServerError, err) 39 | } 40 | 41 | actionLegalEntities, err := f.PolicyRepo.FindMultipleByIDandTargetType(ctx, request.LegalEntityID, e.TargetTypeLegalEntity) 42 | if err != nil { 43 | return nil, e.WrapErr(http.StatusInternalServerError, err) 44 | } 45 | 46 | actionIntersections := f.getIntersections(actionIndustries, actionLegalEntities) 47 | resourceIDWithActions := f.groupActionsByResourceID(actionIntersections) 48 | 49 | resourceIDs := []int64{} 50 | for resourceID := range resourceIDWithActions { 51 | resourceIDs = append(resourceIDs, resourceID) 52 | } 53 | 54 | idWithResource, err := f.ResourceRepo.FindMultipleByIDs(ctx, resourceIDs) 55 | if err != nil { 56 | return nil, e.WrapErr(http.StatusInternalServerError, err) 57 | } 58 | 59 | resourceWithActions := map[e.Resource][]e.Action{} 60 | for resourceID, actions := range resourceIDWithActions { 61 | resource, ok := idWithResource[resourceID] 62 | if !ok { 63 | continue 64 | } 65 | 66 | if !slices.ContainsStr(request.Resources, resource.Name) { 67 | continue 68 | } 69 | 70 | resourceWithActions[resource] = actions 71 | } 72 | 73 | return resourceWithActions, nil 74 | } 75 | 76 | func (f *findResourcesByComplianceUC) getIntersections(xs []e.Action, ys []e.Action) []e.Action { 77 | actionWithTotal := map[e.Action]int{} 78 | 79 | for _, x := range xs { 80 | actionWithTotal[x] = actionWithTotal[x] + 1 81 | } 82 | 83 | for _, y := range ys { 84 | actionWithTotal[y] = actionWithTotal[y] + 1 85 | } 86 | 87 | results := []e.Action{} 88 | for action, total := range actionWithTotal { 89 | if total == 1 { 90 | continue 91 | } 92 | 93 | results = append(results, action) 94 | } 95 | 96 | return results 97 | } 98 | 99 | func (f *findResourcesByComplianceUC) groupActionsByResourceID(actions []e.Action) map[int64][]e.Action { 100 | resourceIDWithActions := map[int64][]e.Action{} 101 | 102 | for _, action := range actions { 103 | _, ok := resourceIDWithActions[action.ResourceID] 104 | if !ok { 105 | resourceIDWithActions[action.ResourceID] = []e.Action{} 106 | } 107 | 108 | resourceIDWithActions[action.ResourceID] = append(resourceIDWithActions[action.ResourceID], action) 109 | } 110 | 111 | return resourceIDWithActions 112 | } 113 | -------------------------------------------------------------------------------- /src/etc/demo-event-sourcing/internal/domain/task/task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/syafdia/demo-es/internal/domain" 8 | ) 9 | 10 | type TaskStatus string 11 | 12 | const ( 13 | TaskStatusNotStarted TaskStatus = "NOT_STARTED" 14 | TaskStatusInProgress TaskStatus = "IN_PROGRESS" 15 | TaskStatusInReview TaskStatus = "IN_REVIEW" 16 | TaskStatusCompleted TaskStatus = "COMPLETED" 17 | ) 18 | 19 | var ( 20 | ErrTaskTitleTooShort = errors.New("task: title is too shorts") 21 | ErrTaskStatusTransitionNotAllowed = errors.New("task: status transition is not allowed") 22 | ErrTaskCanNotContainsMoreTag = errors.New("task: can't add more tag") 23 | ErrTaskDiscussionContentTooShort = errors.New("task: content is too shorts") 24 | ErrTaskCanNotVoteCompletedTask = errors.New("task: can not vote completed task") 25 | ) 26 | 27 | type Tag struct { 28 | ID domain.ID 29 | Name string 30 | } 31 | 32 | func NewTag(name string) Tag { 33 | return Tag{ 34 | ID: domain.NewID(), 35 | Name: name, 36 | } 37 | } 38 | 39 | type Task struct { 40 | ID domain.ID 41 | Title string 42 | Description string 43 | Status TaskStatus 44 | Tags []Tag 45 | TotalVotes int 46 | 47 | events []domain.Event 48 | version int 49 | } 50 | 51 | func NewTask(title string, description string) (Task, error) { 52 | if len(title) < 4 { 53 | return Task{}, ErrTaskTitleTooShort 54 | } 55 | 56 | // Implement some business validation here 57 | // ... 58 | // ... 59 | 60 | t := Task{} 61 | t.AddEvent(TaskCreated{ 62 | ID: domain.NewID(), 63 | Title: title, 64 | Description: description, 65 | Status: TaskStatusNotStarted, 66 | Timestamp: time.Now(), 67 | }) 68 | 69 | return t, nil 70 | } 71 | 72 | func (t *Task) ChangeStatus(newStatus TaskStatus) error { 73 | if t.Status == TaskStatusNotStarted { 74 | if newStatus == TaskStatusCompleted { 75 | return ErrTaskStatusTransitionNotAllowed 76 | } 77 | } 78 | 79 | // Implement some business validation here 80 | // ... 81 | // ... 82 | 83 | t.AddEvent(TaskStatusChanged{ 84 | ID: t.ID, 85 | Status: newStatus, 86 | Timestamp: time.Now(), 87 | }) 88 | 89 | return nil 90 | } 91 | 92 | func (t *Task) AddTag(tag Tag) error { 93 | if len(t.Tags) == 4 { 94 | return ErrTaskCanNotContainsMoreTag 95 | } 96 | 97 | t.AddEvent(TaskTagAdded{ 98 | ID: t.ID, 99 | Tag: tag, 100 | Timestamp: time.Now(), 101 | }) 102 | 103 | return nil 104 | } 105 | 106 | func (t *Task) Upvote() error { 107 | if t.Status == TaskStatusCompleted { 108 | return ErrTaskCanNotVoteCompletedTask 109 | } 110 | 111 | t.AddEvent(TaskUpvoted{ 112 | ID: t.ID, 113 | Timestamp: time.Now(), 114 | }) 115 | 116 | return nil 117 | } 118 | 119 | func (t *Task) Downvote() error { 120 | if t.Status == TaskStatusCompleted { 121 | return ErrTaskCanNotVoteCompletedTask 122 | } 123 | 124 | t.AddEvent(TaskDownvoted{ 125 | ID: t.ID, 126 | Timestamp: time.Now(), 127 | }) 128 | 129 | return nil 130 | } 131 | 132 | func (t *Task) AddEvent(event domain.Event) { 133 | t.events = append(t.events, event) 134 | t.ApplyEvent(event) 135 | } 136 | 137 | func (t *Task) ApplyEvent(event domain.Event) { 138 | switch e := event.(type) { 139 | case TaskCreated: 140 | t.ID = e.ID 141 | t.Title = e.Title 142 | t.Description = e.Description 143 | t.Status = e.Status 144 | 145 | case TaskStatusChanged: 146 | t.Status = e.Status 147 | 148 | case TaskTagAdded: 149 | t.Tags = append(t.Tags, e.Tag) 150 | 151 | case TaskUpvoted: 152 | t.TotalVotes++ 153 | 154 | case TaskDownvoted: 155 | t.TotalVotes-- 156 | } 157 | 158 | t.version++ 159 | } 160 | 161 | func (t *Task) GetEvents() []domain.Event { 162 | return t.events 163 | } 164 | -------------------------------------------------------------------------------- /src/etc/go-calculator/cmd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | "strconv" 10 | "time" 11 | 12 | "github.com/gin-gonic/gin" 13 | "github.com/gorilla/websocket" 14 | "github.com/redis/go-redis/v9" 15 | ) 16 | 17 | type Module struct { 18 | redisClient *redis.Client 19 | } 20 | 21 | func PingHandler(m *Module) gin.HandlerFunc { 22 | return func(c *gin.Context) { 23 | log.Printf("[PingHandler] Start process, headers: %+v\n", c.Request.Header) 24 | _, err := m.redisClient.Ping(c).Result() 25 | if err != nil { 26 | c.JSON(http.StatusInternalServerError, gin.H{ 27 | "message": fmt.Sprintf("Redis is not OK, err: %v", err), 28 | }) 29 | return 30 | } 31 | 32 | c.JSON(http.StatusOK, gin.H{ 33 | "message": "OK", 34 | "data": "pong", 35 | }) 36 | } 37 | } 38 | 39 | func AddHandler(m *Module) gin.HandlerFunc { 40 | return func(c *gin.Context) { 41 | urlQuery := c.Request.URL.Query() 42 | rawA := urlQuery.Get("a") 43 | rawB := urlQuery.Get("b") 44 | 45 | log.Printf("[AddHandler] Start process, a:%v, b:%v\n", rawA, rawB) 46 | 47 | maxNum, _ := strconv.Atoi(envMaxNumber) 48 | a, _ := strconv.Atoi(rawA) 49 | b, _ := strconv.Atoi(rawB) 50 | 51 | if a > maxNum || b > maxNum { 52 | c.JSON(http.StatusBadRequest, gin.H{ 53 | "message": fmt.Sprintf("max allowed a or b value is %d", maxNum), 54 | }) 55 | return 56 | } 57 | 58 | cacheKey := fmt.Sprintf("add:%d+%d", a, b) 59 | var result int 60 | 61 | val, err := m.redisClient.Get(c, cacheKey).Result() 62 | if err != nil { 63 | if !errors.Is(err, redis.Nil) { 64 | c.JSON(http.StatusInternalServerError, gin.H{ 65 | "message": fmt.Sprintf("Redis is not OK when GET, err: %v", err), 66 | }) 67 | return 68 | } 69 | 70 | result = doHeavyCalculation(a, b) 71 | 72 | _, err := m.redisClient.Set(c, cacheKey, result, 30*time.Second).Result() 73 | if err != nil { 74 | c.JSON(http.StatusInternalServerError, gin.H{ 75 | "message": fmt.Sprintf("Redis is not OK when SET, err: %v", err), 76 | }) 77 | return 78 | } 79 | } else { 80 | result, _ = strconv.Atoi(val) 81 | } 82 | 83 | c.JSON(http.StatusOK, gin.H{ 84 | "message": "OK", 85 | "data": result, 86 | }) 87 | } 88 | } 89 | 90 | func WebSocketHandler(m *Module) gin.HandlerFunc { 91 | 92 | wsUpgrader := websocket.Upgrader{ 93 | ReadBufferSize: 1024, 94 | WriteBufferSize: 1024, 95 | } 96 | return func(c *gin.Context) { 97 | log.Printf("[WebSocketHandler] Start process") 98 | 99 | conn, err := wsUpgrader.Upgrade(c.Writer, c.Request, nil) 100 | if err != nil { 101 | return 102 | } 103 | defer conn.Close() 104 | 105 | i := 0 106 | for { 107 | i++ 108 | conn.WriteMessage(websocket.TextMessage, []byte("New message (#"+strconv.Itoa(i)+")")) 109 | time.Sleep(3 * time.Second) 110 | } 111 | } 112 | } 113 | 114 | func doHeavyCalculation(a int, b int) int { 115 | time.Sleep(3 * time.Second) 116 | return a + b 117 | } 118 | 119 | var ( 120 | envHttpServerPort = os.Getenv("HTTP_SERVER_PORT") 121 | envMaxNumber = os.Getenv("MAX_NUMBER") 122 | envRedisURL = os.Getenv("REDIS_URL") 123 | ) 124 | 125 | func main() { 126 | log.Println("[main] Will start the app") 127 | httpServerPort := "8080" 128 | if envHttpServerPort != "" { 129 | httpServerPort = envHttpServerPort 130 | } 131 | 132 | redisOpt, err := redis.ParseURL(envRedisURL) 133 | if err != nil { 134 | log.Panicf("[main] Failed parsing Redis URL, err: %s", err) 135 | } 136 | 137 | redisClient := redis.NewClient(redisOpt) 138 | 139 | module := &Module{ 140 | redisClient: redisClient, 141 | } 142 | 143 | r := gin.Default() 144 | 145 | r.GET("/ping", PingHandler(module)) 146 | r.GET("/add", AddHandler(module)) 147 | r.GET("/ws", WebSocketHandler(module)) 148 | 149 | r.Run(fmt.Sprintf(":%s", httpServerPort)) 150 | } 151 | -------------------------------------------------------------------------------- /src/etc/XAAM/internal/domain/usecase/auth/find_resources_by_compliance_use_case_test.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/golang/mock/gomock" 10 | "github.com/syafdia/xaam/internal/domain/entity" 11 | e "github.com/syafdia/xaam/internal/domain/entity" 12 | ae "github.com/syafdia/xaam/internal/domain/entity/auth" 13 | "github.com/syafdia/xaam/internal/domain/repo" 14 | mock_repo "github.com/syafdia/xaam/internal/mock/domain/repo" 15 | ) 16 | 17 | func Test_findResourcesByComplianceUC_Execute(t *testing.T) { 18 | ctl := gomock.NewController(t) 19 | defer ctl.Finish() 20 | 21 | mockPolicyRepo := mock_repo.NewMockPolicyRepo(ctl) 22 | mockResourceRepo := mock_repo.NewMockResourceRepo(ctl) 23 | 24 | type fields struct { 25 | PolicyRepo repo.PolicyRepo 26 | ResourceRepo repo.ResourceRepo 27 | } 28 | type args struct { 29 | ctx context.Context 30 | request ae.FindResourcesByComplianceRequest 31 | } 32 | tests := []struct { 33 | name string 34 | fields fields 35 | args args 36 | beforeTest func() 37 | want map[e.Resource][]e.Action 38 | wantErr bool 39 | }{ 40 | { 41 | name: "on failed FindMultipleByIDandTargetType for industries", 42 | fields: fields{ 43 | PolicyRepo: mockPolicyRepo, 44 | ResourceRepo: mockResourceRepo, 45 | }, 46 | args: args{ 47 | ctx: context.TODO(), 48 | request: ae.FindResourcesByComplianceRequest{ 49 | IndustryID: 1, 50 | LegalEntityID: 2, 51 | }, 52 | }, 53 | beforeTest: func() { 54 | mockPolicyRepo.EXPECT(). 55 | FindMultipleByIDandTargetType(context.TODO(), int64(1), entity.TargetTypeIndustry). 56 | Return(nil, errors.New("whoops, error")) 57 | 58 | }, 59 | want: nil, 60 | wantErr: true, 61 | }, 62 | { 63 | name: "on failed FindMultipleByIDandTargetType for legal entity", 64 | fields: fields{ 65 | PolicyRepo: mockPolicyRepo, 66 | ResourceRepo: mockResourceRepo, 67 | }, 68 | args: args{ 69 | ctx: context.TODO(), 70 | request: ae.FindResourcesByComplianceRequest{ 71 | IndustryID: 1, 72 | LegalEntityID: 2, 73 | }, 74 | }, 75 | beforeTest: func() { 76 | 77 | mockPolicyRepo.EXPECT(). 78 | FindMultipleByIDandTargetType(context.TODO(), int64(1), entity.TargetTypeIndustry). 79 | Return([]e.Action{ 80 | {ID: 1, Name: "compliant", ResourceID: 1}, 81 | }, nil) 82 | 83 | mockPolicyRepo.EXPECT(). 84 | FindMultipleByIDandTargetType(context.TODO(), int64(2), entity.TargetTypeLegalEntity). 85 | Return(nil, errors.New("whoops, error")) 86 | 87 | }, 88 | want: nil, 89 | wantErr: true, 90 | }, 91 | { 92 | name: "on success", 93 | fields: fields{ 94 | PolicyRepo: mockPolicyRepo, 95 | ResourceRepo: mockResourceRepo, 96 | }, 97 | args: args{ 98 | ctx: context.TODO(), 99 | request: ae.FindResourcesByComplianceRequest{ 100 | IndustryID: 1, 101 | LegalEntityID: 2, 102 | Resources: []string{"VA"}, 103 | }, 104 | }, 105 | beforeTest: func() { 106 | 107 | mockPolicyRepo.EXPECT(). 108 | FindMultipleByIDandTargetType(context.TODO(), int64(1), entity.TargetTypeIndustry). 109 | Return([]e.Action{ 110 | {ID: 1, Name: "compliant", ResourceID: 10}, 111 | }, nil) 112 | 113 | mockPolicyRepo.EXPECT(). 114 | FindMultipleByIDandTargetType(context.TODO(), int64(2), entity.TargetTypeLegalEntity). 115 | Return([]e.Action{ 116 | {ID: 1, Name: "compliant", ResourceID: 10}, 117 | {ID: 3, Name: "compliant", ResourceID: 11}, 118 | }, nil) 119 | 120 | mockResourceRepo.EXPECT(). 121 | FindMultipleByIDs(context.TODO(), []int64{10}). 122 | Return(map[int64]e.Resource{ 123 | 10: {ID: 10, Name: "VA"}, 124 | }, nil) 125 | 126 | }, 127 | want: map[e.Resource][]e.Action{ 128 | {ID: 10, Name: "VA"}: { 129 | {ID: 1, Name: "compliant", ResourceID: 10}, 130 | }, 131 | }, 132 | wantErr: false, 133 | }, 134 | } 135 | for _, tt := range tests { 136 | t.Run(tt.name, func(t *testing.T) { 137 | f := &findResourcesByComplianceUC{ 138 | PolicyRepo: tt.fields.PolicyRepo, 139 | ResourceRepo: tt.fields.ResourceRepo, 140 | } 141 | 142 | if tt.beforeTest != nil { 143 | tt.beforeTest() 144 | } 145 | 146 | got, err := f.Execute(tt.args.ctx, tt.args.request) 147 | if (err != nil) != tt.wantErr { 148 | t.Errorf("findResourcesByComplianceUC.Execute() error = %v, wantErr %v", err, tt.wantErr) 149 | return 150 | } 151 | if !reflect.DeepEqual(got, tt.want) { 152 | t.Errorf("findResourcesByComplianceUC.Execute() = %v, want %v", got, tt.want) 153 | } 154 | }) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/distributed/semaphore/semaphore.go: -------------------------------------------------------------------------------- 1 | package semaphore 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/go-redis/redis/v8" 9 | "github.com/google/uuid" 10 | ) 11 | 12 | const ( 13 | luaScriptReleaseSemaphore = ` 14 | local keyResourceWithTimeout = KEYS[1] 15 | local keyResources = KEYS[2] 16 | local token = ARGV[1] 17 | 18 | local score = redis.call('ZSCORE', keyResourceWithTimeout, token) 19 | if not score or tonumber(score) == 0 then 20 | return 0 21 | end 22 | 23 | redis.call('LPUSH', keyResources, token) 24 | redis.call('ZREM', keyResourceWithTimeout, token) 25 | 26 | return 1 27 | ` 28 | 29 | luaScriptCleanupDanglingResources = ` 30 | local keyResourceWithTimeout = KEYS[1] 31 | local keyResources = KEYS[2] 32 | local minStr = ARGV[1] 33 | local maxStr = ARGV[2] 34 | 35 | local danglingResources = redis.call('ZRANGEBYSCORE', keyResourceWithTimeout, minStr, maxStr) 36 | if #danglingResources == 0 then 37 | return 1 38 | end 39 | 40 | redis.call('ZREM', keyResourceWithTimeout, unpack(danglingResources)) 41 | redis.call('LPUSH', keyResources, unpack(danglingResources)) 42 | 43 | return 1 44 | ` 45 | ) 46 | 47 | type Semaphore struct { 48 | maxConcurrency int 49 | name string 50 | redisClient *redis.Client 51 | acquireTimeout time.Duration 52 | } 53 | 54 | func New( 55 | maxConcurrency int, 56 | name string, 57 | redisClient *redis.Client, 58 | acquireTimeout time.Duration, 59 | ) (*Semaphore, error) { 60 | s := &Semaphore{ 61 | maxConcurrency: maxConcurrency, 62 | name: name, 63 | redisClient: redisClient, 64 | acquireTimeout: acquireTimeout, 65 | } 66 | 67 | err := s.initResources() 68 | if err != nil { 69 | return nil, fmt.Errorf("semaphore: failed on init resources, %w", err) 70 | } 71 | 72 | return s, nil 73 | } 74 | 75 | // Acquire will borrow resource from the pool. 76 | func (s *Semaphore) Acquire(ctx context.Context) (string, error) { 77 | err := s.cleanupDanglingResources(ctx) 78 | if err != nil { 79 | return "", fmt.Errorf("semaphore: failed when cleanup dangling resources, %w", err) 80 | } 81 | 82 | vals, err := s.redisClient.BRPop(ctx, s.acquireTimeout, s.keyResources()).Result() 83 | if err != nil { 84 | return "", fmt.Errorf("semaphore: failed when acquire, %w", err) 85 | } 86 | 87 | if len(vals) != 2 { 88 | return "", fmt.Errorf("semaphore: no resource available") 89 | } 90 | 91 | token := vals[1] 92 | 93 | member := &redis.Z{Score: s.timeNowUnix(), Member: token} 94 | _, err = s.redisClient.ZAdd(ctx, s.keyResourceWithTimeout(), member).Result() 95 | if err != nil { 96 | return "", fmt.Errorf("semaphore: failed when set timeout, %w", err) 97 | } 98 | 99 | return token, nil 100 | } 101 | 102 | // Release will return back borrowed resources to pool. 103 | func (s *Semaphore) Release(ctx context.Context, token string) error { 104 | _, err := s.redisClient. 105 | Eval(ctx, luaScriptReleaseSemaphore, []string{s.keyResourceWithTimeout(), s.keyResources()}, token). 106 | Result() 107 | 108 | if err != nil { 109 | return fmt.Errorf("semaphore: failed when release, %w", err) 110 | } 111 | 112 | return nil 113 | } 114 | 115 | func (s *Semaphore) keyResources() string { 116 | return fmt.Sprintf("semaphore:resources:%s", s.name) 117 | } 118 | 119 | func (s *Semaphore) keyResourceWithTimeout() string { 120 | return fmt.Sprintf("semaphore:resource_timeout:%s", s.name) 121 | } 122 | 123 | func (s *Semaphore) timeNowUnix() float64 { 124 | return float64(time.Now().Unix()) 125 | } 126 | 127 | func (s *Semaphore) initResources() error { 128 | ctx, cancelFunc := context.WithTimeout(context.Background(), 60*time.Second) 129 | defer cancelFunc() 130 | 131 | tokens := s.generateTokens() 132 | _, err := s.redisClient.Pipelined(ctx, func(p redis.Pipeliner) error { 133 | _, err := p.Del(ctx, s.keyResources(), s.keyResourceWithTimeout()).Result() 134 | if err != nil { 135 | return err 136 | } 137 | 138 | _, err = p.LPush(ctx, s.keyResources(), tokens).Result() 139 | return err 140 | }) 141 | 142 | return err 143 | } 144 | 145 | func (s *Semaphore) generateTokens() []string { 146 | tokens := []string{} 147 | for i := 0; i < s.maxConcurrency; i++ { 148 | tokens = append(tokens, uuid.New().String()) 149 | } 150 | 151 | return tokens 152 | } 153 | 154 | func (s *Semaphore) cleanupDanglingResources(ctx context.Context) error { 155 | minStr := "-inf" 156 | maxStr := fmt.Sprintf("%d", int64(s.timeNowUnix()-s.acquireTimeout.Seconds())) 157 | 158 | _, err := s.redisClient. 159 | Eval( 160 | ctx, 161 | luaScriptCleanupDanglingResources, 162 | []string{s.keyResourceWithTimeout(), s.keyResources()}, 163 | minStr, 164 | maxStr, 165 | ). 166 | Result() 167 | 168 | return err 169 | } 170 | --------------------------------------------------------------------------------