├── cover
└── .keep
├── envs
├── common.env
└── api.env
├── logger
├── go.mod
├── logger.go
└── go.sum
├── util
├── ctxhelper
│ ├── go.mod
│ ├── ctxhelper_test.go
│ ├── ctxhelper.coverprofile
│ ├── ctxhelper.go
│ └── go.sum
├── userinfo
│ ├── go.mod
│ └── userinfo.go
└── go.mod
├── protos
├── types
│ ├── go.mod
│ ├── field_mask.proto
│ ├── enums.proto
│ ├── types.graphql.go
│ ├── go.sum
│ └── enums.pb.go
├── google
│ └── api
│ │ ├── annotation.proto
│ │ ├── httpbody.proto
│ │ ├── field_behaviour.proto
│ │ └── field_mask.proto
└── include
│ └── graphql.proto
├── .idea
├── vcs.xml
├── .gitignore
├── modules.xml
└── platform.iml
├── config
├── fx.go
├── go.mod
└── config.go
├── servers
├── prometheusServer
│ ├── fx.go
│ ├── prometheus.yml
│ └── server.go
├── cleanup
│ ├── fx.go
│ └── cleanup.go
├── openTracing
│ └── tracer
│ │ └── jaeger
│ │ ├── fx.go
│ │ └── jaeger.go
├── grpc
│ ├── fx.go
│ ├── middleware
│ │ ├── public_endpoint.go
│ │ ├── interceptors.go
│ │ └── auth.go
│ ├── go.mod
│ ├── register.go
│ ├── server.go
│ └── grpcErrors
│ │ └── grpcErrors.go
├── graphql
│ ├── middleware
│ │ ├── ctx-change.go
│ │ ├── cors.go
│ │ ├── logger.go
│ │ └── request-id.go
│ ├── register.go
│ └── server.go
├── healthcheck
│ ├── health-check.go
│ └── db
│ │ └── db.go
├── rest
│ ├── middleware
│ │ ├── cors.go
│ │ ├── request-id.go
│ │ └── logger.go
│ ├── register.go
│ └── server.go
└── metrics
│ ├── manager.go
│ └── metrics.go
├── client
└── grpcClient
│ ├── fx.go
│ └── client.go
├── Dockerfile
├── db
├── fx.go
├── go.mod
└── connection.go
├── modules
├── authentication
│ └── v1
│ │ ├── generate.go
│ │ ├── external-svc
│ │ ├── logout_test.go
│ │ ├── login.go
│ │ ├── login_test.go
│ │ ├── logout.go
│ │ ├── login_callback_test.go
│ │ ├── external_suite_test.go
│ │ ├── service.go
│ │ └── login_callback.go
│ │ └── pb
│ │ ├── authentication.proto
│ │ └── pb.graphql.go
└── user-profile
│ └── v1
│ ├── generate.go
│ ├── external-svc
│ ├── service.go
│ ├── get_user_profile_by_sub.go
│ ├── get_user_profile.go
│ ├── get_user_profile_test.go
│ ├── get_user_profile_by_sub_test.go
│ └── external_suite_test.go
│ ├── internal-svc
│ ├── service.go
│ ├── update_user_profile.go
│ ├── delete_user_profile.go
│ ├── create_user_profile.go
│ ├── delete_user_profile_test.go
│ ├── update_user_profile_test.go
│ ├── internal_suite_test.go
│ └── create_user_profile_test.go
│ ├── models
│ └── user_profile.go
│ └── pb
│ ├── user_profile_int.proto
│ ├── user_profile.proto
│ ├── user_profile_grpc.pb.go
│ ├── user_profile_int_grpc.pb.go
│ ├── user_profile_int.pb.validate.go
│ └── user_profile.pb.validate.go
├── .editorconfig
├── cmd
└── api
│ ├── main.go
│ ├── app.go
│ ├── provider.go
│ └── invoker.go
├── .gitignore
├── telementry
└── prometheus
│ └── prometheus.yml
├── docker-compose.yml
├── Makefile
├── LICENSE
├── config.yml
├── .github
└── workflows
│ └── go.yml
├── go.mod
└── README.md
/cover/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/envs/common.env:
--------------------------------------------------------------------------------
1 | CONFIG_NAME=config
2 | CONFIG_DIRECTORY=/app/
3 |
--------------------------------------------------------------------------------
/logger/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/deqode/GoArcc/logger
2 |
3 | go 1.16
4 |
5 | require go.uber.org/zap v1.18.1
6 |
--------------------------------------------------------------------------------
/util/ctxhelper/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/deqode/GoArcc/util/ctxhelper
2 |
3 | go 1.16
4 |
5 | require go.uber.org/zap v1.18.1
6 |
--------------------------------------------------------------------------------
/envs/api.env:
--------------------------------------------------------------------------------
1 | POSTGRES_HOST=postgres
2 | POSTGRES_PORT=5432
3 | POSTGRES_USER=postgres
4 | POSTGRES_PASSWORD=postgres
5 | POSTGRES_DB_NAME=postgres
6 |
--------------------------------------------------------------------------------
/util/userinfo/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/deqode/GoArcc/util/userinfo
2 |
3 | go 1.16
4 |
5 | require (
6 | google.golang.org/grpc v1.39.0
7 | gorm.io/gorm v1.21.11
8 | )
9 |
--------------------------------------------------------------------------------
/protos/types/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/deqode/GoArcc/protos/types
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/graphql-go/graphql v0.7.9
7 | google.golang.org/protobuf v1.27.1
8 | )
9 |
--------------------------------------------------------------------------------
/util/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/deqode/GoArcc/util
2 |
3 | go 1.16
4 |
5 | require (
6 | go.uber.org/zap v1.18.1
7 | google.golang.org/grpc v1.39.0
8 | gorm.io/gorm v1.21.11
9 | )
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/config/fx.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import "go.uber.org/fx"
4 |
5 | // ProviderFx ConfigProviderFx : Provider of GetConfig
6 | var ProviderFx = fx.Options(
7 | fx.Provide(
8 | GetConfig,
9 | ),
10 | )
11 |
--------------------------------------------------------------------------------
/servers/prometheusServer/fx.go:
--------------------------------------------------------------------------------
1 | package prometheusServer
2 |
3 | /*import "go.uber.org/fx"
4 |
5 | var InitPromthesiusServerFx = fx.Options(
6 | fx.Provide(
7 | InitPromthesiusServer,
8 | ),
9 | )
10 | */
11 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/protos/types/field_mask.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package goarcc.types;
4 |
5 | option go_package = "github.com/deqode/GoArcc/protos/types";
6 |
7 | message FieldMask {
8 | repeated string field_mask = 1;
9 | }
10 |
--------------------------------------------------------------------------------
/servers/cleanup/fx.go:
--------------------------------------------------------------------------------
1 | package cleanup
2 |
3 | import "go.uber.org/fx"
4 |
5 | // CleanupFx will provide the constructor method GetCleanupConfig.
6 | var CleanupFx = fx.Options(
7 | fx.Provide(
8 | GetCleanupConfig,
9 | ),
10 | )
11 |
--------------------------------------------------------------------------------
/servers/openTracing/tracer/jaeger/fx.go:
--------------------------------------------------------------------------------
1 | package jaeger
2 |
3 | import "go.uber.org/fx"
4 |
5 | // JaegerTracerFx : Constructor method for InitJaeger.
6 | var JaegerTracerFx = fx.Options(
7 | fx.Provide(
8 | InitJaeger,
9 | ),
10 | )
11 |
--------------------------------------------------------------------------------
/client/grpcClient/fx.go:
--------------------------------------------------------------------------------
1 | package grpcClient
2 |
3 | import "go.uber.org/fx"
4 |
5 | // ConnectionFx GrpcClientConnectionFx : Provider of GetGrpcClientConnection
6 | var ConnectionFx = fx.Options(
7 | fx.Provide(
8 | GetGrpcClientConnection,
9 | ),
10 | )
11 |
--------------------------------------------------------------------------------
/servers/grpc/fx.go:
--------------------------------------------------------------------------------
1 | package grpc
2 |
3 | import "go.uber.org/fx"
4 |
5 | // InitGrpcBeforeServingFx : constructor for InitGrpcBeforeServing method.
6 | var InitGrpcBeforeServingFx = fx.Options(
7 | fx.Provide(
8 | InitGrpcBeforeServing,
9 | ),
10 | )
11 |
--------------------------------------------------------------------------------
/util/ctxhelper/ctxhelper_test.go:
--------------------------------------------------------------------------------
1 | package ctxhelper
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "testing"
7 | )
8 |
9 | func TestContext(t *testing.T) {
10 | ctx := NewContextComponentName(context.Background(), "Shivang")
11 | fmt.Println(ComponentNameFromContext(ctx))
12 | }
13 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.16.4-stretch
2 |
3 | #ENV GO111MODULE=on go get program@latest
4 |
5 | #RUN go get -u golang.org/x/tools/
6 |
7 | WORKDIR /app
8 |
9 | #COPY go.mod .
10 | #COPY go.sum .
11 | #
12 | #RUN go mod download
13 | #
14 | #COPY . .
15 | #
16 | #RUN make build-api
17 |
18 | #RUN ls bin
19 |
--------------------------------------------------------------------------------
/.idea/platform.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/config/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/deqode/GoArcc/config
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/spf13/viper v1.8.1
7 | go.uber.org/fx v1.13.1
8 | go.uber.org/zap v1.18.1
9 | github.com/deqode/GoArcc/logger v0.0.0-00010101000000-000000000000
10 | )
11 |
12 | replace github.com/deqode/GoArcc/logger => ./../logger
13 |
--------------------------------------------------------------------------------
/db/fx.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import "go.uber.org/fx"
4 |
5 | // DatabaseConnectionFx : constructor provide a initialised db connection with the given port.
6 | // No need to close the connection of the db because we have a single pool with single db instance.
7 | var DatabaseConnectionFx = fx.Options(
8 | fx.Provide(
9 | NewConnection,
10 | ),
11 | )
12 |
--------------------------------------------------------------------------------
/protos/types/enums.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package goarcc.types;
4 |
5 | option go_package = "github.com/deqode/GoArcc/protos/types";
6 |
7 | enum VCSProviders {
8 | UNKNOWN = 0;
9 | GITHUB = 1;
10 | GITLAB = 2;
11 | BITBUCKET = 3;
12 | }
13 |
14 | /*
15 | protoc -I ./ --go_out=paths=source_relative:. --graphql_out=paths=source_relative:. enums.proto && goimports -w .
16 | */
17 |
--------------------------------------------------------------------------------
/modules/authentication/v1/generate.go:
--------------------------------------------------------------------------------
1 | package authentication
2 |
3 | // - protoc plugins must be in $PATH
4 |
5 | //generate from authentication.proto
6 | //go:generate bash -e -o pipefail -c "protoc -I . --proto_path=../../../protos --grpc-gateway_out=./ --grpc-gateway_opt logtostderr=true --go_out=./ --go-grpc_out=./ --go-grpc_opt=require_unimplemented_servers=false --graphql_out=./ --validate_out='lang=go:./' ./pb/authentication.proto"
7 |
--------------------------------------------------------------------------------
/modules/authentication/v1/external-svc/logout_test.go:
--------------------------------------------------------------------------------
1 | package external_svc_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | )
6 |
7 | var _ = Describe("Logout Test", func() {
8 | Describe("Logout Test", func() {
9 | By("By Rpc Calls")
10 |
11 | Context("Get an error when auth0 domain is not present or empty", func() {
12 | It("Failed precondition err must be responded", func() {
13 |
14 | })
15 | })
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/servers/prometheusServer/prometheus.yml:
--------------------------------------------------------------------------------
1 | global:
2 | scrape_interval: 10s
3 | evaluation_interval: 10s
4 |
5 | scrape_configs:
6 | - job_name: 'prometheus'
7 | static_configs:
8 | - targets: ['localhost:9090']
9 |
10 | - job_name: 'system'
11 | static_configs:
12 | - targets: ['localhost:9100']
13 |
14 | - job_name: 'Alfred Monitoring'
15 | static_configs:
16 | - targets: ['localhost:7070']
17 |
--------------------------------------------------------------------------------
/servers/grpc/middleware/public_endpoint.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | // TODO: Priority zero, validate if these are available are registered, otherwise fail on startup
4 | var publicEndpoint = []string{
5 | "/goarcc.vcs_connection.v1.VCSConnections/ListAllSupportedVCSProviders",
6 | "/goarcc.authentication.v1.Authentications/Login",
7 | "/goarcc.authentication.v1.Authentications/LoginCallback",
8 | "/goarcc.user_profile.v1.user-profile/GetUserProfileBySub",
9 | }
10 |
--------------------------------------------------------------------------------
/db/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/deqode/GoArcc/db
2 |
3 | go 1.16
4 |
5 | require (
6 | go.uber.org/fx v1.13.1
7 | go.uber.org/zap v1.18.1
8 | github.com/deqode/GoArcc/config v0.0.0-00010101000000-000000000000
9 | github.com/deqode/GoArcc/logger v0.0.0-00010101000000-000000000000
10 | gorm.io/driver/postgres v1.1.0
11 | gorm.io/gorm v1.21.11
12 | )
13 |
14 | replace (
15 | github.com/deqode/GoArcc/config => ./../config
16 | github.com/deqode/GoArcc/logger => ./../logger
17 | )
18 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | ; indicate this is the root of the project
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 |
7 | end_of_line = LF
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | indent_style = space
12 | indent_size = 4
13 |
14 | [Makefile]
15 | indent_style = tab
16 |
17 | [makefile]
18 | indent_style = tab
19 |
20 | [*.go]
21 | indent_style = tab
22 |
23 | [*.yaml]
24 | indent_style = space
25 | indent_size = 2
26 |
27 | [*.yml]
28 | indent_style = space
29 | indent_size = 2
30 |
--------------------------------------------------------------------------------
/cmd/api/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/deqode/GoArcc/logger"
5 | "go.uber.org/zap"
6 | )
7 |
8 | //todo : Alarm !!!!!! Do not touch the invocation sequence, either you might go through sleepless nights
9 | // main : entry point
10 | func main() {
11 | //logger initialize before app starts because in provider we need logger
12 | logger.Init(logger.Config{
13 | LogLevel: zap.DebugLevel, // TODO: Take this level from config
14 | Development: false,
15 | })
16 | GetApp().Run()
17 | }
18 |
--------------------------------------------------------------------------------
/cmd/api/app.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "go.uber.org/fx"
4 |
5 | //GetApp : Get App will return the fx app.
6 | // Fx App contains invokers , providers , lifecycles etc.
7 | // When we start the application using fx app then used providers will initialises first.
8 | // After that invoker will invoked automatically.
9 | // Note: Invokers will be executed in the same order.
10 | func GetApp() *fx.App {
11 | opts := make([]fx.Option, 0)
12 | opts = GetProviderOptions()
13 | opts = append(opts, GetInvokersOptions())
14 | return fx.New(
15 | opts...,
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/util/ctxhelper/ctxhelper.coverprofile:
--------------------------------------------------------------------------------
1 | mode: atomic
2 | github.com/deqode/GoArcc/util/ctxhelper/ctxhelper.go:20.89,22.2 1 1
3 | github.com/deqode/GoArcc/util/ctxhelper/ctxhelper.go:25.84,28.2 2 1
4 | github.com/deqode/GoArcc/util/ctxhelper/ctxhelper.go:32.80,34.2 1 0
5 | github.com/deqode/GoArcc/util/ctxhelper/ctxhelper.go:37.75,40.2 2 0
6 | github.com/deqode/GoArcc/util/ctxhelper/ctxhelper.go:44.74,46.2 1 0
7 | github.com/deqode/GoArcc/util/ctxhelper/ctxhelper.go:49.69,52.2 2 0
8 | github.com/deqode/GoArcc/util/ctxhelper/ctxhelper.go:56.80,58.2 1 0
9 | github.com/deqode/GoArcc/util/ctxhelper/ctxhelper.go:61.75,64.2 2 0
10 |
--------------------------------------------------------------------------------
/servers/graphql/middleware/ctx-change.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "google.golang.org/grpc/metadata"
5 | "net/http"
6 | )
7 |
8 | // ChangeContext : ChangeContext is responsible to add the authorization token to the context so that
9 | // in grpc server we can easily get authentication token and verify the authentication token.
10 | func ChangeContext(h http.Handler) http.Handler {
11 | return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
12 | request = request.WithContext(metadata.AppendToOutgoingContext(request.Context(), "Authorization", request.Header.Get("Authorization")))
13 | h.ServeHTTP(writer, request)
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/generate.go:
--------------------------------------------------------------------------------
1 | package user_profile
2 |
3 | // generate from pb/user_profile.proto
4 | //go:generate bash -e -o pipefail -c "protoc -I . --proto_path=../../../protos --grpc-gateway_out=./ --grpc-gateway_opt logtostderr=true --go_out=./ --go-grpc_out=./ --go-grpc_opt=require_unimplemented_servers=false --graphql_out=./ --validate_out='lang=go:./' ./pb/user_profile.proto"
5 |
6 | // generate from pb/user_profile_int.proto
7 | //go:generate bash -e -o pipefail -c "protoc -I . --proto_path=../../../protos --proto_path=./pb --grpc-gateway_out=./ --grpc-gateway_opt logtostderr=true --go_out=./ --go-grpc_out=./ --go-grpc_opt=require_unimplemented_servers=false --validate_out='lang=go:./' ./pb/user_profile_int.proto"
8 |
--------------------------------------------------------------------------------
/cmd/api/provider.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/deqode/GoArcc/client/grpcClient"
5 | "github.com/deqode/GoArcc/config"
6 | "github.com/deqode/GoArcc/db"
7 | "github.com/deqode/GoArcc/servers/cleanup"
8 | "github.com/deqode/GoArcc/servers/grpc"
9 | "github.com/deqode/GoArcc/servers/openTracing/tracer/jaeger"
10 | "go.uber.org/fx"
11 | )
12 |
13 | // GetProviderOptions ProviderOptions: Global Constructor.
14 | // Sequence in fx does not matter, So you can write in any order you want.
15 | func GetProviderOptions() []fx.Option {
16 | return []fx.Option{
17 | config.ProviderFx,
18 | grpc.InitGrpcBeforeServingFx,
19 | db.DatabaseConnectionFx,
20 | cleanup.CleanupFx,
21 | jaeger.JaegerTracerFx,
22 | grpcClient.ConnectionFx,
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # The file containing environment variables
2 | .env
3 | ### Go template
4 | # Binaries for programs and plugins
5 | *.exe
6 | *.exe~
7 | *.dll
8 | *.so
9 | *.dylib
10 |
11 | # Test binary, built with `go test -c`
12 | *.test
13 |
14 | # Output of the go coverage tool, specifically when used with LiteIDE
15 | *.out
16 |
17 | # Ignore IDE specific files
18 | .idea/
19 | .vscode/
20 |
21 | #ignore build file
22 | goarcc
23 | go_build_goarcc
24 | go_build_platform
25 | go_build_backend_
26 | cmd/api/repositories/
27 | cmd/api/repositories
28 | vendor
29 |
30 | # temporary remove for production
31 | ./tests
32 |
33 |
34 | #test file
35 | coverage.out
36 | coverage.html
37 | .cover
38 | #cover/
39 |
40 | # Build artifacts
41 | bin/
42 |
43 | cache
44 | .docker/
45 |
--------------------------------------------------------------------------------
/telementry/prometheus/prometheus.yml:
--------------------------------------------------------------------------------
1 | global:
2 | scrape_interval: 15s # By default, scrape targets every 15 seconds.
3 |
4 | # Attach these labels to any time series or alerts when communicating with
5 | # external systems (federation, remote storage, Alertmanager).
6 | external_labels:
7 | monitor: 'codelab-monitor'
8 |
9 | # A scrape configuration containing exactly one endpoint to scrape:
10 | # Here it's Prometheus itself.
11 | scrape_configs:
12 | # The job name is added as a label `job=` to any timeseries scraped from this config.
13 | - job_name: 'prometheus'
14 |
15 | # Override the global default and scrape targets from this job every 5 seconds.
16 | scrape_interval: 5s
17 |
18 | static_configs:
19 | - targets: ['localhost:9090']
20 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.3'
2 |
3 | volumes:
4 | postgres:
5 |
6 | services:
7 |
8 | backend_api:
9 | image: golang:1.16.4-stretch
10 | working_dir: /app
11 | volumes:
12 | - .:/app
13 | - /tmp/docker_cache/go_mod:/go/pkg/mod
14 | env_file:
15 | - envs/common.env
16 | - envs/api.env
17 | command: bin/goarcc
18 | depends_on:
19 | - postgres
20 |
21 | jaeger:
22 | image: jaegertracing/all-in-one:latest
23 | ports:
24 | - "6831:6831/udp"
25 | - "16686:16686"
26 |
27 | postgres:
28 | image: postgres
29 | environment:
30 | POSTGRES_DB: postgres
31 | POSTGRES_USER: postgres
32 | POSTGRES_PASSWORD: postgres
33 | PGDATA: /data/postgres
34 | volumes:
35 | - postgres:/data/postgres
36 |
--------------------------------------------------------------------------------
/servers/graphql/register.go:
--------------------------------------------------------------------------------
1 | package graphql
2 |
3 | import (
4 | "github.com/deqode/GoArcc/logger"
5 | userProfilePb "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
6 | "github.com/ysugimoto/grpc-graphql-gateway/runtime"
7 | "go.uber.org/zap"
8 | "google.golang.org/grpc"
9 | )
10 |
11 | // RegisterGraphqlModules Todo : Whenever any new modules will be in goarcc : it must be registered in below method
12 | /*
13 | RegisterGraphqlModules: Mapping the services with the single graphql endpoint
14 | */
15 | func RegisterGraphqlModules(mux *runtime.ServeMux, conn *grpc.ClientConn) error {
16 | if err := userProfilePb.RegisterUserProfilesGraphqlHandler(mux, conn); err != nil {
17 | logger.Log.Fatal("failed to start HTTP gateway", zap.String("reason", err.Error()))
18 | return err
19 | }
20 | return nil
21 | }
22 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/external-svc/service.go:
--------------------------------------------------------------------------------
1 | package external_svc
2 |
3 | import (
4 | "github.com/deqode/GoArcc/config"
5 | model "github.com/deqode/GoArcc/modules/user-profile/v1/models"
6 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
7 | "google.golang.org/grpc"
8 | "gorm.io/gorm"
9 | )
10 |
11 | type userProfilesServer struct {
12 | db *gorm.DB
13 | config *config.Config
14 | grpcClient *grpc.ClientConn
15 | }
16 |
17 | func NewUserProfilesServer(
18 | db *gorm.DB,
19 | config *config.Config,
20 | grpcClientConn *grpc.ClientConn,
21 | ) pb.UserProfilesServer {
22 |
23 | //initial migration of databases: schema migration
24 | model.InitialMigrationUserProfile(db)
25 | return &userProfilesServer{
26 | db: db,
27 | config: config,
28 | grpcClient: grpcClientConn,
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/modules/authentication/v1/external-svc/login.go:
--------------------------------------------------------------------------------
1 | package external_svc
2 |
3 | import (
4 | "context"
5 | "crypto/rand"
6 | "encoding/base64"
7 | "github.com/deqode/GoArcc/modules/authentication/v1/pb"
8 | "github.com/golang/protobuf/ptypes/empty"
9 | "google.golang.org/grpc/codes"
10 | "google.golang.org/grpc/status"
11 | )
12 |
13 | func (s *authenticationServer) Login(context.Context, *empty.Empty) (*pb.LoginResponse, error) {
14 | b := make([]byte, 32)
15 | _, err := rand.Read(b)
16 | if err != nil {
17 | return nil, err
18 | }
19 | state := base64.StdEncoding.EncodeToString(b)
20 | if s.authenticator == nil {
21 | return nil, status.Error(codes.Internal, "Authenticator not initialised")
22 | }
23 | url := s.authenticator.Config.AuthCodeURL(state)
24 |
25 | return &pb.LoginResponse{
26 | Url: url,
27 | }, nil
28 | }
29 |
--------------------------------------------------------------------------------
/servers/grpc/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/deqode/GoArcc/servers/grpc
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/go-redis/redis/v8 v8.11.0
7 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
8 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
9 | github.com/justinas/alice v1.2.0
10 | github.com/opentracing/opentracing-go v1.2.0
11 | github.com/pkg/errors v0.9.1
12 | github.com/prometheus/client_golang v1.11.0
13 | go.uber.org/fx v1.13.1
14 | go.uber.org/zap v1.18.1
15 | github.com/deqode/GoArcc/config v0.0.0-00010101000000-000000000000
16 | github.com/deqode/GoArcc/logger v0.0.0-00010101000000-000000000000
17 | google.golang.org/grpc v1.39.0
18 | gopkg.in/square/go-jose.v2 v2.6.0
19 | gorm.io/gorm v1.21.11
20 | )
21 |
22 | replace (
23 | github.com/deqode/GoArcc/config => ./../../config
24 | github.com/deqode/GoArcc/logger => ./../../logger
25 | )
26 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/internal-svc/service.go:
--------------------------------------------------------------------------------
1 | package internal_svc
2 |
3 | import (
4 | "github.com/deqode/GoArcc/config"
5 | model "github.com/deqode/GoArcc/modules/user-profile/v1/models"
6 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
7 | "google.golang.org/grpc"
8 | "gorm.io/gorm"
9 | )
10 |
11 | // Internal Service Configuration
12 | type userProfileInServer struct {
13 | db *gorm.DB
14 | config *config.Config
15 | grpcClient *grpc.ClientConn
16 | }
17 |
18 | func NewUserProfileInServer(
19 | db *gorm.DB,
20 | config *config.Config,
21 | grpcClientConn *grpc.ClientConn,
22 | ) pb.UserProfileInternalServer {
23 | //initial migration of databases: schema migration
24 | model.InitialMigrationUserProfile(db)
25 | return &userProfileInServer{
26 | db: db,
27 | config: config,
28 | grpcClient: grpcClientConn,
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/internal-svc/update_user_profile.go:
--------------------------------------------------------------------------------
1 | package internal_svc
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
6 | "google.golang.org/grpc/codes"
7 | "google.golang.org/grpc/status"
8 | )
9 |
10 | func (s *userProfileInServer) UpdateUserProfile(ctx context.Context, in *pb.UpdateUserProfileRequest) (*pb.UserProfile, error) {
11 | if in.GetUserProfile() == nil {
12 | return nil, status.Error(codes.FailedPrecondition, "UserProfile to update is not provided")
13 | }
14 | if err := in.Validate(); err != nil {
15 | return nil, err
16 | }
17 | return &pb.UserProfile{
18 | Id: "",
19 | Sub: "",
20 | Name: "",
21 | UserName: "",
22 | Email: "",
23 | PhoneNumber: "",
24 | ExternalSource: 0,
25 | ProfilePicUrl: "",
26 | TokenValidTill: nil,
27 | }, nil
28 | }
29 |
--------------------------------------------------------------------------------
/modules/authentication/v1/external-svc/login_test.go:
--------------------------------------------------------------------------------
1 | package external_svc_test
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/modules/authentication/v1/pb"
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | )
9 |
10 | var _ = Describe("Login", func() {
11 | var (
12 | authServer pb.AuthenticationsServer
13 | //authenticator *external_svc.Authenticator
14 | )
15 | BeforeEach(func() {
16 | authServer = AuthServerTest
17 | //authenticator = AuthenticatorTest
18 | })
19 | Describe("Getting External VCS login url", func() {
20 | By("By Rpc Calls")
21 |
22 | // positive test case
23 | Context("Get an error if returned url is empty", func() {
24 | It("Failed precondition err must be responded", func() {
25 | res, err := authServer.Login(context.Background(), nil)
26 | Expect(err).Should(BeNil())
27 | Expect(res).ShouldNot(BeNil())
28 | })
29 | })
30 | })
31 | })
32 |
--------------------------------------------------------------------------------
/protos/types/types.graphql.go:
--------------------------------------------------------------------------------
1 | // Code generated by proroc-gen-graphql, DO NOT EDIT.
2 | package types
3 |
4 | import (
5 | "github.com/graphql-go/graphql"
6 | )
7 |
8 | var (
9 | gql__enum_VCSProviders *graphql.Enum // enum VCSProviders in enums.proto
10 | )
11 |
12 | func Gql__enum_VCSProviders() *graphql.Enum {
13 | if gql__enum_VCSProviders == nil {
14 | gql__enum_VCSProviders = graphql.NewEnum(graphql.EnumConfig{
15 | Name: "Types_Enum_VCSProviders",
16 | Values: graphql.EnumValueConfigMap{
17 | "UNKNOWN": &graphql.EnumValueConfig{
18 | Value: VCSProviders(0),
19 | },
20 | "GITHUB": &graphql.EnumValueConfig{
21 | Value: VCSProviders(1),
22 | },
23 | "GITLAB": &graphql.EnumValueConfig{
24 | Value: VCSProviders(2),
25 | },
26 | "BITBUCKET": &graphql.EnumValueConfig{
27 | Value: VCSProviders(3),
28 | },
29 | },
30 | })
31 | }
32 | return gql__enum_VCSProviders
33 | }
34 |
--------------------------------------------------------------------------------
/protos/types/go.sum:
--------------------------------------------------------------------------------
1 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
2 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
3 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
4 | github.com/graphql-go/graphql v0.7.9 h1:5Va/Rt4l5g3YjwDnid3vFfn43faaQBq7rMcIZ0VnV34=
5 | github.com/graphql-go/graphql v0.7.9/go.mod h1:k6yrAYQaSP59DC5UVxbgxESlmVyojThKdORUqGDGmrI=
6 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
7 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
8 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
9 | google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
10 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
11 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/internal-svc/delete_user_profile.go:
--------------------------------------------------------------------------------
1 | package internal_svc
2 |
3 | import (
4 | "context"
5 | model "github.com/deqode/GoArcc/modules/user-profile/v1/models"
6 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
7 | "github.com/golang/protobuf/ptypes/empty"
8 | "google.golang.org/grpc/codes"
9 | "google.golang.org/grpc/status"
10 | )
11 |
12 | func (s *userProfileInServer) DeleteUserProfile(ctx context.Context, in *pb.DeleteUserProfileRequest) (*empty.Empty, error) {
13 | //request validation
14 | if in == nil {
15 | return nil, status.Error(codes.FailedPrecondition, "Request can't be nil")
16 | }
17 | if err := in.Validate(); err != nil {
18 | return nil, err
19 | }
20 | //by default it will delete with primary key.
21 | // ie: Delete From Account where id = in.id
22 | tx := s.db.Where("id = ?", in.Id).Delete(&model.UserProfile{})
23 | if tx.Error != nil {
24 | return nil, tx.Error
25 | }
26 | return &empty.Empty{}, nil
27 | }
28 |
--------------------------------------------------------------------------------
/servers/healthcheck/health-check.go:
--------------------------------------------------------------------------------
1 | package healthcheck
2 |
3 | import (
4 | "github.com/deqode/GoArcc/config"
5 | postgreshealth "github.com/deqode/GoArcc/servers/healthcheck/db"
6 | "github.com/dimiro1/health"
7 | "gorm.io/gorm"
8 | "net/http"
9 | )
10 |
11 | // RunHealthCheckServer : will start the health check server
12 | func RunHealthCheckServer(config *config.Config, db *gorm.DB) error {
13 |
14 | //postgres health check
15 | postgresql := postgreshealth.NewPostgresChecker(db)
16 | handler := health.NewHandler()
17 | postgresql.Check()
18 | handler.AddChecker("Postgres", postgresql)
19 | http.Handle("/health/", handler)
20 | return http.ListenAndServe(config.HealthCheck.Host+":"+config.HealthCheck.Port, nil)
21 | }
22 |
23 | // HealthCheckRunner : will responsible to run health check server in non blocking way.
24 | func HealthCheckRunner(config *config.Config, db *gorm.DB) error {
25 | go func() {
26 | _ = RunHealthCheckServer(config, db)
27 | }()
28 | return nil
29 | }
30 |
--------------------------------------------------------------------------------
/servers/rest/middleware/cors.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import "net/http"
4 |
5 | //setupCorsResponse : is the helper method which is responsible to set the header in the response on every request.
6 | func setupCorsResponse(w *http.ResponseWriter, req *http.Request) {
7 | (*w).Header().Set("Access-Control-Allow-Origin", "*")
8 | (*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH")
9 | (*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
10 | }
11 |
12 | // AddCors : AddCors is the middleware which will add the cors in the response and response
13 | // with status ok on preflight request.
14 | func AddCors(h http.Handler) http.Handler {
15 | return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
16 | setupCorsResponse(&writer, request)
17 | if request.Method == "OPTIONS" {
18 | writer.WriteHeader(http.StatusOK)
19 | }
20 | h.ServeHTTP(writer, request)
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/servers/metrics/manager.go:
--------------------------------------------------------------------------------
1 | package metrics
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/servers/grpc/grpcErrors"
6 | "google.golang.org/grpc"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | // Manager Manager
12 | type Manager struct {
13 | metrics Metrics
14 | }
15 |
16 | // NewMetricsManager InterceptorManager constructor
17 | func NewMetricsManager(metrics Metrics) *Manager {
18 | return &Manager{metrics: metrics}
19 | }
20 |
21 | // Metrics :
22 | func (im *Manager) Metrics(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
23 | start := time.Now()
24 | resp, err := handler(ctx, req)
25 | var status = http.StatusOK
26 | if err != nil {
27 | status = grpcErrors.MapGRPCErrCodeToHTTPStatus(grpcErrors.ParseGRPCErrStatusCode(err))
28 | }
29 | im.metrics.ObserveResponseTime(status, info.FullMethod, info.FullMethod, time.Since(start).Seconds())
30 | im.metrics.IncHits(status, info.FullMethod, info.FullMethod)
31 |
32 | return resp, err
33 | }
34 |
--------------------------------------------------------------------------------
/modules/authentication/v1/external-svc/logout.go:
--------------------------------------------------------------------------------
1 | package external_svc
2 |
3 | import (
4 | "context"
5 | "github.com/golang/protobuf/ptypes/empty"
6 | "net/url"
7 | )
8 |
9 | func (s *authenticationServer) Logout(context.Context, *empty.Empty) (*empty.Empty, error) {
10 | domain := s.config.Auth.Auth0Domain
11 |
12 | logoutURL, err := url.Parse("http://" + domain)
13 |
14 | if err != nil {
15 | //http.Error(w, err.Error(), http.StatusInternalServerError)
16 | return nil, err
17 | }
18 |
19 | logoutURL.Path += "/v1/authenticationServer/logout"
20 | parameters := url.Values{}
21 |
22 | var scheme string
23 | scheme = "http"
24 |
25 | returnTo, err := url.Parse(scheme + "://" + "http://localhost:8082")
26 | if err != nil {
27 | //http.Error(w, err.Error(), http.StatusInternalServerError)
28 | return nil, err
29 | }
30 | parameters.Add("returnTo", returnTo.String())
31 | parameters.Add("client_id", s.config.Auth.Auth0ClientID)
32 | logoutURL.RawQuery = parameters.Encode()
33 |
34 | return &empty.Empty{}, nil
35 | }
36 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | get-linter:
2 | go get github.com/mgechev/revive
3 |
4 | get-richgo:
5 | go get -u github.com/kyoh86/richgo
6 |
7 | get-dupl:
8 | go get -u github.com/mibk/dupl
9 |
10 | get-errcheck:
11 | go get -u github.com/kisielk/errcheck
12 |
13 | get-gocritic:
14 | go get -u github.com/go-critic/go-critic/cmd/gocritic
15 |
16 | run-gocritic:
17 | gocritic check ./...
18 |
19 | run-errcheck: get-errcheck
20 | errcheck ./...
21 |
22 | run-dupl: get-dupl
23 | dupl */**.go
24 |
25 |
26 |
27 | run-linter: get-linter
28 | revive -formatter friendly ./...
29 |
30 | get-fmt:
31 | go get fmt
32 |
33 | run-fmt:
34 | go fmt ./...
35 |
36 | tidy:
37 | go mod tidy
38 | go mod vendor
39 |
40 | # Run all the linters
41 | run-all-linter: get-linter get-fmt run-linter run-fmt tidy
42 |
43 | #Get the test coverage
44 | test: get-richgo
45 | go test -cover ./...
46 | go test -coverprofile=./cover/coverage.out ./...
47 | cat cover/coverage.out
48 | go tool cover -html=./cover/coverage.out
49 |
50 | build:
51 | go build -o bin/goarcc -buildmode pie ./cmd/api
52 |
--------------------------------------------------------------------------------
/servers/healthcheck/db/db.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "github.com/dimiro1/health"
5 | "gorm.io/gorm"
6 | )
7 |
8 | //Checker is a checker that check a given db
9 | type Checker struct {
10 | DB *gorm.DB
11 | }
12 |
13 | // NewPostgresChecker NewChecker returns a new url.Checker with the given URL
14 | func NewPostgresChecker(db *gorm.DB) Checker {
15 | return Checker{
16 | DB: db,
17 | }
18 | }
19 |
20 | // Check execute queries in the database
21 | // The first is a simple one used to verify if the database is up
22 | // If is Up then another query is executed, querying for the database version
23 | func (c Checker) Check() health.Health {
24 | var (
25 | version string
26 | )
27 |
28 | h := health.NewHealth()
29 |
30 | if c.DB == nil {
31 | h.Down().AddInfo("error", "Empty resource")
32 | return h
33 | }
34 |
35 | tx := c.DB.Raw(`Select version()`).Scan(&version)
36 | if tx.Error != nil || version == "" {
37 | h.Down().AddInfo("error", tx.Error.Error())
38 | return h
39 | }
40 |
41 | h.Up().AddInfo("version", version)
42 |
43 | return h
44 | }
45 |
--------------------------------------------------------------------------------
/servers/graphql/middleware/cors.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import "net/http"
4 |
5 | //setupCorsResponse : is the helper method which is responsible to set the header in the response on every request.
6 | // TODO: Instead of allowing "8" hardcoade, move this to config
7 | func setupCorsResponse(w *http.ResponseWriter, req *http.Request) {
8 | (*w).Header().Set("Access-Control-Allow-Origin", "*")
9 | (*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH")
10 | (*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
11 | }
12 |
13 | // AddCors : AddCors is the middleware which will add the cors in the response and response
14 | // with status ok on preflight request.
15 | func AddCors(h http.Handler) http.Handler {
16 | return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
17 | setupCorsResponse(&writer, request)
18 | if request.Method == "OPTIONS" {
19 | writer.WriteHeader(http.StatusOK)
20 | }
21 | h.ServeHTTP(writer, request)
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/servers/grpc/register.go:
--------------------------------------------------------------------------------
1 | package grpc
2 |
3 | import (
4 | "github.com/deqode/GoArcc/config"
5 | authExt "github.com/deqode/GoArcc/modules/authentication/v1/external-svc"
6 | authPb "github.com/deqode/GoArcc/modules/authentication/v1/pb"
7 | userExt "github.com/deqode/GoArcc/modules/user-profile/v1/external-svc"
8 | userProfilePb "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
9 | "google.golang.org/grpc"
10 | "gorm.io/gorm"
11 | )
12 |
13 | // RegisterGrpcModules Todo : Whenever any new modules will be in goarcc : it must be registered in below method
14 | /*
15 | RegisterGrpcModules: will register the modules/services to the server.
16 | */
17 | func RegisterGrpcModules(srv *grpc.Server,
18 | db *gorm.DB,
19 | config *config.Config,
20 | grpcClientConnection *grpc.ClientConn) {
21 |
22 | //todo register new grpc modules here
23 | //register user modules
24 | userProfilePb.RegisterUserProfilesServer(srv, userExt.NewUserProfilesServer(db, config, grpcClientConnection))
25 | authPb.RegisterAuthenticationsServer(srv, authExt.NewAuthenticationServer(db, config, grpcClientConnection))
26 | }
27 |
--------------------------------------------------------------------------------
/servers/rest/register.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/logger"
6 | authPb "github.com/deqode/GoArcc/modules/authentication/v1/pb"
7 | userProfilePb "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
8 | "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
9 | "go.uber.org/zap"
10 | "google.golang.org/grpc"
11 | )
12 |
13 | // RegisterRESTModules Todo : Whenever any new modules will be in goarcc : it must be registered in below method
14 | //Todo: Remove local host from here
15 | func RegisterRESTModules(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
16 | //opts := []grpc.DialOption{grpc.WithInsecure()}
17 | if err := userProfilePb.RegisterUserProfilesHandler(ctx, mux, conn); err != nil {
18 | logger.Log.Fatal("failed to start HTTP gateway", zap.String("reason", err.Error()))
19 | return err
20 | }
21 |
22 | if err := authPb.RegisterAuthenticationsHandler(ctx, mux, conn); err != nil {
23 | logger.Log.Fatal("failed to start HTTP gateway", zap.String("reason", err.Error()))
24 | return err
25 | }
26 | return nil
27 | }
28 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/models/user_profile.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "github.com/deqode/GoArcc/logger"
5 | "github.com/deqode/GoArcc/protos/types"
6 | "go.uber.org/zap"
7 | "gorm.io/gorm"
8 | "time"
9 | )
10 |
11 | type UserProfile struct {
12 | ID string `gorm:"primarykey"`
13 | Name string
14 | UserName string
15 | Email string
16 | PhoneNumber string
17 | Sub string `gorm:"uniqueIndex"`
18 | ProfilePicURL string
19 | Source types.VCSProviders
20 | CreatedAt time.Time
21 | UpdatedAt time.Time
22 | DeletedAt gorm.DeletedAt `gorm:"index"`
23 | }
24 |
25 | func InitialMigrationUserProfile(db *gorm.DB) {
26 | if err := db.AutoMigrate(&UserProfile{}); err != nil {
27 | logger.Log.Debug("unable to migrate user profile service", zap.Error(err))
28 | }
29 | //Check if table exist or not
30 | if !db.Migrator().HasTable(&UserProfile{}) {
31 | // if table not exist then create table
32 | if err := db.Migrator().CreateTable(&UserProfile{}); err != nil {
33 | logger.Log.Debug("unable to create UserProfile table")
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Deqode
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/config.yml:
--------------------------------------------------------------------------------
1 | grpc:
2 | port: 8000
3 | host: localhost
4 | request_timeout: 20
5 |
6 | graphql:
7 | port: 8081
8 | host: localhost
9 | request_Timeout: 20
10 |
11 | rest:
12 | port: 8082
13 | host: localhost
14 | request_timeout: 20
15 |
16 | health_check:
17 | port: 8083
18 | host: 0.0.0.0
19 |
20 | cadence_config:
21 | domain: simple-domain
22 | port: 7933
23 | host: localhost
24 | service: cadence-frontend
25 |
26 | promthesius:
27 | port: 8084
28 | host: localhost
29 |
30 | logger:
31 | log_level: -1
32 |
33 | postgres:
34 | host: localhost
35 | port: 5432
36 | user: postgres
37 | password: root
38 | db_name: alfred
39 | ssl_mode: true
40 | pg_driver: pgx
41 |
42 | metrics:
43 | url: 0.0.0.0:7070
44 | service_name: AlfredMetrics
45 |
46 | jaeger:
47 | host: localhost
48 | port: 6831
49 | service_name: AlfredTracing
50 | log_spans: false
51 |
52 | auth:
53 | auth0_client_id: BFnfdaibKSdqkSAOksr3XuUNJuCW9zbZ
54 | auth0_domain: alfred-sh.us.auth0.com
55 | auth0_client_secret: e6JJn1cKSEHk8p1FuoBdDbhmWWINevDitFBsWMTkZi8BbL8AVTQlLfgkwSCyyqJX
56 | auth0_call_back_url: http://localhost:3000/authentication/callback
57 |
--------------------------------------------------------------------------------
/db/connection.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "fmt"
5 | "github.com/deqode/GoArcc/config"
6 | "github.com/deqode/GoArcc/logger"
7 | "go.uber.org/zap"
8 | "gorm.io/driver/postgres"
9 | "gorm.io/gorm"
10 | )
11 |
12 | // NewConnection NewConnection: will open the connection with the database information
13 | // that is passed as an argument
14 | func NewConnection(config *config.Config) *gorm.DB {
15 | psqlInfo := fmt.Sprintf("host=%s port=%s user=%s "+
16 | "password=%s dbname=%s sslmode=disable Timezone=Asia/Shanghai",
17 | config.Postgres.Host, config.Postgres.Port, config.Postgres.User, config.Postgres.Password, config.Postgres.DbName)
18 |
19 | // https://github.com/go-gorm/postgres
20 | db, err := gorm.Open(postgres.New(postgres.Config{
21 | DSN: psqlInfo,
22 | PreferSimpleProtocol: true, // disables implicit prepared statement usage
23 | }), &gorm.Config{})
24 |
25 | if err != nil {
26 | logger.Log.Fatal("GORM connection failed", zap.Error(err))
27 | panic(err)
28 | }
29 |
30 | logger.Log.Info("connection established with the database")
31 | //No need to close the connection because we have a single pool of connection
32 | return db
33 | }
34 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/external-svc/get_user_profile_by_sub.go:
--------------------------------------------------------------------------------
1 | package external_svc
2 |
3 | import (
4 | "context"
5 | "errors"
6 | model "github.com/deqode/GoArcc/modules/user-profile/v1/models"
7 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
8 | "gorm.io/gorm"
9 | )
10 |
11 | func (s *userProfilesServer) GetUserProfileBySub(ctx context.Context, in *pb.GetUserProfileBySubRequest) (*pb.UserProfile, error) {
12 | if err := in.Validate(); err != nil {
13 | return nil, err
14 | }
15 | //user profile model
16 | usrProfile := model.UserProfile{}
17 | gormDb := s.db
18 | //ie: Select * from account where id = in.id
19 | if err := gormDb.First(&usrProfile, "sub= ?", in.Sub).Error; err != nil {
20 | if errors.Is(err, gorm.ErrRecordNotFound) {
21 | return nil, err
22 | }
23 | return nil, err
24 | }
25 | return &pb.UserProfile{
26 | Id: usrProfile.ID,
27 | Sub: usrProfile.Sub,
28 | Name: usrProfile.Name,
29 | UserName: usrProfile.UserName,
30 | Email: usrProfile.Email,
31 | PhoneNumber: usrProfile.PhoneNumber,
32 | ExternalSource: usrProfile.Source,
33 | ProfilePicUrl: usrProfile.ProfilePicURL,
34 | }, nil
35 | }
36 |
--------------------------------------------------------------------------------
/protos/google/api/annotation.proto:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package google.api;
18 |
19 | import "google/api/http.proto";
20 | import "google/protobuf/descriptor.proto";
21 |
22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
23 | option java_multiple_files = true;
24 | option java_outer_classname = "AnnotationsProto";
25 | option java_package = "com.google.api";
26 | option objc_class_prefix = "GAPI";
27 |
28 | extend google.protobuf.MethodOptions {
29 | // See `HttpRule`.
30 | HttpRule http = 72295728;
31 | }
32 |
--------------------------------------------------------------------------------
/cmd/api/invoker.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/deqode/GoArcc/servers/cleanup"
5 | "github.com/deqode/GoArcc/servers/graphql"
6 | "github.com/deqode/GoArcc/servers/grpc"
7 | "github.com/deqode/GoArcc/servers/healthcheck"
8 | "github.com/deqode/GoArcc/servers/rest"
9 | "go.uber.org/fx"
10 | )
11 |
12 | /*
13 | Todo Here you can new invokers.
14 | Todo Alarm !!!!!! Do not touch the invocation sequence, either you might go through sleepless nights
15 | */
16 |
17 | // GetInvokersOptions GetInvokersOptions: Please do not change the sequence because it invoker is lifecycle based method .
18 | // So changing the sequence will be harmful.
19 | func GetInvokersOptions() fx.Option {
20 | return fx.Invoke(
21 | //run server will run Rest , Graphql , prometheus server
22 | //RunServer,
23 | //all service got registered
24 | grpc.RunGRPCServer,
25 | grpc.RegisterGrpcModules,
26 | rest.RunRestServer,
27 | graphql.RunGraphqlServer,
28 | //After Registering Grpc Modules then only we can use prometheus
29 | //prometheusServer.PrometheusRunner,
30 | //run cleanup code after closing the server
31 | //Add Health check
32 | healthcheck.HealthCheckRunner,
33 | cleanup.Cleanup,
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/external-svc/get_user_profile.go:
--------------------------------------------------------------------------------
1 | package external_svc
2 |
3 | import (
4 | "context"
5 | "errors"
6 | model "github.com/deqode/GoArcc/modules/user-profile/v1/models"
7 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
8 | "github.com/deqode/GoArcc/util/userinfo"
9 | "github.com/golang/protobuf/ptypes/empty"
10 | "gorm.io/gorm"
11 | )
12 |
13 | func (s *userProfilesServer) GetUserProfile(ctx context.Context, empty *empty.Empty) (*pb.UserProfile, error) {
14 | //user profile model
15 | var usrProfile model.UserProfile
16 | usrId := userinfo.FromContext(ctx).ID
17 | gormDb := s.db
18 | //ie: Select * from account where id = in.id
19 | if err := gormDb.First(&usrProfile, "id= ?", usrId).Error; err != nil {
20 | if errors.Is(err, gorm.ErrRecordNotFound) {
21 | return nil, err
22 | }
23 | return nil, err
24 | }
25 | return &pb.UserProfile{
26 | Id: usrProfile.ID,
27 | Sub: usrProfile.Sub,
28 | Name: usrProfile.Name,
29 | UserName: usrProfile.UserName,
30 | Email: usrProfile.Email,
31 | PhoneNumber: usrProfile.PhoneNumber,
32 | ExternalSource: usrProfile.Source,
33 | ProfilePicUrl: usrProfile.ProfilePicURL,
34 | }, nil
35 | }
36 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/pb/user_profile_int.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package goarcc.user_profile_internal.v1;
3 |
4 | option go_package = "/pb";
5 |
6 | import "google/protobuf/empty.proto";
7 | import "user_profile.proto";
8 | import "google/api/annotation.proto";
9 | import "validate/validate.proto";
10 |
11 |
12 | //Private Service - Requires Authentication
13 | service UserProfileInternal {
14 | //CreateUserProfile creates a user profile by external oAuth
15 | rpc CreateUserProfile(CreateUserProfileRequest) returns (goarcc.user_profile.v1.UserProfile);
16 |
17 | // CreateUserProfile will update userprofile
18 | rpc UpdateUserProfile(UpdateUserProfileRequest) returns (goarcc.user_profile.v1.UserProfile);
19 |
20 | // DeleteUserProfile delete the user
21 | rpc DeleteUserProfile(DeleteUserProfileRequest) returns (google.protobuf.Empty);
22 | }
23 |
24 |
25 | message CreateUserProfileRequest {
26 | goarcc.user_profile.v1.UserProfile user_profile = 1;
27 | }
28 |
29 | message DeleteUserProfileRequest {
30 | string id = 1 [(validate.rules).string.min_len = 3];
31 | }
32 |
33 | message UpdateUserProfileRequest {
34 | goarcc.user_profile.v1.UserProfile user_profile = 1 [(validate.rules).message.required = true];
35 | }
36 |
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: Backend GO CI
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 |
7 | jobs:
8 | build:
9 | name: Test Code
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Install Go
14 | uses: actions/setup-go@v2
15 | with:
16 | go-version: 1.16.x
17 | - uses: actions/checkout@v2
18 |
19 | - uses: actions/cache@v2
20 | with:
21 | path: /tmp/docker_cache/go_mod
22 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}-
23 | restore-keys: |
24 | ${{ runner.os }}-go-
25 | # # In this step, this action saves a list of existing images,
26 | # # the cache is created without them in the post run.
27 | # # It also restores the cache if it exists.
28 | # - uses: satackey/action-docker-layer-caching@v0.0.11
29 | # # Ignore the failure of a step and avoid terminating the job.
30 | # continue-on-error: true
31 |
32 | - name: go mod download
33 | run: docker-compose run backend_api go mod download
34 |
35 | - name: Lint
36 | run: docker-compose run backend_api make run-linter
37 |
38 | - name: Test In Docker
39 | run: docker-compose run backend_api make test
40 |
41 |
42 | - name: build
43 | run: docker-compose run backend_api make build
44 |
45 | - run: docker-compose down
46 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/external-svc/get_user_profile_test.go:
--------------------------------------------------------------------------------
1 | package external_svc_test
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
6 | "github.com/deqode/GoArcc/util/userinfo"
7 | . "github.com/onsi/ginkgo"
8 | . "github.com/onsi/gomega"
9 | "gorm.io/gorm"
10 | _ "log"
11 | "time"
12 | )
13 |
14 | var _ = Describe("GetUserProfile", func() {
15 | var (
16 | userProfileServer pb.UserProfilesServer
17 | ctx context.Context
18 | )
19 |
20 | // this block will run after each it block
21 | BeforeEach(func() {
22 | userProfileServer = UserProfileServerTest
23 | ctx = CtxTest
24 | })
25 |
26 | // negative
27 | By("internal or external call")
28 | Context("Get an error when id is wrong", func() {
29 | It("Return record not found error", func() {
30 | ui := userinfo.UserInfo{
31 | ID: "Id",
32 | Email: "Email",
33 | Sub: "Sub",
34 | TokenExpiry: time.Time{},
35 | }
36 | newCtx := userinfo.NewContext(context.Background(), ui)
37 | _, err := userProfileServer.GetUserProfile(newCtx, nil)
38 | Expect(err).Should(Equal(gorm.ErrRecordNotFound))
39 | })
40 | })
41 | // positive
42 | Context("Get a record when id is provided", func() {
43 | It("should return requested field in the object", func() {
44 | _, err := userProfileServer.GetUserProfile(ctx, nil)
45 | Expect(err).Should(BeNil())
46 | })
47 | })
48 | })
49 |
--------------------------------------------------------------------------------
/client/grpcClient/client.go:
--------------------------------------------------------------------------------
1 | package grpcClient
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/config"
6 | "github.com/deqode/GoArcc/logger"
7 | "github.com/deqode/GoArcc/servers/openTracing/tracer/jaeger"
8 | grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
9 | "go.uber.org/zap"
10 | "google.golang.org/grpc"
11 | "time"
12 | )
13 |
14 | //ClientContext to store context
15 | type ClientContext struct {
16 | Ctx context.Context
17 | }
18 |
19 | //GetGrpcClientConnection is used to get the client connection
20 | func GetGrpcClientConnection(config *config.Config) *grpc.ClientConn {
21 | var opts []grpc.DialOption
22 | opts = append(opts,
23 | grpc.WithUnaryInterceptor(
24 | //tracing for unary interceptors
25 | grpc_opentracing.UnaryClientInterceptor(grpc_opentracing.WithTracer(jaeger.Tracer)),
26 | ),
27 | //tracing for stream interceptors
28 | grpc.WithStreamInterceptor(grpc_opentracing.StreamClientInterceptor(grpc_opentracing.WithTracer(jaeger.Tracer))),
29 | )
30 |
31 | //append grpc insecure
32 | opts = append(opts,
33 | grpc.WithInsecure(),
34 | )
35 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(config.Grpc.RequestTimeout))
36 | defer cancel()
37 |
38 | // You must have some sort of OpenTracing Tracer instance on hand.
39 | conn, err := grpc.DialContext(ctx, config.Grpc.Host+":"+config.Grpc.Port, opts...)
40 | if err != nil {
41 | logger.Log.Fatal("did not connect", zap.Error(err))
42 | }
43 | return conn
44 | }
45 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/internal-svc/create_user_profile.go:
--------------------------------------------------------------------------------
1 | package internal_svc
2 |
3 | import (
4 | "context"
5 | model "github.com/deqode/GoArcc/modules/user-profile/v1/models"
6 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
7 | "google.golang.org/grpc/codes"
8 | "google.golang.org/grpc/status"
9 | "gorm.io/gorm"
10 | "time"
11 | )
12 |
13 | func (s *userProfileInServer) CreateUserProfile(ctx context.Context, in *pb.CreateUserProfileRequest) (*pb.UserProfile, error) {
14 | if in.UserProfile == nil || in == nil {
15 | return nil, status.Error(codes.FailedPrecondition, "UserProfile not provided")
16 | }
17 | //request validation
18 | if err := in.Validate(); err != nil {
19 | return nil, err
20 | }
21 |
22 | //prepare insert object
23 | UserProfileModel := &model.UserProfile{
24 | ID: in.GetUserProfile().GetId(),
25 | Name: in.GetUserProfile().GetName(),
26 | UserName: in.GetUserProfile().GetUserName(),
27 | Email: in.GetUserProfile().GetEmail(),
28 | PhoneNumber: in.GetUserProfile().GetPhoneNumber(),
29 | Sub: in.GetUserProfile().GetId(),
30 | ProfilePicURL: in.GetUserProfile().GetProfilePicUrl(),
31 | Source: in.GetUserProfile().GetExternalSource(),
32 | CreatedAt: time.Time{},
33 | UpdatedAt: time.Time{},
34 | DeletedAt: gorm.DeletedAt{},
35 | }
36 | //insert into db
37 | gormDb := s.db
38 | tx := gormDb.Create(UserProfileModel)
39 | if tx.Error != nil {
40 | return nil, tx.Error
41 | }
42 | return in.UserProfile, nil
43 | }
44 |
--------------------------------------------------------------------------------
/servers/cleanup/cleanup.go:
--------------------------------------------------------------------------------
1 | package cleanup
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/logger"
6 | "go.uber.org/fx"
7 | "google.golang.org/grpc"
8 | "io"
9 | )
10 |
11 | // Config CleanupConfig : cleanup config is the configuration of closing the instance.
12 | // more type will be added in cleanup config if we stop or destroy the instance.
13 | type Config struct {
14 | //Db *gorm.DB
15 | GrpcServerConnection *grpc.Server
16 | GrpcClientConnection *grpc.ClientConn
17 | JaegerCloser io.Closer
18 | }
19 |
20 | // GetCleanupConfig GetCleanupConfig: Get cleanup config is the constructor.
21 | // required all the closing instance
22 | func GetCleanupConfig(
23 | GrpcServerConnection *grpc.Server,
24 | GrpcClientConnection *grpc.ClientConn,
25 | JaegerCloser io.Closer) *Config {
26 | return &Config{
27 | GrpcServerConnection: GrpcServerConnection,
28 | JaegerCloser: JaegerCloser,
29 | GrpcClientConnection: GrpcClientConnection,
30 | }
31 | }
32 |
33 | // Cleanup /*
34 | func Cleanup(lc fx.Lifecycle, config *Config) {
35 | lc.Append(fx.Hook{
36 | OnStop: func(ctx context.Context) error {
37 | logger.Log.Info(".......Starting Cleanup code ......")
38 |
39 | //Closing client connection
40 | config.GrpcClientConnection.Close()
41 | logger.Log.Info("successfully closed grpc client connection")
42 | //Closing grpc server connection
43 | config.GrpcServerConnection.GracefulStop()
44 | logger.Log.Info("successfully closed grpc server connection")
45 | //todo more cleanup code will be added
46 | defer config.JaegerCloser.Close()
47 | logger.Log.Info("cleanup code successfully executed")
48 | return nil
49 | },
50 | })
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/modules/authentication/v1/external-svc/login_callback_test.go:
--------------------------------------------------------------------------------
1 | package external_svc_test
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/modules/authentication/v1/pb"
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | "google.golang.org/grpc/codes"
9 | "google.golang.org/grpc/status"
10 | )
11 |
12 | var _ = Describe("Login Callback", func() {
13 | var (
14 | authServer pb.AuthenticationsServer
15 | //authenticator *external_svc.Authenticator
16 | )
17 | BeforeEach(func() {
18 | authServer = AuthServerTest
19 | //authenticator = AuthenticatorTest
20 | })
21 |
22 | Describe("Login Callback Test", func() {
23 | By("By Rpc Calls")
24 | Context("Get an error when request object is nil", func() {
25 | It("Failed precondition err must be responded", func() {
26 | _, err := authServer.LoginCallback(context.Background(), nil)
27 | Expect(err).Should(Equal(status.Error(codes.FailedPrecondition, "Request is Nil")))
28 | })
29 | })
30 | Context("Get an error if state or code is empty", func() {
31 | It("Failed precondition err must be responded", func() {
32 | _, err := authServer.LoginCallback(context.Background(), &pb.LoginCallbackRequest{Code: "", State: ""})
33 | Expect(err).ShouldNot(BeNil())
34 | })
35 | })
36 |
37 | Context("Get an error if user profile is not present", func() {
38 | It("gorm error must be thrown", func() {
39 |
40 | })
41 | })
42 |
43 | Context("Get CallBack Response when request object is valid", func() {
44 | It("Throw error if response object is nil", func() {
45 | })
46 | It("Throw error if user id is empty", func() {
47 | })
48 | It("Throw error if Access token is empty", func() {
49 | })
50 | It("Throw error if raw id is empty", func() {
51 | })
52 | })
53 | })
54 | })
55 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/external-svc/get_user_profile_by_sub_test.go:
--------------------------------------------------------------------------------
1 | package external_svc_test
2 |
3 | import (
4 | "context"
5 | pb "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | "gorm.io/gorm"
9 | )
10 |
11 | var _ = Describe("GetUserProfile By unique External Sub", func() {
12 | var (
13 | userProfileServer pb.UserProfilesServer
14 | ctx context.Context
15 | usrProfile *pb.UserProfile
16 | )
17 |
18 | // this block will run after each it block
19 | BeforeEach(func() {
20 | userProfileServer = UserProfileServerTest
21 | ctx = CtxTest
22 | usrProfile = UsrProfile
23 | })
24 |
25 | Describe("Get a user-profile", func() {
26 | By("internal or external call")
27 | Context("Get an error when sub is empty", func() {
28 | It("it should return validation error", func() {
29 | _, err := userProfileServer.GetUserProfileBySub(ctx, &pb.GetUserProfileBySubRequest{Sub: ""})
30 | Expect(err.(pb.GetUserProfileBySubRequestValidationError).Reason()).Should(Equal("value length must be at least 3 runes"))
31 | })
32 | })
33 | Context("Get an error when sub_id is wrong", func() {
34 | It("should return not found error", func() {
35 | _, err := userProfileServer.GetUserProfileBySub(ctx, &pb.GetUserProfileBySubRequest{Sub: "fji5895"})
36 | Expect(err).Should(Equal(gorm.ErrRecordNotFound))
37 | })
38 | })
39 |
40 | Context("Get a record when sub_id is provided", func() {
41 | It("should return requested field in the object", func() {
42 | usr, err := userProfileServer.GetUserProfileBySub(ctx, &pb.GetUserProfileBySubRequest{Sub: usrProfile.Sub})
43 | Expect(err).Should(BeNil())
44 | Expect(usr.Sub).Should(Equal(usrProfile.Sub))
45 | })
46 | })
47 | })
48 | })
49 |
--------------------------------------------------------------------------------
/modules/authentication/v1/external-svc/external_suite_test.go:
--------------------------------------------------------------------------------
1 | package external_svc_test
2 |
3 | import (
4 | "github.com/deqode/GoArcc/client/grpcClient"
5 | "github.com/deqode/GoArcc/config"
6 | "github.com/deqode/GoArcc/db"
7 | "github.com/deqode/GoArcc/logger"
8 | "github.com/deqode/GoArcc/modules/authentication/v1/external-svc"
9 | "github.com/deqode/GoArcc/modules/authentication/v1/pb"
10 | "go.uber.org/zap"
11 | "google.golang.org/grpc"
12 | "gorm.io/gorm"
13 | "log"
14 | "testing"
15 |
16 | . "github.com/onsi/ginkgo"
17 | . "github.com/onsi/gomega"
18 | )
19 |
20 | var (
21 | AuthServerTest pb.AuthenticationsServer
22 | )
23 |
24 | func TestAuthenticationExt(t *testing.T) {
25 | //now init logger
26 | logger.Init(logger.Config{
27 | LogLevel: zap.DebugLevel,
28 | Development: false,
29 | })
30 | RegisterFailHandler(Fail)
31 | RunSpecs(t, "TestAuthenticationExt Service Suite")
32 | }
33 |
34 | // Before Suite Run only once
35 | var _ = BeforeSuite(func() {
36 | //getting config
37 | cfgFile, err := config.LoadConfig("config", config.GetConfigDirectory())
38 | if err != nil {
39 | log.Fatal(err)
40 | }
41 | cfg, err := config.ParseConfig(cfgFile)
42 | if err != nil {
43 | log.Fatal(err)
44 | }
45 | fields := struct {
46 | db *gorm.DB
47 | config *config.Config
48 | grpcClient *grpc.ClientConn
49 | }{
50 | db: db.NewConnection(cfg),
51 | config: cfg,
52 | grpcClient: grpcClient.GetGrpcClientConnection(cfg),
53 | }
54 |
55 | authServer := external_svc.NewAuthenticationServer(fields.db, fields.config, fields.grpcClient)
56 | _, err = external_svc.NewAuthenticator(fields.config)
57 | if err != nil {
58 | log.Fatal(err)
59 | }
60 |
61 | AuthServerTest = authServer
62 | })
63 |
64 | // nil all global variables
65 | var _ = AfterSuite(func() {
66 | AuthServerTest = nil
67 | })
68 |
--------------------------------------------------------------------------------
/servers/graphql/middleware/logger.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "go.uber.org/zap"
5 | "net/http"
6 | "strings"
7 | "time"
8 | )
9 |
10 | // AddLogger logs request/response pair
11 | func AddLogger(logger *zap.Logger, h http.Handler) http.Handler {
12 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
13 | ctx := r.Context()
14 | // We do not want to be spammed by Kubernetes health check.
15 | // Do not log Kubernetes health check.
16 | // You can change this behavior as you wish.
17 | if r.Header.Get("X-Liveness-Probe") == "Healthz" {
18 | h.ServeHTTP(w, r)
19 | return
20 | }
21 |
22 | id := GetReqID(ctx)
23 |
24 | // Prepare fields to log
25 | var scheme string
26 | if r.TLS != nil {
27 | scheme = "https"
28 | } else {
29 | scheme = "http"
30 | }
31 | proto := r.Proto
32 | method := r.Method
33 | remoteAddr := r.RemoteAddr
34 | userAgent := r.UserAgent()
35 | uri := strings.Join([]string{scheme, "://", r.Host, r.RequestURI}, "")
36 |
37 | // Log HTTP request
38 | logger.Debug("request started",
39 | zap.String("request-id", id),
40 | zap.String("http-scheme", scheme),
41 | zap.String("http-proto", proto),
42 | zap.String("http-method", method),
43 | zap.String("remote-addr", remoteAddr),
44 | zap.String("user-agent", userAgent),
45 | zap.String("uri", uri),
46 | )
47 |
48 | t1 := time.Now()
49 |
50 | h.ServeHTTP(w, r)
51 |
52 | // Log HTTP response
53 | logger.Debug("request completed",
54 | zap.String("request-id", id),
55 | zap.String("http-scheme", scheme),
56 | zap.String("http-proto", proto),
57 | zap.String("http-method", method),
58 | zap.String("remote-addr", remoteAddr),
59 | zap.String("user-agent", userAgent),
60 | zap.String("uri", uri),
61 | zap.Float64("elapsed-ms", float64(time.Since(t1).Nanoseconds())/1000000.0),
62 | )
63 | })
64 | }
65 |
--------------------------------------------------------------------------------
/servers/openTracing/tracer/jaeger/jaeger.go:
--------------------------------------------------------------------------------
1 | package jaeger
2 |
3 | import (
4 | "github.com/deqode/GoArcc/config"
5 | "github.com/deqode/GoArcc/logger"
6 | "github.com/opentracing/opentracing-go"
7 | "github.com/uber/jaeger-client-go"
8 | jaegerconfig "github.com/uber/jaeger-client-go/config"
9 | jaegerlog "github.com/uber/jaeger-client-go/log"
10 | "github.com/uber/jaeger-lib/metrics"
11 | "go.uber.org/zap"
12 | "io"
13 | "strconv"
14 | )
15 |
16 | // Jaeger struct for conversion
17 | type Jaeger struct {
18 | Host string
19 | ServiceName string
20 | LogSpans bool
21 | }
22 |
23 | var (
24 | Tracer opentracing.Tracer
25 | )
26 |
27 | // InitJaeger todo close the connection
28 | // InitJaeger is responsible for initialising the jaeger tracing instance.
29 | func InitJaeger(config *config.Config) (io.Closer, opentracing.Tracer) {
30 | //conversion string val to bool
31 | logSpan, _ := strconv.ParseBool(config.Jaeger.LogSpans)
32 | //Jaeger configuration setup
33 | jaegerCfgInstance := jaegerconfig.Configuration{
34 | //Service name is the name of project for which tracing will be present
35 | ServiceName: config.Jaeger.ServiceName,
36 | Sampler: &jaegerconfig.SamplerConfig{
37 | // SamplerTypeConst is the type of sampler that always makes the same decision.
38 | Type: jaeger.SamplerTypeConst,
39 | Param: 1,
40 | },
41 | //Reporter configuration
42 | Reporter: &jaegerconfig.ReporterConfig{
43 | LogSpans: logSpan,
44 | //Local Agent Host port
45 | LocalAgentHostPort: config.Jaeger.Host + ":" + config.Jaeger.Port,
46 | },
47 | }
48 | //New Tracer instance
49 | tracer, closer, err := jaegerCfgInstance.NewTracer(
50 | jaegerconfig.Logger(jaegerlog.StdLogger),
51 | jaegerconfig.Metrics(metrics.NullFactory),
52 | )
53 | if err != nil {
54 | logger.Log.Fatal("cannot create tracer", zap.Error(err))
55 | panic(err)
56 | }
57 |
58 | logger.Log.Info("Jaeger tracer initiated")
59 | //global tracer setup , opentracing
60 | opentracing.SetGlobalTracer(tracer)
61 | return closer, tracer
62 | }
63 |
--------------------------------------------------------------------------------
/servers/grpc/server.go:
--------------------------------------------------------------------------------
1 | package grpc
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/config"
6 | "github.com/deqode/GoArcc/logger"
7 | "github.com/deqode/GoArcc/servers/grpc/middleware"
8 | grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
9 | "github.com/opentracing/opentracing-go"
10 | "github.com/prometheus/client_golang/prometheus/promhttp"
11 | "go.uber.org/fx"
12 | "go.uber.org/zap"
13 | "google.golang.org/grpc"
14 | "google.golang.org/grpc/reflection"
15 | "net"
16 | "net/http"
17 | )
18 |
19 | // InitGrpcBeforeServing InitGrpcBeforeServing: give the instance of server and listener.
20 | func InitGrpcBeforeServing(config *config.Config, tracer opentracing.Tracer) (*grpc.Server, net.Listener) {
21 | listen, err := net.Listen("tcp", config.Grpc.Host+":"+config.Grpc.Port)
22 | if err != nil {
23 | logger.Log.Fatal("not able to initialize grpc server", zap.Error(err))
24 | panic(err)
25 | }
26 | // gRPC server setup options
27 | var opts []grpc.ServerOption
28 | // add logging middleware
29 | opts = middleware.AddInterceptors(logger.Log, tracer, opts)
30 | // register service
31 | server := grpc.NewServer(opts...)
32 | return server, listen
33 | }
34 |
35 | // RunGRPCServer runs gRPC service on the given port.
36 | func RunGRPCServer(lc fx.Lifecycle, server *grpc.Server, listener net.Listener) error {
37 | lc.Append(
38 | fx.Hook{
39 | OnStart: func(ctx context.Context) error {
40 | reflection.Register(server)
41 | grpc_prometheus.EnableHandlingTimeHistogram()
42 | grpc_prometheus.Register(server)
43 | http.Handle("/metrics", promhttp.Handler())
44 |
45 | logger.Log.Info("Prom metrics endpoint registered on /metrics")
46 |
47 | logger.Log.Info("Starting HTTP2/gRPC server...")
48 | // start gRPC server
49 | go server.Serve(listener)
50 | return nil
51 | },
52 | OnStop: func(ctx context.Context) error {
53 | // start gRPC server
54 | logger.Log.Info("Stopping gRPC server...")
55 | server.GracefulStop()
56 | return nil
57 | },
58 | },
59 | )
60 |
61 | return nil
62 | }
63 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/internal-svc/delete_user_profile_test.go:
--------------------------------------------------------------------------------
1 | package internal_svc_test
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | "google.golang.org/grpc/codes"
9 | "google.golang.org/grpc/status"
10 | )
11 |
12 | var _ = Describe("DeleteUserProfile", func() {
13 | var (
14 | userProfileServer pb.UserProfileInternalServer
15 | ctx context.Context
16 | profile *pb.UserProfile
17 | )
18 | BeforeEach(func() {
19 | profile = UsrProfile
20 | userProfileServer = UserProfileServerIntTest
21 | ctx = CtxTest
22 | })
23 |
24 | Describe("Deleting a user profile", func() {
25 | //Negative Cases
26 | By("By an internal RPC Call")
27 | Context("Get an error when no user_id provided", func() {
28 | It("Should return validation error", func() {
29 | _, err := userProfileServer.DeleteUserProfile(ctx, &pb.DeleteUserProfileRequest{Id: ""})
30 | Expect(err.(pb.DeleteUserProfileRequestValidationError).Reason()).Should(Equal("value length must be at least 3 runes"))
31 | })
32 | })
33 |
34 | Context("Get an error when request is nil", func() {
35 | It("should return error ", func() {
36 | _, err := userProfileServer.DeleteUserProfile(ctx, nil)
37 | Expect(err).Should(Equal(status.Error(codes.FailedPrecondition, "Request can't be nil")))
38 | })
39 | })
40 |
41 | Context("Return nil when wrong user_id provided", func() {
42 | It("should not perform any action in DB and return nil", func() {
43 | _, err := userProfileServer.DeleteUserProfile(ctx, &pb.DeleteUserProfileRequest{Id: "fejfoeiw0r8290r420"})
44 | Expect(err).To(BeNil(), "Error")
45 | })
46 | })
47 |
48 | //Positive Test Cases
49 | Context("Return confirmation when record soft deleted in DB", func() {
50 | It("should return a boolean value", func() {
51 | _, err := userProfileServer.DeleteUserProfile(ctx, &pb.DeleteUserProfileRequest{Id: profile.Id})
52 | Expect(err).To(BeNil(), "Error")
53 | })
54 | })
55 | })
56 |
57 | })
58 |
--------------------------------------------------------------------------------
/servers/graphql/server.go:
--------------------------------------------------------------------------------
1 | package graphql
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/config"
6 | "github.com/deqode/GoArcc/logger"
7 | "github.com/deqode/GoArcc/servers/graphql/middleware"
8 | "net/http"
9 | "time"
10 |
11 | "github.com/ysugimoto/grpc-graphql-gateway/runtime"
12 | "go.uber.org/fx"
13 | "go.uber.org/zap"
14 | "google.golang.org/grpc"
15 | )
16 |
17 | // RunGraphqlServer : Will start the graphql server.
18 | func RunGraphqlServer(lc fx.Lifecycle, config *config.Config, conn *grpc.ClientConn) {
19 | Ctx, cancel := context.WithCancel(context.Background())
20 | defer cancel()
21 |
22 | mux := runtime.NewServeMux()
23 | if err := RegisterGraphqlModules(mux, conn); err != nil {
24 | logger.Log.Fatal("not able to register graphql modules", zap.Error(err))
25 | }
26 | http.Handle("/graphql", mux)
27 | srv := &http.Server{
28 | Addr: config.Graphql.Host + ":" + config.Graphql.Port,
29 | WriteTimeout: time.Second * time.Duration(config.Graphql.RequestTimeout),
30 | // add handler with middleware
31 | Handler: http.TimeoutHandler(middleware.ChangeContext(middleware.AddCors(middleware.AddRequestID(
32 | middleware.AddLogger(logger.Log, mux)))), time.Second*time.Duration(config.Graphql.RequestTimeout), "Context deadline exceeded"),
33 | }
34 |
35 | lc.Append(fx.Hook{
36 | // To mitigate the impact of deadlocks in application startup and
37 | // shutdown, Fx imposes a time limit on OnStart and OnStop hooks. By
38 | // default, hooks have a total of 15 seconds to complete. Timeouts are
39 | // passed via Go's usual context.Context.
40 | OnStart: func(ctx context.Context) error {
41 |
42 | // In production, we'd want to separate the Listen and Serve phases for
43 | // better error-handling.
44 | // run HTTP gateway
45 | logger.Log.Info("starting HTTP/GRAPHQL gateway...")
46 | go func() {
47 | _ = srv.ListenAndServe()
48 | }()
49 |
50 | return nil
51 | },
52 | OnStop: func(ctx context.Context) error {
53 | logger.Log.Info("graceful shutting down Graphql Server")
54 | _ = srv.Shutdown(Ctx)
55 | return nil
56 | },
57 | })
58 | }
59 |
--------------------------------------------------------------------------------
/servers/rest/server.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/config"
6 | "github.com/deqode/GoArcc/logger"
7 | "github.com/deqode/GoArcc/servers/rest/middleware"
8 | "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
9 | "go.uber.org/fx"
10 | "google.golang.org/grpc"
11 | "net/http"
12 | "time"
13 | )
14 |
15 | // RunRestServer : Will responsible to run the Rest server in different port.
16 | func RunRestServer(lc fx.Lifecycle, config *config.Config, conn *grpc.ClientConn) {
17 | Ctx, cancel := context.WithCancel(context.Background())
18 | defer cancel()
19 |
20 | mux := runtime.NewServeMux()
21 | if err := RegisterRESTModules(Ctx, mux, conn); err != nil {
22 | panic(err)
23 | }
24 | srv := &http.Server{
25 | Addr: config.Rest.Host + ":" + config.Rest.Port,
26 | // add handler with middleware
27 | Handler: http.TimeoutHandler(
28 | middleware.AddCors(middleware.AddRequestID(middleware.AddLogger(logger.Log, mux))),
29 | time.Second*time.Duration(config.Rest.RequestTimeout),
30 | "Context deadline exceeded",
31 | ),
32 | //Read Timeout is the time required to read the request body.
33 | WriteTimeout: time.Second * time.Duration(config.Rest.RequestTimeout),
34 | }
35 |
36 | logger.Log.Info("starting HTTP/REST gateway...")
37 |
38 | lc.Append(fx.Hook{
39 | // To mitigate the impact of deadlocks in application startup and
40 | // shutdown, Fx imposes a time limit on OnStart and OnStop hooks. By
41 | // default, hooks have a total of 15 seconds to complete. Timeouts are
42 | // passed via Go's usual context.Context.
43 | OnStart: func(ctx context.Context) error {
44 |
45 | // In production, we'd want to separate the Listen and Serve phases for
46 | // better error-handling.
47 | // run HTTP gateway
48 | logger.Log.Info("starting HTTP/REST gateway...")
49 | go func() {
50 | _ = srv.ListenAndServe()
51 | }()
52 |
53 | return nil
54 | },
55 |
56 | OnStop: func(ctx context.Context) error {
57 | logger.Log.Info("Gracefully Shutting down REST server")
58 | _ = srv.Shutdown(Ctx)
59 | return nil
60 | },
61 | })
62 | }
63 |
--------------------------------------------------------------------------------
/servers/grpc/grpcErrors/grpcErrors.go:
--------------------------------------------------------------------------------
1 | package grpcErrors
2 |
3 | import (
4 | "context"
5 | "database/sql"
6 | "net/http"
7 | "strings"
8 |
9 | "github.com/go-redis/redis/v8"
10 | "github.com/pkg/errors"
11 | "google.golang.org/grpc/codes"
12 | )
13 |
14 | var (
15 | ErrNotFound = errors.New("Not found")
16 | ErrNoCtxMetaData = errors.New("No ctxhelper metadata")
17 | ErrInvalidSessionID = errors.New("Invalid session id")
18 | ErrEmailExists = errors.New("Email already exists")
19 | )
20 |
21 | // ParseGRPCErrStatusCode Parse error and get code
22 | func ParseGRPCErrStatusCode(err error) codes.Code {
23 | switch {
24 | case errors.Is(err, sql.ErrNoRows):
25 | return codes.NotFound
26 | case errors.Is(err, redis.Nil):
27 | return codes.NotFound
28 | case errors.Is(err, context.Canceled):
29 | return codes.Canceled
30 | case errors.Is(err, context.DeadlineExceeded):
31 | return codes.DeadlineExceeded
32 | case errors.Is(err, ErrEmailExists):
33 | return codes.AlreadyExists
34 | case errors.Is(err, ErrNoCtxMetaData):
35 | return codes.Unauthenticated
36 | case errors.Is(err, ErrInvalidSessionID):
37 | return codes.PermissionDenied
38 | case strings.Contains(err.Error(), "Validate"):
39 | return codes.InvalidArgument
40 | case strings.Contains(err.Error(), "redis"):
41 | return codes.NotFound
42 | }
43 | return codes.Internal
44 | }
45 |
46 | // MapGRPCErrCodeToHTTPStatus MapGRPCErrCodeToHttpStatus Map GRPC errors codes to http status
47 | func MapGRPCErrCodeToHTTPStatus(code codes.Code) int {
48 | switch code {
49 | case codes.Unauthenticated:
50 | return http.StatusUnauthorized
51 | case codes.AlreadyExists:
52 | return http.StatusBadRequest
53 | case codes.NotFound:
54 | return http.StatusNotFound
55 | case codes.Internal:
56 | return http.StatusInternalServerError
57 | case codes.PermissionDenied:
58 | return http.StatusForbidden
59 | case codes.Canceled:
60 | return http.StatusRequestTimeout
61 | case codes.DeadlineExceeded:
62 | return http.StatusGatewayTimeout
63 | case codes.InvalidArgument:
64 | return http.StatusBadRequest
65 | }
66 | return http.StatusInternalServerError
67 | }
68 |
--------------------------------------------------------------------------------
/util/ctxhelper/ctxhelper.go:
--------------------------------------------------------------------------------
1 | package ctxhelper
2 |
3 | import (
4 | "context"
5 | "go.uber.org/zap"
6 | "time"
7 | )
8 |
9 | type ctxKey int
10 |
11 | const (
12 | ctxKeyComponent ctxKey = iota
13 | ctxKeyLogger
14 | ctxKeyReqID
15 | ctxKeyStartTime
16 | )
17 |
18 | // NewContextComponentName creates a new context that carries the provided
19 | // componentName value.
20 | func NewContextComponentName(ctx context.Context, componentName string) context.Context {
21 | return context.WithValue(ctx, ctxKeyComponent, componentName)
22 | }
23 |
24 | // ComponentNameFromContext extracts a component name from a context.
25 | func ComponentNameFromContext(ctx context.Context) (componentName string, ok bool) {
26 | componentName, ok = ctx.Value(ctxKeyComponent).(string)
27 | return
28 | }
29 |
30 | // NewContextLogger creates a new context that carries the provided logger
31 | // value.
32 | func NewContextLogger(ctx context.Context, logger *zap.Logger) context.Context {
33 | return context.WithValue(ctx, ctxKeyLogger, logger)
34 | }
35 |
36 | // LoggerFromContext extracts a logger from a context.
37 | func LoggerFromContext(ctx context.Context) (logger *zap.Logger, ok bool) {
38 | logger, ok = ctx.Value(ctxKeyLogger).(*zap.Logger)
39 | return
40 | }
41 |
42 | // NewContextRequestID creates a new context that carries the provided request
43 | // ID value.
44 | func NewContextRequestID(ctx context.Context, id string) context.Context {
45 | return context.WithValue(ctx, ctxKeyReqID, id)
46 | }
47 |
48 | // RequestIDFromContext extracts a request ID from a context.
49 | func RequestIDFromContext(ctx context.Context) (id string, ok bool) {
50 | id, ok = ctx.Value(ctxKeyReqID).(string)
51 | return
52 | }
53 |
54 | // NewContextStartTime creates a new context that carries the provided start
55 | // time.
56 | func NewContextStartTime(ctx context.Context, start time.Time) context.Context {
57 | return context.WithValue(ctx, ctxKeyStartTime, start)
58 | }
59 |
60 | // StartTimeFromContext extracts a start time from a context.
61 | func StartTimeFromContext(ctx context.Context) (start time.Time, ok bool) {
62 | start, ok = ctx.Value(ctxKeyStartTime).(time.Time)
63 | return
64 | }
65 |
--------------------------------------------------------------------------------
/servers/graphql/middleware/request-id.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "context"
5 | "crypto/rand"
6 | "encoding/base64"
7 | "fmt"
8 | "net/http"
9 | "os"
10 | "strings"
11 | "sync/atomic"
12 | )
13 |
14 | // Code is taken from: https://github.com/go-chi/chi/blob/master/middleware/request_id.go
15 |
16 | // ctxKeyRequestID Key to use when setting the request ID.
17 | type ctxKeyRequestID int
18 |
19 | // RequestIDKey is the key that holds th unique request ID in a request context.
20 | const RequestIDKey ctxKeyRequestID = 0
21 |
22 | var (
23 | // prefix is const prefix for request ID
24 | prefix string
25 |
26 | // reqID is counter for request ID
27 | reqID uint64
28 | )
29 |
30 | // init Initializes constant part of request ID
31 | func init() {
32 | hostname, err := os.Hostname()
33 | if hostname == "" || err != nil {
34 | hostname = "localhost"
35 | }
36 | var buf [12]byte
37 | var b64 string
38 | for len(b64) < 10 {
39 | _, _ = rand.Read(buf[:])
40 | b64 = base64.StdEncoding.EncodeToString(buf[:])
41 | b64 = strings.NewReplacer("+", "", "/", "").Replace(b64)
42 | }
43 |
44 | prefix = fmt.Sprintf("%s/%s", hostname, b64[0:10])
45 | }
46 |
47 | // AddRequestID is a middleware that injects a request ID into the context of each
48 | // request. A request ID is a string of the form "host.example.com/random-0001",
49 | // where "random" is a base62 random string that uniquely identifies this go
50 | // process, and where the last number is an atomically incremented request
51 | // counter.
52 | func AddRequestID(h http.Handler) http.Handler {
53 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
54 | myid := atomic.AddUint64(&reqID, 1)
55 | ctx := r.Context()
56 | ctx = context.WithValue(ctx, RequestIDKey, fmt.Sprintf("%s-%06d", prefix, myid))
57 | h.ServeHTTP(w, r.WithContext(ctx))
58 | })
59 | }
60 |
61 | // GetReqID returns a request ID from the given context if one is present.
62 | // Returns the empty string if a request ID cannot be found.
63 | func GetReqID(ctx context.Context) string {
64 | if ctx == nil {
65 | return ""
66 | }
67 | if reqID, ok := ctx.Value(RequestIDKey).(string); ok {
68 | return reqID
69 | }
70 | return ""
71 | }
72 |
--------------------------------------------------------------------------------
/servers/rest/middleware/request-id.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "context"
5 | "crypto/rand"
6 | "encoding/base64"
7 | "fmt"
8 | "net/http"
9 | "os"
10 | "strings"
11 | "sync/atomic"
12 | )
13 |
14 | // Code is taken from: https://github.com/go-chi/chi/blob/master/middleware/request_id.go
15 |
16 | // ctxKeyRequestID Key to use when setting the request ID.
17 | type ctxKeyRequestID int
18 |
19 | // RequestIDKey is the key that holds th unique request ID in a request context.
20 | const RequestIDKey ctxKeyRequestID = 0
21 |
22 | var (
23 | // prefix is const prefix for request ID
24 | prefix string
25 |
26 | // reqID is counter for request ID
27 | reqID uint64
28 | )
29 |
30 | // init Initializes constant part of request ID
31 | func init() {
32 | hostname, err := os.Hostname()
33 | if hostname == "" || err != nil {
34 | hostname = "localhost"
35 | }
36 | var buf [12]byte
37 | var b64 string
38 | for len(b64) < 10 {
39 | _, _ = rand.Read(buf[:])
40 | b64 = base64.StdEncoding.EncodeToString(buf[:])
41 | b64 = strings.NewReplacer("+", "", "/", "").Replace(b64)
42 | }
43 |
44 | prefix = fmt.Sprintf("%s/%s", hostname, b64[0:10])
45 | }
46 |
47 | // AddRequestID RequestID is a middleware that injects a request ID into the context of each
48 | // request. A request ID is a string of the form "host.example.com/random-0001",
49 | // where "random" is a base62 random string that uniquely identifies this go
50 | // process, and where the last number is an atomically incremented request
51 | // counter.
52 | func AddRequestID(h http.Handler) http.Handler {
53 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
54 | myid := atomic.AddUint64(&reqID, 1)
55 | ctx := r.Context()
56 | ctx = context.WithValue(ctx, RequestIDKey, fmt.Sprintf("%s-%06d", prefix, myid))
57 | h.ServeHTTP(w, r.WithContext(ctx))
58 | })
59 | }
60 |
61 | // GetReqID returns a request ID from the given context if one is present.
62 | // Returns the empty string if a request ID cannot be found.
63 | func GetReqID(ctx context.Context) string {
64 | if ctx == nil {
65 | return ""
66 | }
67 | if reqID, ok := ctx.Value(RequestIDKey).(string); ok {
68 | return reqID
69 | }
70 | return ""
71 | }
72 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/pb/user_profile.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package goarcc.user_profile.v1;
3 |
4 | option go_package = "/pb";
5 |
6 | import "google/api/annotation.proto";
7 | import "google/protobuf/empty.proto";
8 | import "include/graphql.proto";
9 | import "google/protobuf/timestamp.proto";
10 | import "types/enums.proto";
11 | import "validate/validate.proto";
12 |
13 | //Public Service - Requires Authentication
14 | service UserProfiles {
15 |
16 | // GetUserProfile return a profile of a user
17 | rpc GetUserProfile(google.protobuf.Empty) returns (UserProfile){
18 | option (google.api.http) = {
19 | get: "/v1/user-profile/get-user-profile"
20 | };
21 | option (graphql.schema) = {
22 | type: QUERY
23 | name: "user"
24 | };
25 | }
26 |
27 | //GetUserProfileBySub return a user profile by its unique sub provided by vcs
28 | rpc GetUserProfileBySub(GetUserProfileBySubRequest) returns (UserProfile){
29 | option (google.api.http) = {
30 | get: "/v1/user-profile/get-user-profile-by-sub/{sub}"
31 | };
32 | option (graphql.schema) = {
33 | type: QUERY
34 | name: "userBySub"
35 | };
36 | }
37 | }
38 |
39 |
40 | message GetUserProfileBySubRequest{
41 | string sub = 1 [(validate.rules).string.min_len = 3, (graphql.field) = {required: true}];
42 | }
43 |
44 | message UserProfile {
45 | string id = 1;
46 | // external unique_id provided by vcs provider
47 | string sub = 2 [(validate.rules).string = {min_len: 3 max_len: 100}, (graphql.field) = {required: true}];
48 | // name of the user
49 | string name = 3 [(validate.rules).string={min_len: 1 max_len: 100}, (graphql.field) = {required: true}];
50 | // a username provided by vcs provider
51 | string user_name = 4;
52 | //email of user
53 | string email = 5 [(validate.rules).string.min_len = 0, (validate.rules).string.email= true];
54 | // phone of user
55 | string phone_number = 6 ;
56 | types.VCSProviders external_source = 7 [(validate.rules).enum = {not_in: [0]}];
57 | string profile_pic_url = 8 ;
58 | google.protobuf.Timestamp token_valid_till = 9;
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/servers/rest/middleware/logger.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "go.uber.org/zap"
7 | "net/http"
8 | "strings"
9 | "time"
10 | )
11 |
12 | // AddLogger logs request/response pair
13 | func AddLogger(logger *zap.Logger, h http.Handler) http.Handler {
14 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15 | ctx := r.Context()
16 |
17 | // We do not want to be spammed by Kubernetes health check.
18 | // Do not log Kubernetes health check.
19 | // You can change this behavior as you wish.
20 | if r.Header.Get("X-Liveness-Probe") == "Healthz" {
21 | h.ServeHTTP(w, r)
22 | return
23 | }
24 |
25 | id := GetReqID(ctx)
26 |
27 | // Prepare fields to log
28 | var scheme string
29 | if r.TLS != nil {
30 | scheme = "https"
31 | } else {
32 | scheme = "http"
33 | }
34 | proto := r.Proto
35 | method := r.Method
36 | remoteAddr := r.RemoteAddr
37 | userAgent := r.UserAgent()
38 | uri := strings.Join([]string{scheme, "://", r.Host, r.RequestURI}, "")
39 |
40 | var request interface{}
41 | if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
42 | zap.Error(err)
43 | }
44 |
45 | if request == nil {
46 | request = "Request object is nil"
47 | }
48 | // Log HTTP request
49 | logger.Debug("request started",
50 | zap.String("request-id", id),
51 | zap.String("http-scheme", scheme),
52 | zap.String("http-proto", proto),
53 | zap.String("http-method", method),
54 | zap.String("remote-addr", remoteAddr),
55 | zap.String("user-agent", userAgent),
56 | zap.String("uri", uri),
57 | zap.ByteString("request-body", []byte(fmt.Sprintf("%v", request.(interface{})))),
58 | )
59 |
60 | t1 := time.Now()
61 | h.ServeHTTP(w, r)
62 |
63 | // Log HTTP response
64 | logger.Debug("request completed",
65 | zap.String("request-id", id),
66 | zap.String("http-scheme", scheme),
67 | zap.String("http-proto", proto),
68 | zap.String("http-method", method),
69 | zap.String("remote-addr", remoteAddr),
70 | zap.String("user-agent", userAgent),
71 | zap.String("uri", uri),
72 | zap.Float64("elapsed-ms", float64(time.Since(t1).Nanoseconds())/1000000.0),
73 | )
74 | })
75 | }
76 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/internal-svc/update_user_profile_test.go:
--------------------------------------------------------------------------------
1 | package internal_svc_test
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | "google.golang.org/grpc/codes"
9 | "google.golang.org/grpc/status"
10 | )
11 |
12 | var _ = Describe("UpdateUserProfile", func() {
13 | var (
14 | userProfileServer pb.UserProfileInternalServer
15 | //ctx context.Context
16 | //profile *pb.UserProfile
17 | )
18 | BeforeEach(func() {
19 | //profile = UsrProfile
20 | userProfileServer = UserProfileServerIntTest
21 | //ctx = CtxTest
22 | })
23 |
24 | Describe("Update a user profile", func() {
25 | //Negative Test Cases
26 | By("By a internal RPC Call")
27 | Context("Get an error when request object is nil", func() {
28 | It("should return nil exception", func() {
29 | _, err := userProfileServer.UpdateUserProfile(context.Background(), &pb.UpdateUserProfileRequest{UserProfile: nil})
30 | Expect(err).Should(Equal(status.Error(codes.FailedPrecondition, "UserProfile to update is not provided")))
31 | })
32 | })
33 |
34 | Context("Get an error when user profile object is nil", func() {
35 | It("should return nil exception", func() {
36 | })
37 | })
38 |
39 | Context("Get an error when update mask is incorrect ", func() {
40 | It("should return failed precondition error", func() {
41 | })
42 | })
43 |
44 | Context("Get an error when update mask contain id ", func() {
45 | It("should return failed precondition error", func() {
46 | })
47 | })
48 | Context("Get an error when id is incorrect", func() {
49 | It("should return failed precondition error", func() {
50 | })
51 | })
52 | Context("Get an error when user profile does not exist", func() {
53 | It("should return failed precondition error when profile not exist", func() {
54 | })
55 | })
56 |
57 | Context("Update user profile when update mask is correct and id is correct", func() {
58 | It("user profile should be updated successfully", func() {
59 | })
60 | })
61 |
62 | Context("Update user profile when update mask is correct and id is correct", func() {
63 | It("check the response of object that user profile is updated or not", func() {
64 | })
65 | })
66 | })
67 | })
68 |
--------------------------------------------------------------------------------
/modules/authentication/v1/external-svc/service.go:
--------------------------------------------------------------------------------
1 | package external_svc
2 |
3 | import (
4 | "context"
5 | oidc "github.com/coreos/go-oidc"
6 | "github.com/deqode/GoArcc/config"
7 | "github.com/deqode/GoArcc/modules/authentication/v1/pb"
8 | "github.com/deqode/GoArcc/modules/user-profile/v1/external-svc"
9 | userProfileInt "github.com/deqode/GoArcc/modules/user-profile/v1/internal-svc"
10 | usrProfilePb "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
11 | "golang.org/x/oauth2"
12 | "google.golang.org/grpc"
13 | "gorm.io/gorm"
14 | "log"
15 | )
16 |
17 | type authenticationServer struct {
18 | db *gorm.DB
19 | config *config.Config
20 | grpcClient *grpc.ClientConn
21 | userProfileServer usrProfilePb.UserProfilesServer
22 | userProfileInServer usrProfilePb.UserProfileInternalServer
23 | authenticator *Authenticator
24 | }
25 |
26 | // NewAuthenticationServer Service Implementation
27 | func NewAuthenticationServer(
28 | db *gorm.DB,
29 | config *config.Config,
30 | grpcClientConn *grpc.ClientConn,
31 |
32 | ) pb.AuthenticationsServer {
33 | userProfileSrv := external_svc.NewUserProfilesServer(db, config, grpcClientConn)
34 | userProfileInSrv := userProfileInt.NewUserProfileInServer(db, config, grpcClientConn)
35 | authenticatorCli, _ := NewAuthenticator(config)
36 | return &authenticationServer{
37 | db: db,
38 | config: config,
39 | grpcClient: grpcClientConn,
40 | userProfileServer: userProfileSrv,
41 | userProfileInServer: userProfileInSrv,
42 | authenticator: authenticatorCli,
43 | }
44 | }
45 |
46 | type Authenticator struct {
47 | Provider *oidc.Provider
48 | Config oauth2.Config
49 | Ctx context.Context
50 | }
51 |
52 | func NewAuthenticator(config *config.Config) (*Authenticator, error) {
53 | ctx := context.Background()
54 |
55 | provider, err := oidc.NewProvider(ctx, "https://"+config.Auth.Auth0Domain+"/")
56 | if err != nil {
57 | log.Printf("failed to get provider: %v", err)
58 | return nil, err
59 | }
60 |
61 | conf := oauth2.Config{
62 | ClientID: config.Auth.Auth0ClientID,
63 | ClientSecret: config.Auth.Auth0ClientSecret,
64 | RedirectURL: config.Auth.Auth0CallBackURL,
65 | Endpoint: provider.Endpoint(),
66 | Scopes: []string{oidc.ScopeOpenID, "profile", "user"},
67 | }
68 |
69 | return &Authenticator{
70 | Provider: provider,
71 | Config: conf,
72 | Ctx: ctx,
73 | }, nil
74 | }
75 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/deqode/GoArcc
2 |
3 | go 1.12
4 |
5 | require (
6 | github.com/HdrHistogram/hdrhistogram-go v1.1.0 // indirect
7 | github.com/bxcodec/faker/v3 v3.6.0
8 | github.com/coreos/go-oidc v2.2.1+incompatible
9 | github.com/deqode/GoArcc/config v0.0.0-00010101000000-000000000000
10 | github.com/deqode/GoArcc/db v0.0.0-00010101000000-000000000000
11 | github.com/deqode/GoArcc/logger v0.0.0-00010101000000-000000000000
12 | github.com/deqode/GoArcc/protos/types v0.0.0-00010101000000-000000000000
13 | github.com/deqode/GoArcc/servers/grpc v0.0.0-00010101000000-000000000000
14 | github.com/deqode/GoArcc/util/userinfo v0.0.0-00010101000000-000000000000
15 | github.com/dimiro1/health v0.0.0-20191019130555-c5cbb4d46ffc
16 | github.com/envoyproxy/protoc-gen-validate v0.1.0
17 | github.com/golang/protobuf v1.5.2
18 | github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
19 | github.com/graphql-go/graphql v0.7.9
20 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
21 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.2.0
22 | github.com/kyoh86/richgo v0.3.9 // indirect
23 | github.com/labstack/echo/v4 v4.4.0
24 | github.com/mattn/go-isatty v0.0.13 // indirect
25 | github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
26 | github.com/onsi/ginkgo v1.15.0
27 | github.com/onsi/gomega v1.10.5
28 | github.com/opentracing/opentracing-go v1.2.0
29 | github.com/pkg/errors v0.9.1
30 | github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac // indirect
31 | github.com/prometheus/client_golang v1.11.0
32 | github.com/smartystreets/assertions v1.1.0 // indirect
33 | github.com/uber/jaeger-client-go v2.25.0+incompatible
34 | github.com/uber/jaeger-lib v2.4.0+incompatible
35 | github.com/ysugimoto/grpc-graphql-gateway v0.20.0
36 | go.uber.org/fx v1.13.1
37 | go.uber.org/zap v1.18.1
38 | golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 // indirect
39 | golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
40 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
41 | golang.org/x/tools v0.1.4 // indirect
42 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c
43 | google.golang.org/grpc v1.39.0
44 | google.golang.org/protobuf v1.27.1
45 | gorm.io/gorm v1.21.11
46 | )
47 |
48 | replace (
49 | github.com/apache/thrift => github.com/apache/thrift v0.0.0-20190309152529-a9b748bb0e02
50 | github.com/deqode/GoArcc/config => ./config
51 | github.com/deqode/GoArcc/db => ./db
52 | github.com/deqode/GoArcc/logger => ./logger
53 | github.com/deqode/GoArcc/protos/types => ./protos/types
54 | github.com/deqode/GoArcc/servers/grpc => ./servers/grpc
55 | github.com/deqode/GoArcc/util/userinfo => ./util/userinfo
56 | )
57 |
--------------------------------------------------------------------------------
/logger/logger.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "errors"
5 | "go.uber.org/zap"
6 | "go.uber.org/zap/zapcore"
7 | "os"
8 | "time"
9 | )
10 |
11 | var (
12 | // Log is global logger
13 | Log *zap.Logger
14 |
15 | // customTimeFormat is custom Time format
16 | customTimeFormat string
17 | )
18 |
19 | type Config struct {
20 | LogLevel zapcore.Level
21 | Development bool
22 | }
23 |
24 | // customTimeEncoder encode Time to our custom format
25 | // This example how we can customize zap default functionality
26 | func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
27 | enc.AppendString(t.Format(customTimeFormat))
28 | }
29 |
30 | // Init initializes log by input parameters
31 | // lvl - global log level: Debug(-1), Info(0), Warn(1), Error(2), DPanic(3), Panic(4), Fatal(5)
32 | // timeFormat - custom time format for logger of empty string to use default
33 | func Init(config Config) (*zap.Logger, error) {
34 |
35 | if Log != nil {
36 | Log.Fatal("Logger already initialized once, No need to do it multiple times")
37 | return nil, errors.New("logger already initialized once")
38 | }
39 |
40 | // First, define our level-handling logic.
41 | globalLevel := config.LogLevel
42 | logTimeFormat := ""
43 |
44 | // High-priority output should also go to standard error, and low-priority
45 | // output should also go to standard out.
46 | // It is useful for Kubernetes deployment.
47 | // Kubernetes interprets os.Stdout log items as INFO and os.Stderr log items
48 | // as ERROR by default.
49 | highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
50 | return lvl >= zapcore.ErrorLevel
51 | })
52 | lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
53 | return lvl >= globalLevel && lvl < zapcore.ErrorLevel
54 | })
55 | consoleInfos := zapcore.Lock(os.Stdout)
56 | consoleErrors := zapcore.Lock(os.Stderr)
57 |
58 | // Configure console output.
59 | var useCustomTimeFormat bool
60 |
61 | var ecfg zapcore.EncoderConfig
62 | if config.Development {
63 | ecfg = zap.NewDevelopmentEncoderConfig()
64 | } else {
65 | ecfg = zap.NewProductionEncoderConfig()
66 | }
67 |
68 | if len(logTimeFormat) > 0 {
69 | customTimeFormat = logTimeFormat
70 | ecfg.EncodeTime = customTimeEncoder
71 | useCustomTimeFormat = true
72 | }
73 | consoleEncoder := zapcore.NewJSONEncoder(ecfg)
74 |
75 | // Join the outputs, encoders, and level-handling functions into
76 | // zapcore.
77 | core := zapcore.NewTee(
78 | zapcore.NewCore(consoleEncoder, consoleErrors, highPriority),
79 | zapcore.NewCore(consoleEncoder, consoleInfos, lowPriority),
80 | )
81 | // From a zapcore.Core, it's easy to construct a Logger.
82 | Log = zap.New(core)
83 | zap.ReplaceGlobals(Log)
84 | zap.RedirectStdLog(Log)
85 |
86 | if !useCustomTimeFormat {
87 | Log.Warn("time format for logger is not provided - use zap default")
88 | }
89 |
90 | return Log, nil
91 | }
92 |
--------------------------------------------------------------------------------
/servers/metrics/metrics.go:
--------------------------------------------------------------------------------
1 | package metrics
2 |
3 | import (
4 | "github.com/deqode/GoArcc/config"
5 | "github.com/deqode/GoArcc/logger"
6 | "github.com/labstack/echo/v4"
7 | "github.com/prometheus/client_golang/prometheus"
8 | "github.com/prometheus/client_golang/prometheus/promhttp"
9 | "go.uber.org/zap"
10 | "strconv"
11 | )
12 |
13 | //Metrics details of hits , response time
14 | type Metrics interface {
15 | IncHits(status int, method, path string)
16 | ObserveResponseTime(status int, method, path string, observeTime float64)
17 | }
18 |
19 | type PrometheusMetrics struct {
20 | HitsTotal prometheus.Counter
21 | Hits *prometheus.CounterVec
22 | Times *prometheus.HistogramVec
23 | }
24 |
25 | // CreateMetrics : Create Metrics will create the metrics in prometheus server.
26 | func CreateMetrics(config *config.Config) Metrics {
27 | var metrics PrometheusMetrics
28 | metrics.HitsTotal = prometheus.NewCounter(prometheus.CounterOpts{
29 | Name: config.Metrics.ServiceName + "_hits_total",
30 | })
31 | if err := prometheus.Register(metrics.HitsTotal); err != nil {
32 | logger.Log.Fatal("error in registering prometheus hits", zap.Error(err))
33 | return nil
34 | }
35 | metrics.Hits = prometheus.NewCounterVec(
36 | prometheus.CounterOpts{
37 | Name: config.Metrics.ServiceName + "_hits",
38 | },
39 | []string{"status", "method", "path"},
40 | )
41 | if err := prometheus.Register(metrics.Hits); err != nil {
42 | logger.Log.Fatal("error in registering prometheus hits", zap.Error(err))
43 | return nil
44 | }
45 | metrics.Times = prometheus.NewHistogramVec(
46 | prometheus.HistogramOpts{
47 | Name: config.Metrics.ServiceName + "_times",
48 | },
49 | []string{"status", "method", "path"},
50 | )
51 | if err := prometheus.Register(metrics.Times); err != nil {
52 | logger.Log.Fatal("error in registering prometheus", zap.Error(err))
53 | return nil
54 | }
55 | if err := prometheus.Register(prometheus.NewBuildInfoCollector()); err != nil {
56 | logger.Log.Fatal("error in registering prometheus", zap.Error(err))
57 | return nil
58 | }
59 | go func() {
60 | router := echo.New()
61 | router.GET("/metrics", echo.WrapHandler(promhttp.Handler()))
62 | if err := router.Start(config.Metrics.URL); err != nil {
63 | logger.Log.Fatal("unable to create metrics", zap.Error(err))
64 | }
65 | }()
66 | logger.Log.Info("Metrics available URL: ", zap.String("url", config.Metrics.URL),
67 | zap.String("service name:", config.Metrics.ServiceName))
68 | return &metrics
69 | }
70 |
71 | //IncHits : increment the total hits
72 | func (metrics *PrometheusMetrics) IncHits(status int, method, path string) {
73 | metrics.HitsTotal.Inc()
74 | metrics.Hits.WithLabelValues(strconv.Itoa(status), method, path).Inc()
75 | }
76 |
77 | //ObserveResponseTime :
78 | func (metrics *PrometheusMetrics) ObserveResponseTime(status int, method, path string, observeTime float64) {
79 | metrics.Times.WithLabelValues(strconv.Itoa(status), method, path).Observe(observeTime)
80 | }
81 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/internal-svc/internal_suite_test.go:
--------------------------------------------------------------------------------
1 | package internal_svc_test
2 |
3 | import (
4 | "context"
5 | "github.com/bxcodec/faker/v3"
6 | "github.com/deqode/GoArcc/client/grpcClient"
7 | "github.com/deqode/GoArcc/config"
8 | "github.com/deqode/GoArcc/db"
9 | "github.com/deqode/GoArcc/logger"
10 | "github.com/deqode/GoArcc/modules/user-profile/v1/internal-svc"
11 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
12 | "github.com/deqode/GoArcc/protos/types"
13 | "github.com/deqode/GoArcc/util/userinfo"
14 | . "github.com/onsi/ginkgo"
15 | . "github.com/onsi/gomega"
16 | "go.uber.org/zap"
17 | "google.golang.org/grpc"
18 | "gorm.io/gorm"
19 | "log"
20 | "testing"
21 | "time"
22 | )
23 |
24 | var (
25 | UserProfileServerIntTest pb.UserProfileInternalServer
26 | CtxTest context.Context
27 | UsrProfile *pb.UserProfile
28 | )
29 |
30 | func TestUserProfileInternalSvc(t *testing.T) {
31 | //now init logger
32 | logger.Init(logger.Config{
33 | LogLevel: zap.DebugLevel,
34 | Development: false,
35 | })
36 | RegisterFailHandler(Fail)
37 | RunSpecs(t, "TestUserProfile Internal Service Suite")
38 | }
39 |
40 | // This block will run only once
41 | var _ = BeforeSuite(func() {
42 | //getting config
43 | cfgFile, err := config.LoadConfig("config", config.GetConfigDirectory())
44 | if err != nil {
45 | log.Fatal(err)
46 | }
47 | cfg, err := config.ParseConfig(cfgFile)
48 | if err != nil {
49 | log.Fatal(err)
50 | }
51 | fields := struct {
52 | db *gorm.DB
53 | config *config.Config
54 | grpcClient *grpc.ClientConn
55 | }{
56 | db: db.NewConnection(cfg),
57 | config: cfg,
58 | grpcClient: grpcClient.GetGrpcClientConnection(cfg),
59 | }
60 | //Int service initialisation
61 | userProfileIntServer := internal_svc.NewUserProfileInServer(fields.db, fields.config, fields.grpcClient)
62 |
63 | id := "github_" + faker.Username()
64 | // Create a UserProfile before getting
65 | res, err := userProfileIntServer.CreateUserProfile(CtxTest, &pb.CreateUserProfileRequest{UserProfile: &pb.UserProfile{
66 | Id: id,
67 | Sub: id,
68 | Name: faker.Name(),
69 | UserName: faker.Username(),
70 | Email: faker.Email(),
71 | PhoneNumber: faker.Phonenumber(),
72 | ExternalSource: types.VCSProviders_GITHUB,
73 | ProfilePicUrl: faker.URL(),
74 | TokenValidTill: nil,
75 | }})
76 | if err != nil {
77 | return
78 | }
79 | ui := userinfo.UserInfo{
80 | ID: res.Id,
81 | Email: res.Email,
82 | Sub: res.Sub,
83 | TokenExpiry: time.Time{},
84 | }
85 |
86 | //initialize to global variable here
87 | CtxTest = userinfo.NewContext(context.Background(), ui)
88 | UsrProfile = res
89 | UserProfileServerIntTest = userProfileIntServer
90 | })
91 |
92 | // must initialize nil to global variable after suit is complete
93 | var _ = AfterSuite(func() {
94 | CtxTest = nil
95 | UsrProfile = nil
96 | UserProfileServerIntTest = nil
97 | })
98 |
--------------------------------------------------------------------------------
/protos/google/api/httpbody.proto:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package google.api;
18 |
19 | import "google/protobuf/any.proto";
20 |
21 | option cc_enable_arenas = true;
22 | option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody";
23 | option java_multiple_files = true;
24 | option java_outer_classname = "HttpBodyProto";
25 | option java_package = "com.google.api";
26 | option objc_class_prefix = "GAPI";
27 |
28 | // Message that represents an arbitrary HTTP body. It should only be used for
29 | // payload formats that can't be represented as JSON, such as raw binary or
30 | // an HTML page.
31 | //
32 | //
33 | // This message can be used both in streaming and non-streaming API methods in
34 | // the request as well as the response.
35 | //
36 | // It can be used as a top-level request field, which is convenient if one
37 | // wants to extract parameters from either the URL or HTTP template into the
38 | // request fields and also want access to the raw HTTP body.
39 | //
40 | // Example:
41 | //
42 | // message GetResourceRequest {
43 | // // A unique request id.
44 | // string request_id = 1;
45 | //
46 | // // The raw HTTP body is bound to this field.
47 | // google.api.HttpBody http_body = 2;
48 | // }
49 | //
50 | // service ResourceService {
51 | // rpc GetResource(GetResourceRequest) returns (google.api.HttpBody);
52 | // rpc UpdateResource(google.api.HttpBody) returns
53 | // (google.protobuf.Empty);
54 | // }
55 | //
56 | // Example with streaming methods:
57 | //
58 | // service CaldavService {
59 | // rpc GetCalendar(stream google.api.HttpBody)
60 | // returns (stream google.api.HttpBody);
61 | // rpc UpdateCalendar(stream google.api.HttpBody)
62 | // returns (stream google.api.HttpBody);
63 | // }
64 | //
65 | // Use of this type only changes how the request and response bodies are
66 | // handled, all other features will continue to work unchanged.
67 | message HttpBody {
68 | // The HTTP Content-Type header value specifying the content type of the body.
69 | string content_type = 1;
70 |
71 | // The HTTP request/response body as raw binary.
72 | bytes data = 2;
73 |
74 | // Application specific response metadata. Must be set in the first response
75 | // for streaming APIs.
76 | repeated google.protobuf.Any extensions = 3;
77 | }
78 |
--------------------------------------------------------------------------------
/servers/grpc/middleware/interceptors.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
5 | grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
6 | grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
7 | grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
8 | grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
9 | grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
10 | grpc_validator "github.com/grpc-ecosystem/go-grpc-middleware/validator"
11 | grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
12 | "github.com/opentracing/opentracing-go"
13 | "go.uber.org/zap"
14 | "google.golang.org/grpc"
15 | "google.golang.org/grpc/codes"
16 | "google.golang.org/grpc/status"
17 | )
18 |
19 | // AddInterceptors AddInterceptors: Add interceptors to the grpc server
20 | func AddInterceptors(logger *zap.Logger, tracer opentracing.Tracer, opts []grpc.ServerOption) []grpc.ServerOption {
21 | // Make sure that log statements internal to gRPC library are logged using the zapLogger as well.
22 | grpc_zap.ReplaceGrpcLoggerV2(logger)
23 | //grpc recovery options
24 | recoveryOptions := []grpc_recovery.Option{
25 | grpc_recovery.WithRecoveryHandler(grpcPanicsRecovery),
26 | }
27 |
28 | // Add unary interceptor
29 | opts = append(opts, grpc_middleware.WithUnaryServerChain(
30 | //turns grpc panics into unknown error
31 | grpc_recovery.UnaryServerInterceptor(recoveryOptions...),
32 | //for context tags
33 |
34 | grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
35 |
36 | grpc_opentracing.UnaryServerInterceptor(grpc_opentracing.WithTracer(tracer)),
37 | //Adding prothesis monitoring
38 | grpc_prometheus.UnaryServerInterceptor,
39 | //zap logger implementation
40 | grpc_zap.UnaryServerInterceptor(logger),
41 |
42 | grpc_auth.UnaryServerInterceptor(AuthMiddleware),
43 |
44 | //validate the incoming request - inbound in proto file
45 | //If request is not correct the error will be sent to client
46 | grpc_validator.UnaryServerInterceptor(),
47 | ))
48 |
49 | // Add stream interceptor (added as an example here)
50 | opts = append(opts, grpc_middleware.WithStreamServerChain(
51 | grpc_recovery.StreamServerInterceptor(recoveryOptions...),
52 | //context tag implementation
53 | grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
54 | //open tracing implementation
55 | grpc_opentracing.StreamServerInterceptor(),
56 | //prom implementation
57 | grpc_prometheus.StreamServerInterceptor,
58 | grpc_auth.StreamServerInterceptor(AuthMiddleware),
59 | //zap implementation
60 | grpc_zap.StreamServerInterceptor(logger),
61 |
62 | //validate the incoming request - inbound in proto file
63 | //If request is not correct the error will be sent to client
64 | grpc_validator.StreamServerInterceptor(),
65 | ))
66 |
67 | return opts
68 | }
69 |
70 | //grpcPanicsRecovery: is responsible to convert panic to the custom message
71 | func grpcPanicsRecovery(in interface{}) error {
72 | return status.Errorf(codes.Unknown, "Unknown error")
73 | }
74 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/external-svc/external_suite_test.go:
--------------------------------------------------------------------------------
1 | package external_svc_test
2 |
3 | import (
4 | "context"
5 | "github.com/bxcodec/faker/v3"
6 | "github.com/deqode/GoArcc/client/grpcClient"
7 | "github.com/deqode/GoArcc/config"
8 | "github.com/deqode/GoArcc/db"
9 | "github.com/deqode/GoArcc/logger"
10 | external_svc "github.com/deqode/GoArcc/modules/user-profile/v1/external-svc"
11 | "github.com/deqode/GoArcc/modules/user-profile/v1/internal-svc"
12 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
13 | "github.com/deqode/GoArcc/protos/types"
14 | "github.com/deqode/GoArcc/util/userinfo"
15 | . "github.com/onsi/ginkgo"
16 | . "github.com/onsi/gomega"
17 | "go.uber.org/zap"
18 | "google.golang.org/grpc"
19 | "gorm.io/gorm"
20 | "log"
21 | "testing"
22 | "time"
23 | )
24 |
25 | var (
26 | UserProfileServerTest pb.UserProfilesServer
27 | CtxTest context.Context
28 | UsrProfile *pb.UserProfile
29 | )
30 |
31 | func TestUserProfileExternalSvc(t *testing.T) {
32 | //now init logger
33 | logger.Init(logger.Config{
34 | LogLevel: zap.DebugLevel,
35 | Development: false,
36 | })
37 | RegisterFailHandler(Fail)
38 | RunSpecs(t, "TestUserProfile External Service Suite")
39 | }
40 |
41 | // This block will run only once
42 | var _ = BeforeSuite(func() {
43 | //getting config
44 | cfgFile, err := config.LoadConfig("config", config.GetConfigDirectory())
45 | if err != nil {
46 | log.Fatal(err)
47 | }
48 | cfg, err := config.ParseConfig(cfgFile)
49 | if err != nil {
50 | log.Fatal(err)
51 | }
52 | fields := struct {
53 | db *gorm.DB
54 | config *config.Config
55 | grpcClient *grpc.ClientConn
56 | }{
57 | db: db.NewConnection(cfg),
58 | config: cfg,
59 | grpcClient: grpcClient.GetGrpcClientConnection(cfg),
60 | }
61 | //Ext service initialisation
62 | userProfileServer := external_svc.NewUserProfilesServer(fields.db, fields.config, fields.grpcClient)
63 | //Int service initialisation
64 | userProfileIntServer := internal_svc.NewUserProfileInServer(fields.db, fields.config, fields.grpcClient)
65 |
66 | id := "github_" + faker.Username()
67 | // Create a UserProfile before getting
68 | res, err := userProfileIntServer.CreateUserProfile(CtxTest, &pb.CreateUserProfileRequest{UserProfile: &pb.UserProfile{
69 | Id: id,
70 | Sub: id,
71 | Name: faker.Name(),
72 | UserName: faker.Username(),
73 | Email: faker.Email(),
74 | PhoneNumber: faker.Phonenumber(),
75 | ExternalSource: types.VCSProviders_GITHUB,
76 | ProfilePicUrl: faker.URL(),
77 | TokenValidTill: nil,
78 | }})
79 | if err != nil {
80 | return
81 | }
82 | ui := userinfo.UserInfo{
83 | ID: res.Id,
84 | Email: res.Email,
85 | Sub: res.Sub,
86 | TokenExpiry: time.Time{},
87 | }
88 |
89 | //initialize to global variable here
90 | CtxTest = userinfo.NewContext(context.Background(), ui)
91 | UsrProfile = res
92 | UserProfileServerTest = userProfileServer
93 | })
94 |
95 | // must initialize nil to global variable after suit is complete
96 | var _ = AfterSuite(func() {
97 | CtxTest = nil
98 | UsrProfile = nil
99 | UserProfileServerTest = nil
100 | })
101 |
--------------------------------------------------------------------------------
/modules/authentication/v1/pb/authentication.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package goarcc.authentication.v1;
3 |
4 | option go_package = "/pb";
5 |
6 | import "google/api/annotation.proto";
7 | import "google/protobuf/empty.proto";
8 | import "validate/validate.proto";
9 |
10 | service ValidateLoginService {
11 | rpc ValidateUserLogin (ValidateUserLoginRequest) returns (ValidateUserLoginResponse);
12 | }
13 |
14 | //ValidateUserLoginRequest contains user login credential.
15 | message ValidateUserLoginRequest {
16 | //Id is the user Unique identifier
17 | string id = 1;
18 | // Password is the user password
19 | string password = 2;
20 | }
21 |
22 | message ValidateUserLoginResponse {
23 | //Id is the user Unique identifier
24 | string id = 1;
25 | }
26 |
27 | service Authentications {
28 | // Login provide a url of External OAuth login endpoint (Auth0)
29 | rpc Login (google.protobuf.Empty) returns (LoginResponse) {
30 | option (google.api.http) = {
31 | get: "/v1/authentication/login"
32 | };
33 | };
34 |
35 | // LoginCallback receives a callback from external OAuth application
36 | rpc LoginCallback (LoginCallbackRequest) returns (LoginCallbackResponse) {
37 | option (google.api.http) = {
38 | get: "/v1/authentication/callback"
39 | };
40 | };
41 |
42 | rpc Logout (google.protobuf.Empty) returns (google.protobuf.Empty) {
43 | option (google.api.http) = {
44 | get: "/v1/authentication/logout"
45 | };
46 | };
47 |
48 | // GetUserLogin returns the specified user by its id.
49 | // rpc GetUserLogin (GetUserLoginRequest) returns (google.protobuf.Empty);
50 |
51 | // DeleteUserLogin is used to delete a user from the system, this will delete all
52 | // rpc DeleteUserLogin (DeleteUserLoginRequest) returns (google.protobuf.Empty);
53 |
54 |
55 | // rpc UpdateUserPassword (UpdateUserPasswordRequest) returns (google.protobuf.Empty);
56 |
57 | // ResetUserPassword , if a user has forgot the password then
58 | // this method can be used to reset the password
59 | // rpc ResetUserPassword (ResetUserPasswordRequest) returns (google.protobuf.Empty);
60 |
61 | }
62 |
63 | message LoginResponse{
64 | string url = 1;
65 | }
66 |
67 | message LoginCallbackRequest{
68 | string state = 1 [(validate.rules).string.min_len = 3];
69 | string code = 2 [(validate.rules).string.min_len = 3];
70 | }
71 |
72 | message LoginCallbackResponse{
73 | string id_token = 1;
74 | string access_token = 2;
75 | string user_id = 3;
76 | }
77 | //message GetUserLoginRequest {
78 | // // Id is the unique user id
79 | // string id = 1;// [(validate.rules).string.min_len = 3];
80 | //}
81 | //
82 | //message DeleteUserLoginRequest {
83 | // // Id is the unique user id
84 | // string id = 1;// [(validate.rules).string.min_len = 3];
85 | //}
86 | //
87 | //message UpdateUserPasswordRequest {
88 | // // Id is the unique user id
89 | // string id = 1 ;//[(validate.rules).string.min_len = 3];
90 | // // Password to be added against the given user id.
91 | // string old_password = 2;// [(validate.rules).string = { min_len:8 max_len:36}];
92 | // string new_password = 3;// [(validate.rules).string = { min_len:8 max_len:36}];
93 | //}
94 | //
95 | //message ResetUserPasswordRequest {
96 | // // Id is the unique user id
97 | // string id = 1;// [(validate.rules).string.min_len = 3];
98 | // // Password to be added against the given user id.
99 | // string new_password = 3;// [(validate.rules).string = { min_len:8 max_len:36}];
100 | //}
101 |
--------------------------------------------------------------------------------
/protos/google/api/field_behaviour.proto:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package google.api;
18 |
19 | import "google/protobuf/descriptor.proto";
20 |
21 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
22 | option java_multiple_files = true;
23 | option java_outer_classname = "FieldBehaviorProto";
24 | option java_package = "com.google.api";
25 | option objc_class_prefix = "GAPI";
26 |
27 | extend google.protobuf.FieldOptions {
28 | // A designation of a specific field behavior (required, output only, etc.)
29 | // in protobuf messages.
30 | //
31 | // Examples:
32 | //
33 | // string name = 1 [(google.api.field_behavior) = REQUIRED];
34 | // State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
35 | // google.protobuf.Duration ttl = 1
36 | // [(google.api.field_behavior) = INPUT_ONLY];
37 | // google.protobuf.Timestamp expire_time = 1
38 | // [(google.api.field_behavior) = OUTPUT_ONLY,
39 | // (google.api.field_behavior) = IMMUTABLE];
40 | repeated google.api.FieldBehavior field_behavior = 1052;
41 | }
42 |
43 | // An indicator of the behavior of a given field (for example, that a field
44 | // is required in requests, or given as output but ignored as input).
45 | // This **does not** change the behavior in protocol buffers itself; it only
46 | // denotes the behavior and may affect how API tooling handles the field.
47 | //
48 | // Note: This enum **may** receive new values in the future.
49 | enum FieldBehavior {
50 | // Conventional default for enums. Do not use this.
51 | FIELD_BEHAVIOR_UNSPECIFIED = 0;
52 |
53 | // Specifically denotes a field as optional.
54 | // While all fields in protocol buffers are optional, this may be specified
55 | // for emphasis if appropriate.
56 | OPTIONAL = 1;
57 |
58 | // Denotes a field as required.
59 | // This indicates that the field **must** be provided as part of the request,
60 | // and failure to do so will cause an error (usually `INVALID_ARGUMENT`).
61 | REQUIRED = 2;
62 |
63 | // Denotes a field as output only.
64 | // This indicates that the field is provided in responses, but including the
65 | // field in a request does nothing (the server *must* ignore it and
66 | // *must not* throw an error as a result of the field's presence).
67 | OUTPUT_ONLY = 3;
68 |
69 | // Denotes a field as input only.
70 | // This indicates that the field is provided in requests, and the
71 | // corresponding field is not included in output.
72 | INPUT_ONLY = 4;
73 |
74 | // Denotes a field as immutable.
75 | // This indicates that the field may be set once in a request to create a
76 | // resource, but may not be changed thereafter.
77 | IMMUTABLE = 5;
78 |
79 | // Denotes that a (repeated) field is an unordered list.
80 | // This indicates that the service may provide the elements of the list
81 | // in any arbitrary order, rather than the order the user originally
82 | // provided. Additionally, the list's order may or may not be stable.
83 | UNORDERED_LIST = 6;
84 | }
85 |
--------------------------------------------------------------------------------
/servers/prometheusServer/server.go:
--------------------------------------------------------------------------------
1 | package prometheusServer
2 |
3 | /*
4 | import (
5 | "github.com/deqode/GoArcc/config"
6 | "github.com/deqode/GoArcc/util/logger"
7 | "context"
8 | grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
9 | "github.com/prometheus/client_golang/prometheus"
10 | "github.com/prometheus/client_golang/prometheus/promhttp"
11 | "google.golang.org/grpc"
12 | "google.golang.org/grpc/reflection"
13 | "net/http"
14 | "os"
15 | "os/signal"
16 | "time"
17 | )
18 |
19 | type PrometheusConfig struct {
20 | Registry *prometheus.Registry
21 | ServerMetrics *grpc_prometheus.ServerMetrics
22 | }
23 |
24 | func InitPromthesiusServer() *PrometheusConfig {
25 | // Create a metrics registry.
26 | registry := prometheus.NewRegistry()
27 | // Create some standard server metrics.
28 | grpcServerMetrics := grpc_prometheus.NewServerMetrics()
29 | registry.MustRegister(grpcServerMetrics)
30 | //CreateMetrics()
31 | return &PrometheusConfig{
32 | Registry: registry,
33 | ServerMetrics: grpcServerMetrics,
34 | }
35 | }
36 |
37 | //Note: Prometheus will only work when all service will be registered with grpc already
38 | func PrometheusRunner(config *config.Config, grpcServer *grpc.Server, prometheusConfig *PrometheusConfig) error {
39 | go func() {
40 | _ = RunPrometheusServer(context.Background(), config, grpcServer, prometheusConfig)
41 | }()
42 | return nil
43 | }
44 |
45 | // Started the prometheus server
46 | func RunPrometheusServer(ctxhelper context.Context, config *config.Config, grpcServer *grpc.Server, prometheusConfig *PrometheusConfig) error {
47 | ctxhelper, cancel := context.WithCancel(ctxhelper)
48 | defer cancel()
49 |
50 | srv := &http.Server{
51 | Addr: config.Promthesius.Host + ":" + config.Promthesius.Port,
52 | Handler: promhttp.HandlerFor(prometheusConfig.Registry, promhttp.HandlerOpts{}),
53 | }
54 |
55 | reflection.Register(grpcServer)
56 | //initializing metrics
57 | prometheusConfig.ServerMetrics.InitializeMetrics(grpcServer)
58 | grpc_prometheus.Register(grpcServer)
59 | grpc_prometheus.EnableHandlingTimeHistogram()
60 | http.Handle("/metrics", promhttp.Handler())
61 | // graceful shutdown
62 | c := make(chan os.Signal, 1)
63 | signal.Notify(c, os.Interrupt)
64 | go func() {
65 | for range c {
66 | // sig is a ^C, handle it
67 | }
68 | _, cancel := context.WithTimeout(ctxhelper, 30*time.Second)
69 | defer cancel()
70 | logger.Log.Info("graceful shutting down promthesius Server")
71 | _ = srv.Shutdown(ctxhelper)
72 | }()
73 |
74 | logger.Log.Info("starting prometheus server...")
75 | return srv.ListenAndServe()
76 | }
77 |
78 | type PrometheusMetrics struct {
79 | HitsTotal prometheus.Counter
80 | Hits *prometheus.CounterVec
81 | Times *prometheus.HistogramVec
82 | }
83 |
84 | func CreateMetrics() (*PrometheusMetrics, error) {
85 | var metr PrometheusMetrics
86 | metr.HitsTotal = prometheus.NewCounter(prometheus.CounterOpts{
87 | Name: "_hits_total",
88 | })
89 | if err := prometheus.Register(metr.HitsTotal); err != nil {
90 | return nil, err
91 | }
92 | metr.Hits = prometheus.NewCounterVec(
93 | prometheus.CounterOpts{
94 | Name: "_hits",
95 | },
96 | []string{"status", "method", "path"},
97 | )
98 | if err := prometheus.Register(metr.Hits); err != nil {
99 | return nil, err
100 | }
101 | metr.Times = prometheus.NewHistogramVec(
102 | prometheus.HistogramOpts{
103 | Name: "_times",
104 | },
105 | []string{"status", "method", "path"},
106 | )
107 | if err := prometheus.Register(metr.Times); err != nil {
108 | return nil, err
109 | }
110 | if err := prometheus.Register(prometheus.NewBuildInfoCollector()); err != nil {
111 | return nil, err
112 | }
113 | return &metr, nil
114 | }
115 | */
116 |
--------------------------------------------------------------------------------
/modules/authentication/v1/external-svc/login_callback.go:
--------------------------------------------------------------------------------
1 | package external_svc
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "github.com/coreos/go-oidc"
7 | "github.com/deqode/GoArcc/modules/authentication/v1/pb"
8 | userprofileModel "github.com/deqode/GoArcc/modules/user-profile/v1/models"
9 | userProfilePb "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
10 | "github.com/deqode/GoArcc/protos/types"
11 | "google.golang.org/grpc/codes"
12 | "google.golang.org/grpc/status"
13 | "gorm.io/gorm"
14 | "time"
15 | )
16 |
17 | const tokenKey = "id_token"
18 |
19 | func (s *authenticationServer) LoginCallback(ctx context.Context, in *pb.LoginCallbackRequest) (*pb.LoginCallbackResponse, error) {
20 | if in == nil {
21 | return nil, status.Error(codes.FailedPrecondition, "Request is Nil")
22 | }
23 | if err := in.Validate(); err != nil {
24 | return nil, err
25 | }
26 | token, err := s.authenticator.Config.Exchange(ctx, in.Code)
27 | if err != nil {
28 | return nil, status.Errorf(codes.Unauthenticated, err.Error())
29 | }
30 |
31 | rawIDToken, ok := token.Extra(tokenKey).(string)
32 | if !ok {
33 | return nil, status.Error(codes.Internal, "No id_token field in oauth2 token")
34 | }
35 |
36 | oidcConfig := &oidc.Config{
37 | ClientID: s.config.Auth.Auth0ClientID,
38 | }
39 |
40 | idToken, err := s.authenticator.Provider.Verifier(oidcConfig).Verify(ctx, rawIDToken)
41 | if err != nil {
42 | return nil, status.Error(codes.Internal, "Failed to verify ID Token")
43 | }
44 |
45 | // Getting now the userInfo
46 | var profile map[string]interface{}
47 | if err := idToken.Claims(&profile); err != nil {
48 | return nil, status.Error(codes.Internal, err.Error())
49 | }
50 |
51 | //get user_details
52 | var userId string
53 | usr, err := s.userProfileServer.GetUserProfileBySub(ctx, &userProfilePb.GetUserProfileBySubRequest{
54 | Sub: fmt.Sprintf("%s", profile["sub"]),
55 | })
56 | if usr != nil {
57 | return &pb.LoginCallbackResponse{
58 | IdToken: rawIDToken,
59 | AccessToken: token.AccessToken,
60 | UserId: usr.Id,
61 | }, nil
62 | }
63 | if err != nil {
64 | if err != gorm.ErrRecordNotFound {
65 | return nil, err
66 | }
67 | }
68 |
69 | userId, err = s.CreateUserAndAccount(ctx, profile)
70 | if err != nil {
71 | return nil, err
72 | }
73 | return &pb.LoginCallbackResponse{
74 | IdToken: rawIDToken,
75 | AccessToken: token.AccessToken,
76 | UserId: userId,
77 | }, nil
78 | }
79 |
80 | func (s *authenticationServer) CreateUserAndAccount(ctx context.Context, profile map[string]interface{}) (string, error) {
81 | gormDb := s.db
82 | userid := ""
83 | err := gormDb.Transaction(func(transaction *gorm.DB) error {
84 | usr := struct {
85 | ID string
86 | Name string
87 | UserName string
88 | Sub string
89 | ProfilePicURL string
90 | Source types.VCSProviders
91 | }{
92 | ID: fmt.Sprintf("%s", profile["sub"]),
93 | Name: fmt.Sprintf("%s", profile["name"]),
94 | UserName: fmt.Sprintf("%s", profile["nickname"]),
95 | Sub: fmt.Sprintf("%s", profile["sub"]),
96 | ProfilePicURL: fmt.Sprintf("%s", profile["picture"]),
97 | Source: types.VCSProviders_GITHUB,
98 | }
99 | userProfileModel := userprofileModel.UserProfile{
100 | ID: usr.ID,
101 | Name: usr.Name,
102 | UserName: usr.UserName,
103 | Sub: usr.Sub,
104 | ProfilePicURL: usr.ProfilePicURL,
105 | Source: usr.Source,
106 | CreatedAt: time.Time{},
107 | UpdatedAt: time.Time{},
108 | DeletedAt: gorm.DeletedAt{},
109 | }
110 | tx := transaction.Create(&userProfileModel)
111 | if tx.Error != nil {
112 | return tx.Error
113 | }
114 |
115 | //if everything goes right then the transaction will commit by itself
116 | userid = usr.ID
117 | return nil
118 | })
119 | //when transaction does not happen for some internal reason
120 | if err != nil {
121 | return "", status.Error(codes.Internal, err.Error())
122 | }
123 | return userid, nil
124 | }
125 |
--------------------------------------------------------------------------------
/logger/go.sum:
--------------------------------------------------------------------------------
1 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
2 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
7 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
8 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
9 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
10 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
11 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
12 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
15 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
16 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
17 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
18 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
19 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
20 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
21 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
22 | go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
23 | go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
24 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
25 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
26 | go.uber.org/zap v1.18.1 h1:CSUJ2mjFszzEWt4CdKISEuChVIXGBn3lAPwkRGyVrc4=
27 | go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
28 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
29 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
30 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
31 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
32 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
33 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
34 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
35 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
36 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
37 | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E=
38 | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
39 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
40 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
41 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
42 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
43 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
44 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
45 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
46 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
47 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
48 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
49 |
--------------------------------------------------------------------------------
/util/ctxhelper/go.sum:
--------------------------------------------------------------------------------
1 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
2 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
7 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
8 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
9 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
10 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
11 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
12 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
15 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
16 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
17 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
18 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
19 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
20 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
21 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
22 | go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
23 | go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
24 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
25 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
26 | go.uber.org/zap v1.18.1 h1:CSUJ2mjFszzEWt4CdKISEuChVIXGBn3lAPwkRGyVrc4=
27 | go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
28 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
29 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
30 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
31 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
32 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
33 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
34 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
35 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
36 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
37 | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E=
38 | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
39 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
40 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
41 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
42 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
43 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
44 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
45 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
46 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
47 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
48 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
49 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/internal-svc/create_user_profile_test.go:
--------------------------------------------------------------------------------
1 | package internal_svc_test
2 |
3 | import (
4 | "context"
5 | "github.com/deqode/GoArcc/modules/user-profile/v1/pb"
6 | "github.com/deqode/GoArcc/protos/types"
7 | . "github.com/onsi/ginkgo"
8 | . "github.com/onsi/gomega"
9 | "google.golang.org/grpc/codes"
10 | "google.golang.org/grpc/status"
11 | )
12 |
13 | var _ = Describe("CreateUserProfile", func() {
14 | var (
15 | userProfileServer pb.UserProfileInternalServer
16 | profile *pb.UserProfile
17 | )
18 |
19 | // this block will run after each it block
20 | BeforeEach(func() {
21 | userProfileServer = UserProfileServerIntTest
22 | profile = UsrProfile
23 | })
24 |
25 | Describe("Creating an user profile", func() {
26 | //Negative Test Cases
27 | Context("Get an error when nil User provided", func() {
28 | It("should return nil exception", func() {
29 | _, err := userProfileServer.CreateUserProfile(context.Background(), &pb.CreateUserProfileRequest{UserProfile: nil})
30 | Expect(err).Should(Equal(status.Error(codes.FailedPrecondition, "UserProfile not provided")))
31 | })
32 | })
33 |
34 | Context("Create a user when subject is empty", func() {
35 | It("It should return validation error", func() {
36 |
37 | })
38 | })
39 |
40 | Context("Get an error when user created with wrong email", func() {
41 | It("if email is not provided then ignore because its a non required field", func() {})
42 | It("Should return validation error when email is provided but not formatted", func() {})
43 | })
44 |
45 | Context("Get an error when user created with wrong Phone", func() {
46 | It("if phone is not provided then ignore because its a non required field", func() {})
47 | It("Should return validation error when phone number is not in proper", func() {})
48 | })
49 |
50 | Context("Get an error when name of user not provided", func() {
51 | It("if name is nil", func() {})
52 | It("if name length exceed maximum upto 100 character", func() {})
53 | })
54 |
55 | Context("Get an error when user-name of user not provided", func() {
56 | It("if user-name is empty return an error", func() {})
57 | It("if user-name length exceed maximum upto 100 character return error", func() {})
58 | })
59 |
60 | Context("Return proper error when user is creating from unknown source", func() {
61 | It("should return error if user is from unknown source", func() {
62 | _, err := userProfileServer.CreateUserProfile(context.Background(), &pb.CreateUserProfileRequest{UserProfile: &pb.UserProfile{
63 | Id: profile.Id,
64 | Sub: profile.Sub,
65 | Name: profile.Name,
66 | UserName: profile.UserName,
67 | Email: profile.Email,
68 | PhoneNumber: profile.PhoneNumber,
69 | ExternalSource: types.VCSProviders_UNKNOWN,
70 | ProfilePicUrl: profile.ProfilePicUrl,
71 | TokenValidTill: profile.TokenValidTill,
72 | }})
73 | Expect(err.(pb.CreateUserProfileRequestValidationError).Cause().(pb.UserProfileValidationError).Reason()).Should(Equal("value must not be in list [0]"))
74 | })
75 | })
76 |
77 | Context("Get an error when wrong profile-address provided", func() {
78 | It("if not a valid url return error", func() {})
79 |
80 | })
81 |
82 | //Positive Test Cases
83 | Context("Create a user profile", func() {
84 | It("should return user_id as uuid", func() {
85 | _, err := userProfileServer.CreateUserProfile(context.Background(), &pb.CreateUserProfileRequest{UserProfile: &pb.UserProfile{
86 | Id: profile.Id + "test",
87 | Sub: profile.Sub + "test",
88 | Name: profile.Name,
89 | UserName: profile.UserName,
90 | Email: profile.Email,
91 | PhoneNumber: profile.PhoneNumber,
92 | ExternalSource: profile.ExternalSource,
93 | ProfilePicUrl: profile.ProfilePicUrl,
94 | TokenValidTill: profile.TokenValidTill,
95 | }})
96 | Expect(err).To(BeNil(), "Error")
97 | })
98 | })
99 |
100 | Context("Get an error when user create with already existing sub", func() {
101 | It("should return already exists error", func() {
102 | request := profile
103 | _, err := userProfileServer.CreateUserProfile(context.Background(), &pb.CreateUserProfileRequest{UserProfile: request})
104 | Expect(err).To(Not(BeNil()), "Error")
105 | })
106 | })
107 |
108 | })
109 | })
110 |
--------------------------------------------------------------------------------
/protos/types/enums.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // versions:
3 | // protoc-gen-go v1.26.0
4 | // protoc v3.6.1
5 | // source: enums.proto
6 |
7 | package types
8 |
9 | import (
10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect"
11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl"
12 | reflect "reflect"
13 | sync "sync"
14 | )
15 |
16 | const (
17 | // Verify that this generated code is sufficiently up-to-date.
18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
19 | // Verify that runtime/protoimpl is sufficiently up-to-date.
20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
21 | )
22 |
23 | type VCSProviders int32
24 |
25 | const (
26 | VCSProviders_UNKNOWN VCSProviders = 0
27 | VCSProviders_GITHUB VCSProviders = 1
28 | VCSProviders_GITLAB VCSProviders = 2
29 | VCSProviders_BITBUCKET VCSProviders = 3
30 | )
31 |
32 | // Enum value maps for VCSProviders.
33 | var (
34 | VCSProviders_name = map[int32]string{
35 | 0: "UNKNOWN",
36 | 1: "GITHUB",
37 | 2: "GITLAB",
38 | 3: "BITBUCKET",
39 | }
40 | VCSProviders_value = map[string]int32{
41 | "UNKNOWN": 0,
42 | "GITHUB": 1,
43 | "GITLAB": 2,
44 | "BITBUCKET": 3,
45 | }
46 | )
47 |
48 | func (x VCSProviders) Enum() *VCSProviders {
49 | p := new(VCSProviders)
50 | *p = x
51 | return p
52 | }
53 |
54 | func (x VCSProviders) String() string {
55 | return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
56 | }
57 |
58 | func (VCSProviders) Descriptor() protoreflect.EnumDescriptor {
59 | return file_enums_proto_enumTypes[0].Descriptor()
60 | }
61 |
62 | func (VCSProviders) Type() protoreflect.EnumType {
63 | return &file_enums_proto_enumTypes[0]
64 | }
65 |
66 | func (x VCSProviders) Number() protoreflect.EnumNumber {
67 | return protoreflect.EnumNumber(x)
68 | }
69 |
70 | // Deprecated: Use VCSProviders.Descriptor instead.
71 | func (VCSProviders) EnumDescriptor() ([]byte, []int) {
72 | return file_enums_proto_rawDescGZIP(), []int{0}
73 | }
74 |
75 | var File_enums_proto protoreflect.FileDescriptor
76 |
77 | var file_enums_proto_rawDesc = []byte{
78 | 0x0a, 0x0b, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x61,
79 | 0x6c, 0x66, 0x72, 0x65, 0x64, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2a, 0x42, 0x0a, 0x0c, 0x56,
80 | 0x43, 0x53, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55,
81 | 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x49, 0x54, 0x48,
82 | 0x55, 0x42, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x49, 0x54, 0x4c, 0x41, 0x42, 0x10, 0x02,
83 | 0x12, 0x0d, 0x0a, 0x09, 0x42, 0x49, 0x54, 0x42, 0x55, 0x43, 0x4b, 0x45, 0x54, 0x10, 0x03, 0x42,
84 | 0x15, 0x5a, 0x13, 0x61, 0x6c, 0x66, 0x72, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
85 | 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
86 | }
87 |
88 | var (
89 | file_enums_proto_rawDescOnce sync.Once
90 | file_enums_proto_rawDescData = file_enums_proto_rawDesc
91 | )
92 |
93 | func file_enums_proto_rawDescGZIP() []byte {
94 | file_enums_proto_rawDescOnce.Do(func() {
95 | file_enums_proto_rawDescData = protoimpl.X.CompressGZIP(file_enums_proto_rawDescData)
96 | })
97 | return file_enums_proto_rawDescData
98 | }
99 |
100 | var file_enums_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
101 | var file_enums_proto_goTypes = []interface{}{
102 | (VCSProviders)(0), // 0: goarcc.types.VCSProviders
103 | }
104 | var file_enums_proto_depIdxs = []int32{
105 | 0, // [0:0] is the sub-list for method output_type
106 | 0, // [0:0] is the sub-list for method input_type
107 | 0, // [0:0] is the sub-list for extension type_name
108 | 0, // [0:0] is the sub-list for extension extendee
109 | 0, // [0:0] is the sub-list for field type_name
110 | }
111 |
112 | func init() { file_enums_proto_init() }
113 | func file_enums_proto_init() {
114 | if File_enums_proto != nil {
115 | return
116 | }
117 | type x struct{}
118 | out := protoimpl.TypeBuilder{
119 | File: protoimpl.DescBuilder{
120 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
121 | RawDescriptor: file_enums_proto_rawDesc,
122 | NumEnums: 1,
123 | NumMessages: 0,
124 | NumExtensions: 0,
125 | NumServices: 0,
126 | },
127 | GoTypes: file_enums_proto_goTypes,
128 | DependencyIndexes: file_enums_proto_depIdxs,
129 | EnumInfos: file_enums_proto_enumTypes,
130 | }.Build()
131 | File_enums_proto = out.File
132 | file_enums_proto_rawDesc = nil
133 | file_enums_proto_goTypes = nil
134 | file_enums_proto_depIdxs = nil
135 | }
136 |
--------------------------------------------------------------------------------
/util/userinfo/userinfo.go:
--------------------------------------------------------------------------------
1 | package userinfo
2 |
3 | import (
4 | "context"
5 | "google.golang.org/grpc/codes"
6 | "google.golang.org/grpc/status"
7 | "gorm.io/gorm"
8 | "time"
9 | )
10 |
11 | type UserInfo struct {
12 | ID string
13 | Email string
14 | Sub string
15 | TokenExpiry time.Time
16 | }
17 |
18 | type ValidateUserInfo struct {
19 | Ctx context.Context
20 | RootTable interface{}
21 | RootTableTag string
22 | //Key Will be tag name
23 | Args map[string]interface{}
24 | SkipRootValidation bool
25 | SkipArgsValidation bool
26 | Db *gorm.DB
27 | skipValidation bool
28 | }
29 |
30 | const (
31 | userKey key = iota
32 | clientIpKey
33 | grpcCallKey
34 | )
35 |
36 | type key int
37 |
38 | func GetClientIP(ctx context.Context) string {
39 | if ip, ok := ctx.Value(clientIpKey).(string); ok {
40 | return ip
41 | }
42 | return ""
43 | }
44 |
45 | func NewContextWithClientIP(ctx context.Context, ip string) context.Context {
46 | return context.WithValue(ctx, clientIpKey, ip)
47 | }
48 |
49 | func NewContextForGrpcCall(ctx context.Context) context.Context {
50 | return context.WithValue(ctx, grpcCallKey, true)
51 | }
52 |
53 | func IsGrpcCall(ctx context.Context) bool {
54 | if v, ok := ctx.Value(grpcCallKey).(bool); ok {
55 | return v
56 | }
57 | return false
58 | }
59 |
60 | func WithClaims(ctx context.Context, c map[string]interface{}) context.Context {
61 | return context.WithValue(ctx, userKey, c)
62 | }
63 |
64 | // FromContext returns the User value stored in ctx, if any.
65 | func FromContext(ctx context.Context) (usr UserInfo) {
66 | usr = FromClaims(ctx.Value(userKey).(map[string]interface{}))
67 | return
68 | }
69 |
70 | func NewContext(ctx context.Context, u UserInfo) context.Context {
71 | m := make(map[string]interface{})
72 | m["sub"] = u.Sub
73 | return context.WithValue(ctx, userKey, m)
74 | }
75 |
76 | func FromClaims(claims map[string]interface{}) (ui UserInfo) {
77 | if claims == nil {
78 | return
79 | }
80 |
81 | if v, ok := claims["sub"]; ok {
82 | ui.Sub = v.(string)
83 | ui.ID = v.(string)
84 | }
85 | if v, ok := claims["ext"]; ok {
86 | if e, exist := v.(map[string]interface{})["email"]; exist {
87 | ui.Email = e.(string)
88 | }
89 | }
90 |
91 | if v, ok := claims["exp"]; ok {
92 | tm := time.Unix(int64(v.(float64)), 0)
93 | ui.TokenExpiry = tm
94 | }
95 |
96 | return
97 | }
98 |
99 | func (validateUserInfo *ValidateUserInfo) validate() error {
100 | if validateUserInfo == nil {
101 | return status.Error(codes.FailedPrecondition, "authentication validation failed")
102 | }
103 | if !validateUserInfo.SkipRootValidation && (validateUserInfo.RootTableTag == "" || validateUserInfo.RootTable == nil) {
104 | return status.Error(codes.FailedPrecondition, "authentication validation failed")
105 | }
106 | if !validateUserInfo.SkipArgsValidation && validateUserInfo.Args == nil {
107 | return status.Error(codes.FailedPrecondition, "authentication validation failed")
108 | }
109 | return nil
110 | }
111 |
112 | // BasicAuthValidation It will check the user id present in context is valid or not
113 | func BasicAuthValidation(ctx context.Context, db *gorm.DB) error {
114 | v := &ValidateUserInfo{
115 | Ctx: ctx,
116 | //RootTable: &models.UserProfile{},
117 | RootTableTag: "id",
118 | Args: nil,
119 | SkipRootValidation: false,
120 | SkipArgsValidation: true,
121 | Db: db,
122 | skipValidation: true,
123 | }
124 | if err := v.ValidateUser(); err != nil {
125 | return err
126 | }
127 | return nil
128 | }
129 |
130 | // ValidateUser Validate User : Will validate user id is present in the given table or not
131 | func (validateUserInfo *ValidateUserInfo) ValidateUser() error {
132 | if !validateUserInfo.skipValidation {
133 | if err := validateUserInfo.validate(); err != nil {
134 | return err
135 | }
136 | }
137 | //Extract user id from context
138 | userInfo := FromContext(validateUserInfo.Ctx)
139 | if userInfo.Sub == "" {
140 | return status.Error(codes.PermissionDenied, "unauthenticated user")
141 | }
142 | if !validateUserInfo.SkipRootValidation {
143 | tx := validateUserInfo.Db.Where(validateUserInfo.RootTableTag+" = ?", userInfo.ID).First(validateUserInfo.RootTable)
144 | if tx.Error != nil {
145 | return status.Error(codes.PermissionDenied, "unauthenticated user")
146 | }
147 | }
148 |
149 | if !validateUserInfo.SkipArgsValidation {
150 | for tag, argTable := range validateUserInfo.Args {
151 | tx := validateUserInfo.Db.Where(tag+" = ?", userInfo.ID).First(argTable)
152 | if tx.Error != nil {
153 | return status.Error(codes.PermissionDenied, "unauthenticated user")
154 | }
155 | }
156 | }
157 | return nil
158 | }
159 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GoArcc - Go monolith with embedded microservices including GRPC,REST, graphQL and The Clean Architecture.
2 |
3 | 
4 |
5 | 
6 | [](https://github.com/deqode/GoArcc/actions/workflows/go.yml)
7 | [](https://pkg.go.dev/github.com/github.com/deqode/GoArcc)
8 | [](https://goreportcard.com/report/github.com/deqode/GoArcc)
9 | [](https://github.com/golang-standards/project-layout)
10 | 
11 | 
12 |
13 | ## Description
14 | When you start writing a Go project, GoArcc helps to set up all the initial code boilerplate for your project. Initial boilerplate code means how you should organize your codebase and how you can write multiple services. We have support for REST, Graphql as well as gRPC.
15 |
16 | It supports logging, tracing, health check, Jaeger, etc so that any developer will come and write services within a minute.
17 |
18 | In short, GoArcc is a boilerplate setup codebase for any monolithic(Architecture) based web/mobile applications which later converted into microservices(Architecture).
19 |
20 | [Read more about GoArcc](https://deqode.github.io/GoArcc/)
21 |
22 | ## Structure of Go packages
23 |
24 | - `client/*` - clients for server dialing
25 | - `grpcClient` - grpcClient dials grpc server
26 | - `cmd/*` - main application(s)
27 | - `config` - application related configs
28 | - `db` - postgres DB connection and adapters
29 | - `logger` - global zap logger
30 | - `modules/*` - embedded microservices, with structure:
31 | - `external-svc` - exposed apis logic implementation
32 | - `internal-svc` - unexposed apis logic implementation
33 | - `models` - database models, operations using gorm
34 | - `pb` - autogenerated files from .proto file
35 | - `protos` - External required protos for internal protos
36 | - `types` - application related common proto types
37 | - `servers` - all running servers
38 | - `graphql` - ms graphql registration and server invoke
39 | - `grpc` - ms grpc registration and server invoke
40 | - `rest` - ms rest registration and server invoke
41 | ## Features
42 |
43 | - [X] Project structure (mostly) follows
44 | [Standard Go Project Layout](https://github.com/golang-standards/project-layout).
45 | - [X] Easily testable code (thanks to The Clean Architecture).
46 | - [X] Graceful shutdown support.
47 | - [X] Example gRPC API:
48 | - [X] External and internal APIs on different host/port.
49 | - [X] gRPC services with and without token-based authentication.
50 | - [X] API design (mostly) follows
51 | [Google API Design Guide](https://cloud.google.com/apis/design) and
52 | [Google API Improvement Proposals](https://google.aip.dev/).
53 | - [X] Example graphQL API:
54 | - [X] Example REST API:
55 | - [X] Example tests, both unit and integration.
56 | - [X] Production logging using [zap](https://github.com/uber-go/zap).
57 | - [X] Production metrics using Prometheus.
58 | - [X] Docker and docker-compose support.
59 | - [X] Smart test coverage report
60 | - [X] CI/CD setup for GitHub Actions.
61 |
62 | ## Development
63 |
64 | ### Requirements
65 |
66 | - Go 1.16
67 | - [Docker](https://docs.docker.com/install/) 19.03+
68 | - [Docker Compose](https://docs.docker.com/compose/install/) 1.25+
69 |
70 | ### Build from source
71 |
72 | 1. Clone the repo:
73 | ```
74 | git clone git@github.com:deqode/GoArcc.git
75 | ```
76 | 2. After cloning the repo, review `config.yml` and update for your system as needed
77 | 3. Build to create GoArcc binary
78 | ```
79 | make build
80 | ```
81 | 4. Run unit tests with
82 | ```
83 | make test
84 | ```
85 |
86 |
87 | ## Run
88 | You can find all the running servers at:
89 |
90 | **Jaeger UI:**
91 |
92 | http://localhost:16686
93 |
94 | **Health Trace:**
95 |
96 | http://localhost:8083/health/
97 |
98 | **Prometheus UI:**
99 |
100 | http://localhost:9090
101 |
102 | **Prometheus UI Metrics:**
103 |
104 | http://localhost:9090/metrics
105 |
106 | **Grpc Server:**
107 |
108 | http://localhost:8080
109 |
110 | **Graphql Server:**
111 |
112 | http://localhost:8081
113 |
114 | **Rest Server:**
115 |
116 | http://localhost:8082
117 |
118 |
119 | ## License
120 | ```
121 | Copyright 2021, DeqodeLabs (https://deqode.com/)
122 |
123 | Licensed under the MIT License(the "License");
124 |
125 | ```
126 | 
127 |
--------------------------------------------------------------------------------
/servers/grpc/middleware/auth.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "github.com/justinas/alice"
7 | "go.uber.org/fx"
8 | "google.golang.org/grpc"
9 | "google.golang.org/grpc/codes"
10 | "google.golang.org/grpc/metadata"
11 | "google.golang.org/grpc/status"
12 | "gopkg.in/square/go-jose.v2"
13 | "gopkg.in/square/go-jose.v2/jwt"
14 | "net/http"
15 | "strings"
16 | "time"
17 |
18 | "github.com/deqode/GoArcc/util/userinfo"
19 | )
20 |
21 | const (
22 | id = "id"
23 | email = "email"
24 | )
25 |
26 | type Config struct {
27 | Issuer string
28 | Secret string
29 | Domain string
30 | Audience []string
31 | }
32 |
33 | // NewConfig todo : keep this config into config-local
34 | // todo: Move these base AUth0 URLs to config
35 | func NewConfig() (Config, error) {
36 | newConfig := Config{
37 | Issuer: "https://alfred-sh.us.auth0.com/",
38 | Secret: "secret",
39 | Domain: "https://alfred-sh.us.auth0.com",
40 | Audience: []string{"BFnfdaibKSdqkSAOksr3XuUNJuCW9zbZ"},
41 | }
42 |
43 | return newConfig, nil
44 | }
45 |
46 | func AuthMiddleware(ctx context.Context) (context.Context, error) {
47 | authRequired := true
48 | //getting authentication from the context . ie bearer
49 | md, ok := metadata.FromIncomingContext(ctx)
50 | if !ok {
51 | return nil, status.Error(codes.FailedPrecondition, "authorization failed")
52 | }
53 | //get method name
54 | operationName, _ := grpc.Method(ctx)
55 | //check for public end point
56 | for _, v := range publicEndpoint {
57 | if operationName == v {
58 | authRequired = false
59 | break
60 | }
61 | }
62 |
63 | if authRequired {
64 | token := md.Get("authorization")
65 | if len(token) < 1 {
66 | return nil, status.Error(codes.FailedPrecondition, "Authorization header missing")
67 | }
68 | splitToken := strings.Split(token[0], " ")
69 | if len(splitToken) < 2 {
70 | return nil, status.Error(codes.FailedPrecondition, "Bearer token not found.")
71 | }
72 | jwk, err := NewJwk()
73 | if err != nil {
74 | return nil, err
75 | }
76 | _, err = NewJWtMiddleware(jwk.JSONWebKeySet, splitToken[1])
77 | if err != nil {
78 | return nil, err
79 | }
80 | ctx, err = NewJWTUnaryInterceptor(ctx, jwk.JSONWebKeySet, splitToken[1])
81 | if err != nil {
82 | return nil, err
83 | }
84 | }
85 | return ctx, nil
86 | }
87 |
88 | type DiscoveryResponse struct {
89 | fx.Out
90 | RevocationEndpoint string `name:"revocation_endpoint"`
91 | JSONWebKeySet *jose.JSONWebKeySet
92 | }
93 |
94 | // NewJwk NewJwk() get certificate from openIdConnect
95 | func NewJwk() (DiscoveryResponse, error) {
96 | c, _ := NewConfig()
97 | discoveryURI := strings.TrimSuffix(c.Issuer, "/") + "/.well-known/openid-configuration"
98 |
99 | client := http.Client{
100 | Timeout: 10 * time.Second,
101 | }
102 |
103 | res, err := client.Get(discoveryURI)
104 | if err != nil {
105 | return DiscoveryResponse{}, err
106 | }
107 | defer res.Body.Close()
108 |
109 | var discoResp struct {
110 | JwkURI string `json:"jwks_uri"`
111 | RevocationEndpoint string `json:"revocation_endpoint"`
112 | }
113 |
114 | if err := json.NewDecoder(res.Body).Decode(&discoResp); err != nil {
115 | return DiscoveryResponse{}, err
116 | }
117 |
118 | res, err = client.Get(discoResp.JwkURI)
119 | if err != nil {
120 | return DiscoveryResponse{}, err
121 | }
122 | defer res.Body.Close()
123 |
124 | var keySet jose.JSONWebKeySet
125 | if err := json.NewDecoder(res.Body).Decode(&keySet); err != nil {
126 | return DiscoveryResponse{}, err
127 | }
128 |
129 | return DiscoveryResponse{
130 | RevocationEndpoint: discoResp.RevocationEndpoint,
131 | JSONWebKeySet: &keySet,
132 | }, nil
133 | }
134 |
135 | // NewJWtMiddleware verify the user's token
136 | func NewJWtMiddleware(ks *jose.JSONWebKeySet, token string) (alice.Constructor, error) {
137 | c, _ := NewConfig()
138 | _, err := VerifyToken(token, c.Issuer, c.Audience, ks)
139 | if err != nil {
140 | return nil, status.Error(codes.Unauthenticated, "Unable to verify User")
141 | }
142 | return nil, nil
143 | }
144 |
145 | // NewJWTUnaryInterceptor add users claims to outgoing context
146 | func NewJWTUnaryInterceptor(ctx context.Context, ks *jose.JSONWebKeySet, token string) (context.Context, error) {
147 | c, _ := NewConfig()
148 | claims, err := VerifyToken(token, c.Issuer, c.Audience, ks)
149 | if err != nil {
150 | return nil, status.Error(codes.Unauthenticated, "Unable to verify User")
151 | }
152 |
153 | ui := userinfo.FromClaims(claims)
154 | if ui.ID != "" {
155 | ctx = metadata.AppendToOutgoingContext(ctx, id, ui.ID, email, ui.Email)
156 | }
157 | return userinfo.WithClaims(ctx, claims), nil
158 | }
159 |
160 | func VerifyToken(t, iss string, aud []string, ks *jose.JSONWebKeySet) (map[string]interface{}, error) {
161 | token, err := jwt.ParseSigned(t)
162 | if err != nil {
163 | return nil, err
164 | }
165 |
166 | claims := jwt.Claims{}
167 | claimMap := make(map[string]interface{})
168 | err = token.Claims(ks, &claims, &claimMap)
169 | if err != nil {
170 | return nil, err
171 | }
172 | if err := claims.Validate(jwt.Expected{
173 | Issuer: iss,
174 | Time: time.Now(),
175 | Audience: aud,
176 | }); err != nil {
177 | return nil, err
178 | }
179 |
180 | return claimMap, nil
181 | }
182 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/pb/user_profile_grpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
2 |
3 | package pb
4 |
5 | import (
6 | context "context"
7 | empty "github.com/golang/protobuf/ptypes/empty"
8 | grpc "google.golang.org/grpc"
9 | codes "google.golang.org/grpc/codes"
10 | status "google.golang.org/grpc/status"
11 | )
12 |
13 | // This is a compile-time assertion to ensure that this generated file
14 | // is compatible with the grpc package it is being compiled against.
15 | const _ = grpc.SupportPackageIsVersion7
16 |
17 | // UserProfilesClient is the client API for UserProfiles service.
18 | //
19 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
20 | type UserProfilesClient interface {
21 | // GetUserProfile return a profile of a user
22 | GetUserProfile(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*UserProfile, error)
23 | //GetUserProfileBySub return a user profile by its unique sub provided by vcs
24 | GetUserProfileBySub(ctx context.Context, in *GetUserProfileBySubRequest, opts ...grpc.CallOption) (*UserProfile, error)
25 | }
26 |
27 | type userProfilesClient struct {
28 | cc grpc.ClientConnInterface
29 | }
30 |
31 | func NewUserProfilesClient(cc grpc.ClientConnInterface) UserProfilesClient {
32 | return &userProfilesClient{cc}
33 | }
34 |
35 | func (c *userProfilesClient) GetUserProfile(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*UserProfile, error) {
36 | out := new(UserProfile)
37 | err := c.cc.Invoke(ctx, "/goarcc.user_profile.v1.UserProfiles/GetUserProfile", in, out, opts...)
38 | if err != nil {
39 | return nil, err
40 | }
41 | return out, nil
42 | }
43 |
44 | func (c *userProfilesClient) GetUserProfileBySub(ctx context.Context, in *GetUserProfileBySubRequest, opts ...grpc.CallOption) (*UserProfile, error) {
45 | out := new(UserProfile)
46 | err := c.cc.Invoke(ctx, "/goarcc.user_profile.v1.UserProfiles/GetUserProfileBySub", in, out, opts...)
47 | if err != nil {
48 | return nil, err
49 | }
50 | return out, nil
51 | }
52 |
53 | // UserProfilesServer is the server API for UserProfiles service.
54 | // All implementations should embed UnimplementedUserProfilesServer
55 | // for forward compatibility
56 | type UserProfilesServer interface {
57 | // GetUserProfile return a profile of a user
58 | GetUserProfile(context.Context, *empty.Empty) (*UserProfile, error)
59 | //GetUserProfileBySub return a user profile by its unique sub provided by vcs
60 | GetUserProfileBySub(context.Context, *GetUserProfileBySubRequest) (*UserProfile, error)
61 | }
62 |
63 | // UnimplementedUserProfilesServer should be embedded to have forward compatible implementations.
64 | type UnimplementedUserProfilesServer struct {
65 | }
66 |
67 | func (UnimplementedUserProfilesServer) GetUserProfile(context.Context, *empty.Empty) (*UserProfile, error) {
68 | return nil, status.Errorf(codes.Unimplemented, "method GetUserProfile not implemented")
69 | }
70 | func (UnimplementedUserProfilesServer) GetUserProfileBySub(context.Context, *GetUserProfileBySubRequest) (*UserProfile, error) {
71 | return nil, status.Errorf(codes.Unimplemented, "method GetUserProfileBySub not implemented")
72 | }
73 |
74 | // UnsafeUserProfilesServer may be embedded to opt out of forward compatibility for this service.
75 | // Use of this interface is not recommended, as added methods to UserProfilesServer will
76 | // result in compilation errors.
77 | type UnsafeUserProfilesServer interface {
78 | mustEmbedUnimplementedUserProfilesServer()
79 | }
80 |
81 | func RegisterUserProfilesServer(s *grpc.Server, srv UserProfilesServer) {
82 | s.RegisterService(&_UserProfiles_serviceDesc, srv)
83 | }
84 |
85 | func _UserProfiles_GetUserProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
86 | in := new(empty.Empty)
87 | if err := dec(in); err != nil {
88 | return nil, err
89 | }
90 | if interceptor == nil {
91 | return srv.(UserProfilesServer).GetUserProfile(ctx, in)
92 | }
93 | info := &grpc.UnaryServerInfo{
94 | Server: srv,
95 | FullMethod: "/goarcc.user_profile.v1.UserProfiles/GetUserProfile",
96 | }
97 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
98 | return srv.(UserProfilesServer).GetUserProfile(ctx, req.(*empty.Empty))
99 | }
100 | return interceptor(ctx, in, info, handler)
101 | }
102 |
103 | func _UserProfiles_GetUserProfileBySub_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
104 | in := new(GetUserProfileBySubRequest)
105 | if err := dec(in); err != nil {
106 | return nil, err
107 | }
108 | if interceptor == nil {
109 | return srv.(UserProfilesServer).GetUserProfileBySub(ctx, in)
110 | }
111 | info := &grpc.UnaryServerInfo{
112 | Server: srv,
113 | FullMethod: "/goarcc.user_profile.v1.UserProfiles/GetUserProfileBySub",
114 | }
115 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
116 | return srv.(UserProfilesServer).GetUserProfileBySub(ctx, req.(*GetUserProfileBySubRequest))
117 | }
118 | return interceptor(ctx, in, info, handler)
119 | }
120 |
121 | var _UserProfiles_serviceDesc = grpc.ServiceDesc{
122 | ServiceName: "goarcc.user_profile.v1.UserProfiles",
123 | HandlerType: (*UserProfilesServer)(nil),
124 | Methods: []grpc.MethodDesc{
125 | {
126 | MethodName: "GetUserProfile",
127 | Handler: _UserProfiles_GetUserProfile_Handler,
128 | },
129 | {
130 | MethodName: "GetUserProfileBySub",
131 | Handler: _UserProfiles_GetUserProfileBySub_Handler,
132 | },
133 | },
134 | Streams: []grpc.StreamDesc{},
135 | Metadata: "pb/user_profile.proto",
136 | }
137 |
--------------------------------------------------------------------------------
/protos/include/graphql.proto:
--------------------------------------------------------------------------------
1 | // graphql.proto
2 | //
3 | // Copyright (c) 2020 ysugimoto
4 | //
5 | // Released under the MIT license.
6 | // see https://opensource.org/licenses/MIT
7 | syntax = "proto3";
8 |
9 | package graphql;
10 |
11 | option go_package = "github.com/ysugimoto/grpc-graphql-gateway/graphql";
12 |
13 | import "google/protobuf/descriptor.proto";
14 |
15 | // Extend ServiceOptions in order to define grpc connection setting.
16 | // User can use this option as following:
17 | //
18 | // service Greeter {
19 | // option (graphql.service) = {
20 | // host: "localhost:50051" // define grpc connection host and port
21 | // insecure: true // set true if connect to insecure grpc server
22 | // };
23 | //
24 | // ... some rpc definitions
25 | // }
26 | message GraphqlService {
27 | // gRPC default connection host.
28 | // This value should include host and port, say localhost:50051.
29 | string host = 1;
30 | // If true, automatic connection with insecure option.
31 | bool insecure = 2;
32 | }
33 |
34 |
35 | // Extend MethodOptions in order to define GraphQL Query or Mutation.
36 | // User can use this option as following:
37 | //
38 | // service Greeter {
39 | // rpc SayHello(HelloRequest) returns (HelloReply) {
40 | // option (graphql.schema) = {
41 | // type: QUERY // declare as Query
42 | // name: "hello" // query name
43 | // }
44 | // }
45 | // }
46 | //
47 | // Since gRPC reason, it has limitation that the response could not be repeated.
48 | // it's dificcurl to respond array response, so that we accept "response.pluck"
49 | // in order to expose repeated fields in response message.
50 | //
51 | // For instance:
52 | //
53 | // message Member {
54 | // string name = 1;
55 | // }
56 | //
57 | // message ListMembersResponse {
58 | // repeated Member members = 1; -- could be array response
59 | // }
60 | //
61 | // message ListMembersRequest {
62 | // }
63 | //
64 | // service MemberService {
65 | // rpc ListMembers(ListMembersRequest) returns (ListMembersResponse) {
66 | // option (graphql.schema) = {
67 | // type: QUERY
68 | // name: "members"
69 | // response {
70 | // repeated : true
71 | // pluck: "members" // Query will respond [Member] instead of ListMembersResponse
72 | // }
73 | // }
74 | // }
75 | // }
76 | //
77 | // In mutation declaration:
78 | //
79 | // service MemberService {
80 | // rpc CreateMember(CreateMemberRequest) returns (Member) {
81 | // option (graphql.schema) = {
82 | // type: MUTATION // declare as Mutation
83 | // name: "cretemember" // mutation name
84 | // }
85 | // }
86 | // }
87 | //
88 | // The Mutation's input always becomes an input object, so you need to declare argument name.
89 | //
90 | // message Member {
91 | // string name = 1;
92 | // }
93 | //
94 | // message CreateMemberRequest {
95 | // string name = 1;
96 | // }
97 | //
98 | // service MemberService {
99 | // rpc CreateMember(CreateMemberRequest) returns (Member) {
100 | // option (graphql.schema) = {
101 | // type: MUTATION
102 | // name: "createmember"
103 | // request {
104 | // name: "member" // this is equivalent to createbook(member: Member): Member in GraphQL
105 | // }
106 | // }
107 | // }
108 | // }
109 | //
110 | // Finally, user can access this query via /graphql?query={members{name}}
111 | message GraphqlSchema {
112 | // graphql type. Enum of QUERY or MUTATION is valid value
113 | GraphqlType type = 1;
114 | // query name. this field is required
115 | string name = 2;
116 | // Query request object configuration
117 | GraphqlRequest request = 3;
118 | // Query response object configuration
119 | GraphqlResponse response = 4;
120 | }
121 |
122 | // configuration option for request
123 | message GraphqlRequest {
124 | // Define input name.
125 | // This field enables only for mutation and note that if this field is specified,
126 | // the gRPC request message will be dealt with an input.
127 | string name = 1;
128 |
129 | // Define pluck message fields
130 | repeated string plucks = 2;
131 | }
132 |
133 | // configuration option for response
134 | message GraphqlResponse {
135 | // If true, this response object is required
136 | // But when you declare "pluck", we respect expose field definition.
137 | bool required = 1;
138 |
139 | // Define pluck message field.
140 | // Note that this field IS NOT repeated, just single string field.
141 | // It means the response could only be single.
142 | string pluck = 2;
143 | }
144 |
145 | // explicit schema declaration enum
146 | enum GraphqlType {
147 | // schema will generate as Query
148 | QUERY = 0;
149 | // schema will generate as Mutation
150 | MUTATION = 1;
151 | // schema will generate as Resolver. Resolver behaves not listed in query, but can resolve nested field.
152 | RESOLVER = 2;
153 | }
154 |
155 | // GraphqlField is FieldOptions in protobuf in order to define type field attribute.
156 | // User can use this option as following:
157 | //
158 | // message Member {
159 | // string name = 1 [(graphql.field) = {required: true}]; // this field is required in GraphQL, it equivalent to String! on GraphQL
160 | // }
161 | //
162 | // message CreateMemberRequest {
163 | // string name = 1; [(grahpql.field) = {default: "anonymous"}]; // use default value on input or query
164 | // }
165 | //
166 | // Note that in protobuf, all fields are dealt with optional
167 | // so the same as it, all GraphQL fields are optional as default.
168 | // If you need to be required, use 'required: true' option
169 | message GraphqlField {
170 | // If true, this field is required.
171 | bool required = 1;
172 | // Use as other field name (not recommend)
173 | string name = 2;
174 | // Define default value on input.
175 | string default = 3;
176 | // Omit this field from graphql definition
177 | bool omit = 4;
178 | // Resolve this field by nested query with additional RPC
179 | string resolver = 5;
180 | }
181 |
182 | // Extend builtin messages
183 |
184 | extend google.protobuf.ServiceOptions {
185 | GraphqlService service = 1079;
186 | }
187 |
188 | extend google.protobuf.FieldOptions {
189 | GraphqlField field = 1079;
190 | }
191 |
192 | extend google.protobuf.MethodOptions {
193 | GraphqlSchema schema = 1079;
194 | }
195 |
--------------------------------------------------------------------------------
/modules/authentication/v1/pb/pb.graphql.go:
--------------------------------------------------------------------------------
1 | // Code generated by proroc-gen-graphql, DO NOT EDIT.
2 | package pb
3 |
4 | import (
5 | "github.com/graphql-go/graphql"
6 | )
7 |
8 | var (
9 | gql__type_ValidateUserLoginResponse *graphql.Object // message ValidateUserLoginResponse in pb/authentication.proto
10 | gql__type_ValidateUserLoginRequest *graphql.Object // message ValidateUserLoginRequest in pb/authentication.proto
11 | gql__type_LoginResponse *graphql.Object // message LoginResponse in pb/authentication.proto
12 | gql__type_LoginCallbackResponse *graphql.Object // message LoginCallbackResponse in pb/authentication.proto
13 | gql__type_LoginCallbackRequest *graphql.Object // message LoginCallbackRequest in pb/authentication.proto
14 | gql__input_ValidateUserLoginResponse *graphql.InputObject // message ValidateUserLoginResponse in pb/authentication.proto
15 | gql__input_ValidateUserLoginRequest *graphql.InputObject // message ValidateUserLoginRequest in pb/authentication.proto
16 | gql__input_LoginResponse *graphql.InputObject // message LoginResponse in pb/authentication.proto
17 | gql__input_LoginCallbackResponse *graphql.InputObject // message LoginCallbackResponse in pb/authentication.proto
18 | gql__input_LoginCallbackRequest *graphql.InputObject // message LoginCallbackRequest in pb/authentication.proto
19 | )
20 |
21 | func Gql__type_ValidateUserLoginResponse() *graphql.Object {
22 | if gql__type_ValidateUserLoginResponse == nil {
23 | gql__type_ValidateUserLoginResponse = graphql.NewObject(graphql.ObjectConfig{
24 | Name: "Pb_Type_ValidateUserLoginResponse",
25 | Fields: graphql.Fields{
26 | "id": &graphql.Field{
27 | Type: graphql.String,
28 | Description: `Id is the user Unique identifier`,
29 | },
30 | },
31 | })
32 | }
33 | return gql__type_ValidateUserLoginResponse
34 | }
35 |
36 | func Gql__type_ValidateUserLoginRequest() *graphql.Object {
37 | if gql__type_ValidateUserLoginRequest == nil {
38 | gql__type_ValidateUserLoginRequest = graphql.NewObject(graphql.ObjectConfig{
39 | Name: "Pb_Type_ValidateUserLoginRequest",
40 | Description: `ValidateUserLoginRequest contains user login credential.`,
41 | Fields: graphql.Fields{
42 | "id": &graphql.Field{
43 | Type: graphql.String,
44 | Description: `Id is the user Unique identifier`,
45 | },
46 | "password": &graphql.Field{
47 | Type: graphql.String,
48 | Description: `Password is the user password`,
49 | },
50 | },
51 | })
52 | }
53 | return gql__type_ValidateUserLoginRequest
54 | }
55 |
56 | func Gql__type_LoginResponse() *graphql.Object {
57 | if gql__type_LoginResponse == nil {
58 | gql__type_LoginResponse = graphql.NewObject(graphql.ObjectConfig{
59 | Name: "Pb_Type_LoginResponse",
60 | Fields: graphql.Fields{
61 | "url": &graphql.Field{
62 | Type: graphql.String,
63 | },
64 | },
65 | })
66 | }
67 | return gql__type_LoginResponse
68 | }
69 |
70 | func Gql__type_LoginCallbackResponse() *graphql.Object {
71 | if gql__type_LoginCallbackResponse == nil {
72 | gql__type_LoginCallbackResponse = graphql.NewObject(graphql.ObjectConfig{
73 | Name: "Pb_Type_LoginCallbackResponse",
74 | Fields: graphql.Fields{
75 | "id_token": &graphql.Field{
76 | Type: graphql.String,
77 | },
78 | "access_token": &graphql.Field{
79 | Type: graphql.String,
80 | },
81 | "user_id": &graphql.Field{
82 | Type: graphql.String,
83 | },
84 | },
85 | })
86 | }
87 | return gql__type_LoginCallbackResponse
88 | }
89 |
90 | func Gql__type_LoginCallbackRequest() *graphql.Object {
91 | if gql__type_LoginCallbackRequest == nil {
92 | gql__type_LoginCallbackRequest = graphql.NewObject(graphql.ObjectConfig{
93 | Name: "Pb_Type_LoginCallbackRequest",
94 | Fields: graphql.Fields{
95 | "state": &graphql.Field{
96 | Type: graphql.String,
97 | },
98 | "code": &graphql.Field{
99 | Type: graphql.String,
100 | },
101 | },
102 | })
103 | }
104 | return gql__type_LoginCallbackRequest
105 | }
106 |
107 | func Gql__input_ValidateUserLoginResponse() *graphql.InputObject {
108 | if gql__input_ValidateUserLoginResponse == nil {
109 | gql__input_ValidateUserLoginResponse = graphql.NewInputObject(graphql.InputObjectConfig{
110 | Name: "Pb_Input_ValidateUserLoginResponse",
111 | Fields: graphql.InputObjectConfigFieldMap{
112 | "id": &graphql.InputObjectFieldConfig{
113 | Description: `Id is the user Unique identifier`,
114 | Type: graphql.String,
115 | },
116 | },
117 | })
118 | }
119 | return gql__input_ValidateUserLoginResponse
120 | }
121 |
122 | func Gql__input_ValidateUserLoginRequest() *graphql.InputObject {
123 | if gql__input_ValidateUserLoginRequest == nil {
124 | gql__input_ValidateUserLoginRequest = graphql.NewInputObject(graphql.InputObjectConfig{
125 | Name: "Pb_Input_ValidateUserLoginRequest",
126 | Fields: graphql.InputObjectConfigFieldMap{
127 | "id": &graphql.InputObjectFieldConfig{
128 | Description: `Id is the user Unique identifier`,
129 | Type: graphql.String,
130 | },
131 | "password": &graphql.InputObjectFieldConfig{
132 | Description: `Password is the user password`,
133 | Type: graphql.String,
134 | },
135 | },
136 | })
137 | }
138 | return gql__input_ValidateUserLoginRequest
139 | }
140 |
141 | func Gql__input_LoginResponse() *graphql.InputObject {
142 | if gql__input_LoginResponse == nil {
143 | gql__input_LoginResponse = graphql.NewInputObject(graphql.InputObjectConfig{
144 | Name: "Pb_Input_LoginResponse",
145 | Fields: graphql.InputObjectConfigFieldMap{
146 | "url": &graphql.InputObjectFieldConfig{
147 | Type: graphql.String,
148 | },
149 | },
150 | })
151 | }
152 | return gql__input_LoginResponse
153 | }
154 |
155 | func Gql__input_LoginCallbackResponse() *graphql.InputObject {
156 | if gql__input_LoginCallbackResponse == nil {
157 | gql__input_LoginCallbackResponse = graphql.NewInputObject(graphql.InputObjectConfig{
158 | Name: "Pb_Input_LoginCallbackResponse",
159 | Fields: graphql.InputObjectConfigFieldMap{
160 | "id_token": &graphql.InputObjectFieldConfig{
161 | Type: graphql.String,
162 | },
163 | "access_token": &graphql.InputObjectFieldConfig{
164 | Type: graphql.String,
165 | },
166 | "user_id": &graphql.InputObjectFieldConfig{
167 | Type: graphql.String,
168 | },
169 | },
170 | })
171 | }
172 | return gql__input_LoginCallbackResponse
173 | }
174 |
175 | func Gql__input_LoginCallbackRequest() *graphql.InputObject {
176 | if gql__input_LoginCallbackRequest == nil {
177 | gql__input_LoginCallbackRequest = graphql.NewInputObject(graphql.InputObjectConfig{
178 | Name: "Pb_Input_LoginCallbackRequest",
179 | Fields: graphql.InputObjectConfigFieldMap{
180 | "state": &graphql.InputObjectFieldConfig{
181 | Type: graphql.String,
182 | },
183 | "code": &graphql.InputObjectFieldConfig{
184 | Type: graphql.String,
185 | },
186 | },
187 | })
188 | }
189 | return gql__input_LoginCallbackRequest
190 | }
191 |
--------------------------------------------------------------------------------
/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/deqode/GoArcc/logger"
5 | "github.com/spf13/viper"
6 | "go.uber.org/zap"
7 | "os"
8 | "path"
9 | "path/filepath"
10 | "runtime"
11 | "strings"
12 | )
13 |
14 | type FileInformation struct {
15 | Path string
16 | Name string
17 | }
18 |
19 | //Config : including all the configuration
20 | type Config struct {
21 | Grpc GrpcServerConfig `mapstructure:"GRPC"`
22 | Graphql GraphqlServerConfig `mapstructure:"GRAPHQL"`
23 | Rest RestServerConfig `mapstructure:"REST"`
24 | HealthCheck HealthCheckServerConfig `mapstructure:"HEALTH_CHECK"`
25 | Logger LoggerConfig `mapstructure:"LOGGER"`
26 | Postgres PostgresConfig `mapstructure:"POSTGRES"`
27 | Metrics MetricsConfig `mapstructure:"METRICS"`
28 | Jaeger JaegerServerConfig `mapstructure:"JAEGER"`
29 | Auth AuthConfig `mapstructure:"AUTH"`
30 | GithubVCSConfig VCSSConfig `mapstructure:"GITHUB_VCS_CONFIG"`
31 | CadenceConfig CadenceConfig `mapstructure:"CADENCE_CONFIG"`
32 | SupportedVcsConfig []string
33 | }
34 |
35 | type CadenceConfig struct {
36 | Domain string `mapstructure:"DOMAIN"`
37 | Service string `mapstructure:"SERVICE"`
38 | Port string `mapstructure:"PORT"`
39 | Host string `mapstructure:"HOST"`
40 | }
41 |
42 | // GrpcServerConfig GrpcServerConfig: gRPC server configuration
43 | // Timeout is the request timeout:
44 | // any client request take longer then the given timeout will automatically cancelled.
45 | type GrpcServerConfig struct {
46 | Port string `mapstructure:"PORT"`
47 | Host string `mapstructure:"HOST"`
48 | RequestTimeout int `mapstructure:"REQUEST_TIMEOUT"`
49 | }
50 |
51 | // GraphqlServerConfig GraphqlServerConfig: Graphql server configuration
52 | type GraphqlServerConfig struct {
53 | Port string `mapstructure:"PORT"`
54 | Host string `mapstructure:"HOST"`
55 | RequestTimeout int `mapstructure:"REQUEST_TIMEOUT"`
56 | }
57 |
58 | // RestServerConfig RestServerConfig: Rest Implementation config
59 | type RestServerConfig struct {
60 | Port string `mapstructure:"PORT"`
61 | Host string `mapstructure:"HOST"`
62 | RequestTimeout int `mapstructure:"REQUEST_TIMEOUT"`
63 | }
64 |
65 | // HealthCheckServerConfig HealthCheckServerConfig: Configuration about health check
66 | type HealthCheckServerConfig struct {
67 | Port string `mapstructure:"PORT"`
68 | Host string `mapstructure:"HOST"`
69 | }
70 |
71 | // LoggerConfig LoggerConfig: Zapier log level
72 | type LoggerConfig struct {
73 | LogLevel string `mapstructure:"LOG_LEVEL"`
74 | }
75 |
76 | // PostgresConfig PostgresConfig: detail config about the postgres database
77 | type PostgresConfig struct {
78 | Host string `mapstructure:"HOST"`
79 | Port string `mapstructure:"PORT"`
80 | User string `mapstructure:"USER"`
81 | Password string `mapstructure:"PASSWORD"`
82 | DbName string `mapstructure:"DB_NAME"`
83 | SslMode string `mapstructure:"SSL_MODE"`
84 | Driver string `mapstructure:"PG_DRIVER"`
85 | }
86 |
87 | //MetricsConfig : detail config about the Metrics
88 | type MetricsConfig struct {
89 | URL string `mapstructure:"URL"`
90 | ServiceName string `mapstructure:"SERVICE_NAME"`
91 | }
92 |
93 | //JaegerServerConfig : detail config about the Jaeger
94 | type JaegerServerConfig struct {
95 | Host string `mapstructure:"HOST"`
96 | Port string `mapstructure:"PORT"`
97 | ServiceName string `mapstructure:"SERVICE_NAME"`
98 | LogSpans string `mapstructure:"LOG_SPANS"`
99 | }
100 |
101 | // AuthConfig Authentication config: details provided by Auth0
102 | type AuthConfig struct {
103 | Auth0ClientID string `mapstructure:"AUTH0_CLIENT_ID"`
104 | Auth0Domain string `mapstructure:"AUTH0_DOMAIN"`
105 | Auth0ClientSecret string `mapstructure:"AUTH0_CLIENT_SECRET"`
106 | Auth0CallBackURL string `mapstructure:"AUTH0_CALL_BACK_URL"`
107 | }
108 |
109 | //VCSSConfig like github,gitlab,bitbucket config
110 | type VCSSConfig struct {
111 | IType int `mapstructure:"ITYPE"`
112 | Provider string `mapstructure:"PROVIDER"`
113 | URLTemplate string `mapstructure:"URL_TEMPLATE"`
114 | ClientID string `mapstructure:"CLIENT_ID"`
115 | RedirectURI string `mapstructure:"REDIRECT_URI"`
116 | State string `mapstructure:"STATE"`
117 | Scope string `mapstructure:"SCOPE"`
118 | ResponseType string `mapstructure:"RESPONSE_TYPE"`
119 | ClientSecret string `mapstructure:"CLIENT_SECRET"`
120 | Name string `mapstructure:"NAME"`
121 | }
122 |
123 | //GetVcsConfig : will give the particular vcs config
124 | func GetVcsConfig(name string, vcsConfig []VCSSConfig) *VCSSConfig {
125 | for _, v := range vcsConfig {
126 | if v.Name == name {
127 | return &v
128 | }
129 | }
130 | return nil
131 | }
132 |
133 | // LoadConfig config file from given path
134 | func LoadConfig(filename, path string) (*viper.Viper, error) {
135 | v := viper.New()
136 | v.AddConfigPath(path)
137 | v.SetConfigName(filename)
138 | v.AutomaticEnv()
139 | v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
140 | if err := v.ReadInConfig(); err != nil {
141 | return nil, err
142 | }
143 |
144 | return v, nil
145 | }
146 |
147 | // ParseConfig file from the given viper
148 | func ParseConfig(v *viper.Viper) (*Config, error) {
149 | var c Config
150 | err := v.Unmarshal(&c)
151 | if err != nil {
152 | logger.Log.Fatal("unable to decode into struct", zap.Error(err))
153 | return nil, err
154 | }
155 | return &c, nil
156 | }
157 |
158 | // GetConfigName get the path from local or docker
159 | func GetConfigName() string {
160 | fileName := os.Getenv("CONFIG_NAME")
161 | if fileName != "" {
162 | return fileName
163 | }
164 | return "config"
165 | }
166 |
167 | func GetConfigDirectory() string {
168 | filePath := os.Getenv("CONFIG_DIRECTORY")
169 | if filePath != "" {
170 | return filePath
171 | }
172 | return RootDir()
173 | }
174 | func RootDir() string {
175 | _, b, _, _ := runtime.Caller(0)
176 | d := path.Join(path.Dir(b))
177 | return filepath.Dir(d)
178 | }
179 |
180 | // GetConfig : will get the config
181 | func GetConfig() *Config {
182 | configFileName := GetConfigName()
183 | configFileDirectory := GetConfigDirectory()
184 | logger.Log.Info("Config Details", zap.String("configFileDirectory", configFileDirectory), zap.String("configFileName", configFileName))
185 |
186 | cfgFile, configFileLoadError := LoadConfig(configFileName, configFileDirectory)
187 | if configFileLoadError != nil {
188 | logger.Log.Fatal("unable to get config", zap.Error(configFileLoadError))
189 | panic(configFileLoadError)
190 | }
191 |
192 | cfg, parseError := ParseConfig(cfgFile)
193 | if parseError != nil {
194 | logger.Log.Fatal("unable to get config", zap.Error(parseError))
195 | panic(parseError)
196 | }
197 |
198 | cfg.SupportedVcsConfig = supportedVcsConfig()
199 | return cfg
200 | }
201 |
202 | // SupportedVcsConfig add supported type from here.
203 | func supportedVcsConfig() []string {
204 | return []string{"github"}
205 | }
206 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/pb/user_profile_int_grpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
2 |
3 | package pb
4 |
5 | import (
6 | context "context"
7 | empty "github.com/golang/protobuf/ptypes/empty"
8 | grpc "google.golang.org/grpc"
9 | codes "google.golang.org/grpc/codes"
10 | status "google.golang.org/grpc/status"
11 | )
12 |
13 | // This is a compile-time assertion to ensure that this generated file
14 | // is compatible with the grpc package it is being compiled against.
15 | const _ = grpc.SupportPackageIsVersion7
16 |
17 | // UserProfileInternalClient is the client API for UserProfileInternal service.
18 | //
19 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
20 | type UserProfileInternalClient interface {
21 | //CreateUserProfile creates a user profile by external oAuth
22 | CreateUserProfile(ctx context.Context, in *CreateUserProfileRequest, opts ...grpc.CallOption) (*UserProfile, error)
23 | // CreateUserProfile will update userprofile
24 | UpdateUserProfile(ctx context.Context, in *UpdateUserProfileRequest, opts ...grpc.CallOption) (*UserProfile, error)
25 | // DeleteUserProfile delete the user
26 | DeleteUserProfile(ctx context.Context, in *DeleteUserProfileRequest, opts ...grpc.CallOption) (*empty.Empty, error)
27 | }
28 |
29 | type userProfileInternalClient struct {
30 | cc grpc.ClientConnInterface
31 | }
32 |
33 | func NewUserProfileInternalClient(cc grpc.ClientConnInterface) UserProfileInternalClient {
34 | return &userProfileInternalClient{cc}
35 | }
36 |
37 | func (c *userProfileInternalClient) CreateUserProfile(ctx context.Context, in *CreateUserProfileRequest, opts ...grpc.CallOption) (*UserProfile, error) {
38 | out := new(UserProfile)
39 | err := c.cc.Invoke(ctx, "/goarcc.user_profile_internal.v1.UserProfileInternal/CreateUserProfile", in, out, opts...)
40 | if err != nil {
41 | return nil, err
42 | }
43 | return out, nil
44 | }
45 |
46 | func (c *userProfileInternalClient) UpdateUserProfile(ctx context.Context, in *UpdateUserProfileRequest, opts ...grpc.CallOption) (*UserProfile, error) {
47 | out := new(UserProfile)
48 | err := c.cc.Invoke(ctx, "/goarcc.user_profile_internal.v1.UserProfileInternal/UpdateUserProfile", in, out, opts...)
49 | if err != nil {
50 | return nil, err
51 | }
52 | return out, nil
53 | }
54 |
55 | func (c *userProfileInternalClient) DeleteUserProfile(ctx context.Context, in *DeleteUserProfileRequest, opts ...grpc.CallOption) (*empty.Empty, error) {
56 | out := new(empty.Empty)
57 | err := c.cc.Invoke(ctx, "/goarcc.user_profile_internal.v1.UserProfileInternal/DeleteUserProfile", in, out, opts...)
58 | if err != nil {
59 | return nil, err
60 | }
61 | return out, nil
62 | }
63 |
64 | // UserProfileInternalServer is the server API for UserProfileInternal service.
65 | // All implementations should embed UnimplementedUserProfileInternalServer
66 | // for forward compatibility
67 | type UserProfileInternalServer interface {
68 | //CreateUserProfile creates a user profile by external oAuth
69 | CreateUserProfile(context.Context, *CreateUserProfileRequest) (*UserProfile, error)
70 | // CreateUserProfile will update userprofile
71 | UpdateUserProfile(context.Context, *UpdateUserProfileRequest) (*UserProfile, error)
72 | // DeleteUserProfile delete the user
73 | DeleteUserProfile(context.Context, *DeleteUserProfileRequest) (*empty.Empty, error)
74 | }
75 |
76 | // UnimplementedUserProfileInternalServer should be embedded to have forward compatible implementations.
77 | type UnimplementedUserProfileInternalServer struct {
78 | }
79 |
80 | func (UnimplementedUserProfileInternalServer) CreateUserProfile(context.Context, *CreateUserProfileRequest) (*UserProfile, error) {
81 | return nil, status.Errorf(codes.Unimplemented, "method CreateUserProfile not implemented")
82 | }
83 | func (UnimplementedUserProfileInternalServer) UpdateUserProfile(context.Context, *UpdateUserProfileRequest) (*UserProfile, error) {
84 | return nil, status.Errorf(codes.Unimplemented, "method UpdateUserProfile not implemented")
85 | }
86 | func (UnimplementedUserProfileInternalServer) DeleteUserProfile(context.Context, *DeleteUserProfileRequest) (*empty.Empty, error) {
87 | return nil, status.Errorf(codes.Unimplemented, "method DeleteUserProfile not implemented")
88 | }
89 |
90 | // UnsafeUserProfileInternalServer may be embedded to opt out of forward compatibility for this service.
91 | // Use of this interface is not recommended, as added methods to UserProfileInternalServer will
92 | // result in compilation errors.
93 | type UnsafeUserProfileInternalServer interface {
94 | mustEmbedUnimplementedUserProfileInternalServer()
95 | }
96 |
97 | func RegisterUserProfileInternalServer(s *grpc.Server, srv UserProfileInternalServer) {
98 | s.RegisterService(&_UserProfileInternal_serviceDesc, srv)
99 | }
100 |
101 | func _UserProfileInternal_CreateUserProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
102 | in := new(CreateUserProfileRequest)
103 | if err := dec(in); err != nil {
104 | return nil, err
105 | }
106 | if interceptor == nil {
107 | return srv.(UserProfileInternalServer).CreateUserProfile(ctx, in)
108 | }
109 | info := &grpc.UnaryServerInfo{
110 | Server: srv,
111 | FullMethod: "/goarcc.user_profile_internal.v1.UserProfileInternal/CreateUserProfile",
112 | }
113 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
114 | return srv.(UserProfileInternalServer).CreateUserProfile(ctx, req.(*CreateUserProfileRequest))
115 | }
116 | return interceptor(ctx, in, info, handler)
117 | }
118 |
119 | func _UserProfileInternal_UpdateUserProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
120 | in := new(UpdateUserProfileRequest)
121 | if err := dec(in); err != nil {
122 | return nil, err
123 | }
124 | if interceptor == nil {
125 | return srv.(UserProfileInternalServer).UpdateUserProfile(ctx, in)
126 | }
127 | info := &grpc.UnaryServerInfo{
128 | Server: srv,
129 | FullMethod: "/goarcc.user_profile_internal.v1.UserProfileInternal/UpdateUserProfile",
130 | }
131 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
132 | return srv.(UserProfileInternalServer).UpdateUserProfile(ctx, req.(*UpdateUserProfileRequest))
133 | }
134 | return interceptor(ctx, in, info, handler)
135 | }
136 |
137 | func _UserProfileInternal_DeleteUserProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
138 | in := new(DeleteUserProfileRequest)
139 | if err := dec(in); err != nil {
140 | return nil, err
141 | }
142 | if interceptor == nil {
143 | return srv.(UserProfileInternalServer).DeleteUserProfile(ctx, in)
144 | }
145 | info := &grpc.UnaryServerInfo{
146 | Server: srv,
147 | FullMethod: "/goarcc.user_profile_internal.v1.UserProfileInternal/DeleteUserProfile",
148 | }
149 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
150 | return srv.(UserProfileInternalServer).DeleteUserProfile(ctx, req.(*DeleteUserProfileRequest))
151 | }
152 | return interceptor(ctx, in, info, handler)
153 | }
154 |
155 | var _UserProfileInternal_serviceDesc = grpc.ServiceDesc{
156 | ServiceName: "goarcc.user_profile_internal.v1.UserProfileInternal",
157 | HandlerType: (*UserProfileInternalServer)(nil),
158 | Methods: []grpc.MethodDesc{
159 | {
160 | MethodName: "CreateUserProfile",
161 | Handler: _UserProfileInternal_CreateUserProfile_Handler,
162 | },
163 | {
164 | MethodName: "UpdateUserProfile",
165 | Handler: _UserProfileInternal_UpdateUserProfile_Handler,
166 | },
167 | {
168 | MethodName: "DeleteUserProfile",
169 | Handler: _UserProfileInternal_DeleteUserProfile_Handler,
170 | },
171 | },
172 | Streams: []grpc.StreamDesc{},
173 | Metadata: "pb/user_profile_int.proto",
174 | }
175 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/pb/user_profile_int.pb.validate.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-validate. DO NOT EDIT.
2 | // source: pb/user_profile_int.proto
3 |
4 | package pb
5 |
6 | import (
7 | "bytes"
8 | "errors"
9 | "fmt"
10 | "net"
11 | "net/mail"
12 | "net/url"
13 | "regexp"
14 | "strings"
15 | "time"
16 | "unicode/utf8"
17 |
18 | "google.golang.org/protobuf/types/known/anypb"
19 | )
20 |
21 | // ensure the imports are used
22 | var (
23 | _ = bytes.MinRead
24 | _ = errors.New("")
25 | _ = fmt.Print
26 | _ = utf8.UTFMax
27 | _ = (*regexp.Regexp)(nil)
28 | _ = (*strings.Reader)(nil)
29 | _ = net.IPv4len
30 | _ = time.Duration(0)
31 | _ = (*url.URL)(nil)
32 | _ = (*mail.Address)(nil)
33 | _ = anypb.Any{}
34 | )
35 |
36 | // Validate checks the field values on CreateUserProfileRequest with the rules
37 | // defined in the proto definition for this message. If any rules are
38 | // violated, an error is returned.
39 | func (m *CreateUserProfileRequest) Validate() error {
40 | if m == nil {
41 | return nil
42 | }
43 |
44 | if v, ok := interface{}(m.GetUserProfile()).(interface{ Validate() error }); ok {
45 | if err := v.Validate(); err != nil {
46 | return CreateUserProfileRequestValidationError{
47 | field: "UserProfile",
48 | reason: "embedded message failed validation",
49 | cause: err,
50 | }
51 | }
52 | }
53 |
54 | return nil
55 | }
56 |
57 | // CreateUserProfileRequestValidationError is the validation error returned by
58 | // CreateUserProfileRequest.Validate if the designated constraints aren't met.
59 | type CreateUserProfileRequestValidationError struct {
60 | field string
61 | reason string
62 | cause error
63 | key bool
64 | }
65 |
66 | // Field function returns field value.
67 | func (e CreateUserProfileRequestValidationError) Field() string { return e.field }
68 |
69 | // Reason function returns reason value.
70 | func (e CreateUserProfileRequestValidationError) Reason() string { return e.reason }
71 |
72 | // Cause function returns cause value.
73 | func (e CreateUserProfileRequestValidationError) Cause() error { return e.cause }
74 |
75 | // Key function returns key value.
76 | func (e CreateUserProfileRequestValidationError) Key() bool { return e.key }
77 |
78 | // ErrorName returns error name.
79 | func (e CreateUserProfileRequestValidationError) ErrorName() string {
80 | return "CreateUserProfileRequestValidationError"
81 | }
82 |
83 | // Error satisfies the builtin error interface
84 | func (e CreateUserProfileRequestValidationError) Error() string {
85 | cause := ""
86 | if e.cause != nil {
87 | cause = fmt.Sprintf(" | caused by: %v", e.cause)
88 | }
89 |
90 | key := ""
91 | if e.key {
92 | key = "key for "
93 | }
94 |
95 | return fmt.Sprintf(
96 | "invalid %sCreateUserProfileRequest.%s: %s%s",
97 | key,
98 | e.field,
99 | e.reason,
100 | cause)
101 | }
102 |
103 | var _ error = CreateUserProfileRequestValidationError{}
104 |
105 | var _ interface {
106 | Field() string
107 | Reason() string
108 | Key() bool
109 | Cause() error
110 | ErrorName() string
111 | } = CreateUserProfileRequestValidationError{}
112 |
113 | // Validate checks the field values on DeleteUserProfileRequest with the rules
114 | // defined in the proto definition for this message. If any rules are
115 | // violated, an error is returned.
116 | func (m *DeleteUserProfileRequest) Validate() error {
117 | if m == nil {
118 | return nil
119 | }
120 |
121 | if utf8.RuneCountInString(m.GetId()) < 3 {
122 | return DeleteUserProfileRequestValidationError{
123 | field: "Id",
124 | reason: "value length must be at least 3 runes",
125 | }
126 | }
127 |
128 | return nil
129 | }
130 |
131 | // DeleteUserProfileRequestValidationError is the validation error returned by
132 | // DeleteUserProfileRequest.Validate if the designated constraints aren't met.
133 | type DeleteUserProfileRequestValidationError struct {
134 | field string
135 | reason string
136 | cause error
137 | key bool
138 | }
139 |
140 | // Field function returns field value.
141 | func (e DeleteUserProfileRequestValidationError) Field() string { return e.field }
142 |
143 | // Reason function returns reason value.
144 | func (e DeleteUserProfileRequestValidationError) Reason() string { return e.reason }
145 |
146 | // Cause function returns cause value.
147 | func (e DeleteUserProfileRequestValidationError) Cause() error { return e.cause }
148 |
149 | // Key function returns key value.
150 | func (e DeleteUserProfileRequestValidationError) Key() bool { return e.key }
151 |
152 | // ErrorName returns error name.
153 | func (e DeleteUserProfileRequestValidationError) ErrorName() string {
154 | return "DeleteUserProfileRequestValidationError"
155 | }
156 |
157 | // Error satisfies the builtin error interface
158 | func (e DeleteUserProfileRequestValidationError) Error() string {
159 | cause := ""
160 | if e.cause != nil {
161 | cause = fmt.Sprintf(" | caused by: %v", e.cause)
162 | }
163 |
164 | key := ""
165 | if e.key {
166 | key = "key for "
167 | }
168 |
169 | return fmt.Sprintf(
170 | "invalid %sDeleteUserProfileRequest.%s: %s%s",
171 | key,
172 | e.field,
173 | e.reason,
174 | cause)
175 | }
176 |
177 | var _ error = DeleteUserProfileRequestValidationError{}
178 |
179 | var _ interface {
180 | Field() string
181 | Reason() string
182 | Key() bool
183 | Cause() error
184 | ErrorName() string
185 | } = DeleteUserProfileRequestValidationError{}
186 |
187 | // Validate checks the field values on UpdateUserProfileRequest with the rules
188 | // defined in the proto definition for this message. If any rules are
189 | // violated, an error is returned.
190 | func (m *UpdateUserProfileRequest) Validate() error {
191 | if m == nil {
192 | return nil
193 | }
194 |
195 | if m.GetUserProfile() == nil {
196 | return UpdateUserProfileRequestValidationError{
197 | field: "UserProfile",
198 | reason: "value is required",
199 | }
200 | }
201 |
202 | if v, ok := interface{}(m.GetUserProfile()).(interface{ Validate() error }); ok {
203 | if err := v.Validate(); err != nil {
204 | return UpdateUserProfileRequestValidationError{
205 | field: "UserProfile",
206 | reason: "embedded message failed validation",
207 | cause: err,
208 | }
209 | }
210 | }
211 |
212 | return nil
213 | }
214 |
215 | // UpdateUserProfileRequestValidationError is the validation error returned by
216 | // UpdateUserProfileRequest.Validate if the designated constraints aren't met.
217 | type UpdateUserProfileRequestValidationError struct {
218 | field string
219 | reason string
220 | cause error
221 | key bool
222 | }
223 |
224 | // Field function returns field value.
225 | func (e UpdateUserProfileRequestValidationError) Field() string { return e.field }
226 |
227 | // Reason function returns reason value.
228 | func (e UpdateUserProfileRequestValidationError) Reason() string { return e.reason }
229 |
230 | // Cause function returns cause value.
231 | func (e UpdateUserProfileRequestValidationError) Cause() error { return e.cause }
232 |
233 | // Key function returns key value.
234 | func (e UpdateUserProfileRequestValidationError) Key() bool { return e.key }
235 |
236 | // ErrorName returns error name.
237 | func (e UpdateUserProfileRequestValidationError) ErrorName() string {
238 | return "UpdateUserProfileRequestValidationError"
239 | }
240 |
241 | // Error satisfies the builtin error interface
242 | func (e UpdateUserProfileRequestValidationError) Error() string {
243 | cause := ""
244 | if e.cause != nil {
245 | cause = fmt.Sprintf(" | caused by: %v", e.cause)
246 | }
247 |
248 | key := ""
249 | if e.key {
250 | key = "key for "
251 | }
252 |
253 | return fmt.Sprintf(
254 | "invalid %sUpdateUserProfileRequest.%s: %s%s",
255 | key,
256 | e.field,
257 | e.reason,
258 | cause)
259 | }
260 |
261 | var _ error = UpdateUserProfileRequestValidationError{}
262 |
263 | var _ interface {
264 | Field() string
265 | Reason() string
266 | Key() bool
267 | Cause() error
268 | ErrorName() string
269 | } = UpdateUserProfileRequestValidationError{}
270 |
--------------------------------------------------------------------------------
/modules/user-profile/v1/pb/user_profile.pb.validate.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-validate. DO NOT EDIT.
2 | // source: pb/user_profile.proto
3 |
4 | package pb
5 |
6 | import (
7 | "bytes"
8 | "errors"
9 | "fmt"
10 | "net"
11 | "net/mail"
12 | "net/url"
13 | "regexp"
14 | "strings"
15 | "time"
16 | "unicode/utf8"
17 |
18 | "google.golang.org/protobuf/types/known/anypb"
19 |
20 | types "github.com/deqode/GoArcc/protos/types"
21 | )
22 |
23 | // ensure the imports are used
24 | var (
25 | _ = bytes.MinRead
26 | _ = errors.New("")
27 | _ = fmt.Print
28 | _ = utf8.UTFMax
29 | _ = (*regexp.Regexp)(nil)
30 | _ = (*strings.Reader)(nil)
31 | _ = net.IPv4len
32 | _ = time.Duration(0)
33 | _ = (*url.URL)(nil)
34 | _ = (*mail.Address)(nil)
35 | _ = anypb.Any{}
36 |
37 | _ = types.VCSProviders(0)
38 | )
39 |
40 | // Validate checks the field values on GetUserProfileBySubRequest with the
41 | // rules defined in the proto definition for this message. If any rules are
42 | // violated, an error is returned.
43 | func (m *GetUserProfileBySubRequest) Validate() error {
44 | if m == nil {
45 | return nil
46 | }
47 |
48 | if utf8.RuneCountInString(m.GetSub()) < 3 {
49 | return GetUserProfileBySubRequestValidationError{
50 | field: "Sub",
51 | reason: "value length must be at least 3 runes",
52 | }
53 | }
54 |
55 | return nil
56 | }
57 |
58 | // GetUserProfileBySubRequestValidationError is the validation error returned
59 | // by GetUserProfileBySubRequest.Validate if the designated constraints aren't met.
60 | type GetUserProfileBySubRequestValidationError struct {
61 | field string
62 | reason string
63 | cause error
64 | key bool
65 | }
66 |
67 | // Field function returns field value.
68 | func (e GetUserProfileBySubRequestValidationError) Field() string { return e.field }
69 |
70 | // Reason function returns reason value.
71 | func (e GetUserProfileBySubRequestValidationError) Reason() string { return e.reason }
72 |
73 | // Cause function returns cause value.
74 | func (e GetUserProfileBySubRequestValidationError) Cause() error { return e.cause }
75 |
76 | // Key function returns key value.
77 | func (e GetUserProfileBySubRequestValidationError) Key() bool { return e.key }
78 |
79 | // ErrorName returns error name.
80 | func (e GetUserProfileBySubRequestValidationError) ErrorName() string {
81 | return "GetUserProfileBySubRequestValidationError"
82 | }
83 |
84 | // Error satisfies the builtin error interface
85 | func (e GetUserProfileBySubRequestValidationError) Error() string {
86 | cause := ""
87 | if e.cause != nil {
88 | cause = fmt.Sprintf(" | caused by: %v", e.cause)
89 | }
90 |
91 | key := ""
92 | if e.key {
93 | key = "key for "
94 | }
95 |
96 | return fmt.Sprintf(
97 | "invalid %sGetUserProfileBySubRequest.%s: %s%s",
98 | key,
99 | e.field,
100 | e.reason,
101 | cause)
102 | }
103 |
104 | var _ error = GetUserProfileBySubRequestValidationError{}
105 |
106 | var _ interface {
107 | Field() string
108 | Reason() string
109 | Key() bool
110 | Cause() error
111 | ErrorName() string
112 | } = GetUserProfileBySubRequestValidationError{}
113 |
114 | // Validate checks the field values on UserProfile with the rules defined in
115 | // the proto definition for this message. If any rules are violated, an error
116 | // is returned.
117 | func (m *UserProfile) Validate() error {
118 | if m == nil {
119 | return nil
120 | }
121 |
122 | // no validation rules for Id
123 |
124 | if l := utf8.RuneCountInString(m.GetSub()); l < 3 || l > 100 {
125 | return UserProfileValidationError{
126 | field: "Sub",
127 | reason: "value length must be between 3 and 100 runes, inclusive",
128 | }
129 | }
130 |
131 | if l := utf8.RuneCountInString(m.GetName()); l < 1 || l > 100 {
132 | return UserProfileValidationError{
133 | field: "Name",
134 | reason: "value length must be between 1 and 100 runes, inclusive",
135 | }
136 | }
137 |
138 | // no validation rules for UserName
139 |
140 | if utf8.RuneCountInString(m.GetEmail()) < 0 {
141 | return UserProfileValidationError{
142 | field: "Email",
143 | reason: "value length must be at least 0 runes",
144 | }
145 | }
146 |
147 | if err := m._validateEmail(m.GetEmail()); err != nil {
148 | return UserProfileValidationError{
149 | field: "Email",
150 | reason: "value must be a valid email address",
151 | cause: err,
152 | }
153 | }
154 |
155 | // no validation rules for PhoneNumber
156 |
157 | if _, ok := _UserProfile_ExternalSource_NotInLookup[m.GetExternalSource()]; ok {
158 | return UserProfileValidationError{
159 | field: "ExternalSource",
160 | reason: "value must not be in list [0]",
161 | }
162 | }
163 |
164 | // no validation rules for ProfilePicUrl
165 |
166 | if v, ok := interface{}(m.GetTokenValidTill()).(interface{ Validate() error }); ok {
167 | if err := v.Validate(); err != nil {
168 | return UserProfileValidationError{
169 | field: "TokenValidTill",
170 | reason: "embedded message failed validation",
171 | cause: err,
172 | }
173 | }
174 | }
175 |
176 | return nil
177 | }
178 |
179 | func (m *UserProfile) _validateHostname(host string) error {
180 | s := strings.ToLower(strings.TrimSuffix(host, "."))
181 |
182 | if len(host) > 253 {
183 | return errors.New("hostname cannot exceed 253 characters")
184 | }
185 |
186 | for _, part := range strings.Split(s, ".") {
187 | if l := len(part); l == 0 || l > 63 {
188 | return errors.New("hostname part must be non-empty and cannot exceed 63 characters")
189 | }
190 |
191 | if part[0] == '-' {
192 | return errors.New("hostname parts cannot begin with hyphens")
193 | }
194 |
195 | if part[len(part)-1] == '-' {
196 | return errors.New("hostname parts cannot end with hyphens")
197 | }
198 |
199 | for _, r := range part {
200 | if (r < 'a' || r > 'z') && (r < '0' || r > '9') && r != '-' {
201 | return fmt.Errorf("hostname parts can only contain alphanumeric characters or hyphens, got %q", string(r))
202 | }
203 | }
204 | }
205 |
206 | return nil
207 | }
208 |
209 | func (m *UserProfile) _validateEmail(addr string) error {
210 | a, err := mail.ParseAddress(addr)
211 | if err != nil {
212 | return err
213 | }
214 | addr = a.Address
215 |
216 | if len(addr) > 254 {
217 | return errors.New("email addresses cannot exceed 254 characters")
218 | }
219 |
220 | parts := strings.SplitN(addr, "@", 2)
221 |
222 | if len(parts[0]) > 64 {
223 | return errors.New("email address local phrase cannot exceed 64 characters")
224 | }
225 |
226 | return m._validateHostname(parts[1])
227 | }
228 |
229 | // UserProfileValidationError is the validation error returned by
230 | // UserProfile.Validate if the designated constraints aren't met.
231 | type UserProfileValidationError struct {
232 | field string
233 | reason string
234 | cause error
235 | key bool
236 | }
237 |
238 | // Field function returns field value.
239 | func (e UserProfileValidationError) Field() string { return e.field }
240 |
241 | // Reason function returns reason value.
242 | func (e UserProfileValidationError) Reason() string { return e.reason }
243 |
244 | // Cause function returns cause value.
245 | func (e UserProfileValidationError) Cause() error { return e.cause }
246 |
247 | // Key function returns key value.
248 | func (e UserProfileValidationError) Key() bool { return e.key }
249 |
250 | // ErrorName returns error name.
251 | func (e UserProfileValidationError) ErrorName() string { return "UserProfileValidationError" }
252 |
253 | // Error satisfies the builtin error interface
254 | func (e UserProfileValidationError) Error() string {
255 | cause := ""
256 | if e.cause != nil {
257 | cause = fmt.Sprintf(" | caused by: %v", e.cause)
258 | }
259 |
260 | key := ""
261 | if e.key {
262 | key = "key for "
263 | }
264 |
265 | return fmt.Sprintf(
266 | "invalid %sUserProfile.%s: %s%s",
267 | key,
268 | e.field,
269 | e.reason,
270 | cause)
271 | }
272 |
273 | var _ error = UserProfileValidationError{}
274 |
275 | var _ interface {
276 | Field() string
277 | Reason() string
278 | Key() bool
279 | Cause() error
280 | ErrorName() string
281 | } = UserProfileValidationError{}
282 |
283 | var _UserProfile_ExternalSource_NotInLookup = map[types.VCSProviders]struct{}{
284 | 0: {},
285 | }
286 |
--------------------------------------------------------------------------------
/protos/google/api/field_mask.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option java_package = "com.google.protobuf";
37 | option java_outer_classname = "FieldMaskProto";
38 | option java_multiple_files = true;
39 | option objc_class_prefix = "GPB";
40 | option go_package = "google.golang.org/protobuf/types/known/fieldmaskpb";
41 | option cc_enable_arenas = true;
42 |
43 | // `FieldMask` represents a set of symbolic field paths, for example:
44 | //
45 | // paths: "f.a"
46 | // paths: "f.b.d"
47 | //
48 | // Here `f` represents a field in some root message, `a` and `b`
49 | // fields in the message found in `f`, and `d` a field found in the
50 | // message in `f.b`.
51 | //
52 | // Field masks are used to specify a subset of fields that should be
53 | // returned by a get operation or modified by an update operation.
54 | // Field masks also have a custom JSON encoding (see below).
55 | //
56 | // # Field Masks in Projections
57 | //
58 | // When used in the context of a projection, a response message or
59 | // sub-message is filtered by the API to only contain those fields as
60 | // specified in the mask. For example, if the mask in the previous
61 | // example is applied to a response message as follows:
62 | //
63 | // f {
64 | // a : 22
65 | // b {
66 | // d : 1
67 | // x : 2
68 | // }
69 | // y : 13
70 | // }
71 | // z: 8
72 | //
73 | // The result will not contain specific values for fields x,y and z
74 | // (their value will be set to the default, and omitted in proto text
75 | // output):
76 | //
77 | //
78 | // f {
79 | // a : 22
80 | // b {
81 | // d : 1
82 | // }
83 | // }
84 | //
85 | // A repeated field is not allowed except at the last position of a
86 | // paths string.
87 | //
88 | // If a FieldMask object is not present in a get operation, the
89 | // operation applies to all fields (as if a FieldMask of all fields
90 | // had been specified).
91 | //
92 | // Note that a field mask does not necessarily apply to the
93 | // top-level response message. In case of a REST get operation, the
94 | // field mask applies directly to the response, but in case of a REST
95 | // list operation, the mask instead applies to each individual message
96 | // in the returned resource list. In case of a REST custom method,
97 | // other definitions may be used. Where the mask applies will be
98 | // clearly documented together with its declaration in the API. In
99 | // any case, the effect on the returned resource/resources is required
100 | // behavior for APIs.
101 | //
102 | // # Field Masks in Update Operations
103 | //
104 | // A field mask in update operations specifies which fields of the
105 | // targeted resource are going to be updated. The API is required
106 | // to only change the values of the fields as specified in the mask
107 | // and leave the others untouched. If a resource is passed in to
108 | // describe the updated values, the API ignores the values of all
109 | // fields not covered by the mask.
110 | //
111 | // If a repeated field is specified for an update operation, new values will
112 | // be appended to the existing repeated field in the target resource. Note that
113 | // a repeated field is only allowed in the last position of a `paths` string.
114 | //
115 | // If a sub-message is specified in the last position of the field mask for an
116 | // update operation, then new value will be merged into the existing sub-message
117 | // in the target resource.
118 | //
119 | // For example, given the target message:
120 | //
121 | // f {
122 | // b {
123 | // d: 1
124 | // x: 2
125 | // }
126 | // c: [1]
127 | // }
128 | //
129 | // And an update message:
130 | //
131 | // f {
132 | // b {
133 | // d: 10
134 | // }
135 | // c: [2]
136 | // }
137 | //
138 | // then if the field mask is:
139 | //
140 | // paths: ["f.b", "f.c"]
141 | //
142 | // then the result will be:
143 | //
144 | // f {
145 | // b {
146 | // d: 10
147 | // x: 2
148 | // }
149 | // c: [1, 2]
150 | // }
151 | //
152 | // An implementation may provide options to override this default behavior for
153 | // repeated and message fields.
154 | //
155 | // In order to reset a field's value to the default, the field must
156 | // be in the mask and set to the default value in the provided resource.
157 | // Hence, in order to reset all fields of a resource, provide a default
158 | // instance of the resource and set all fields in the mask, or do
159 | // not provide a mask as described below.
160 | //
161 | // If a field mask is not present on update, the operation applies to
162 | // all fields (as if a field mask of all fields has been specified).
163 | // Note that in the presence of schema evolution, this may mean that
164 | // fields the client does not know and has therefore not filled into
165 | // the request will be reset to their default. If this is unwanted
166 | // behavior, a specific service may require a client to always specify
167 | // a field mask, producing an error if not.
168 | //
169 | // As with get operations, the location of the resource which
170 | // describes the updated values in the request message depends on the
171 | // operation kind. In any case, the effect of the field mask is
172 | // required to be honored by the API.
173 | //
174 | // ## Considerations for HTTP REST
175 | //
176 | // The HTTP kind of an update operation which uses a field mask must
177 | // be set to PATCH instead of PUT in order to satisfy HTTP semantics
178 | // (PUT must only be used for full updates).
179 | //
180 | // # JSON Encoding of Field Masks
181 | //
182 | // In JSON, a field mask is encoded as a single string where paths are
183 | // separated by a comma. Fields name in each path are converted
184 | // to/from lower-camel naming conventions.
185 | //
186 | // As an example, consider the following message declarations:
187 | //
188 | // message Profile {
189 | // User user = 1;
190 | // Photo photo = 2;
191 | // }
192 | // message User {
193 | // string display_name = 1;
194 | // string address = 2;
195 | // }
196 | //
197 | // In proto a field mask for `Profile` may look as such:
198 | //
199 | // mask {
200 | // paths: "user.display_name"
201 | // paths: "photo"
202 | // }
203 | //
204 | // In JSON, the same mask is represented as below:
205 | //
206 | // {
207 | // mask: "user.displayName,photo"
208 | // }
209 | //
210 | // # Field Masks and Oneof Fields
211 | //
212 | // Field masks treat fields in oneofs just as regular fields. Consider the
213 | // following message:
214 | //
215 | // message SampleMessage {
216 | // oneof test_oneof {
217 | // string name = 4;
218 | // SubMessage sub_message = 9;
219 | // }
220 | // }
221 | //
222 | // The field mask can be:
223 | //
224 | // mask {
225 | // paths: "name"
226 | // }
227 | //
228 | // Or:
229 | //
230 | // mask {
231 | // paths: "sub_message"
232 | // }
233 | //
234 | // Note that oneof type names ("test_oneof" in this case) cannot be used in
235 | // paths.
236 | //
237 | // ## Field Mask Verification
238 | //
239 | // The implementation of any API method which has a FieldMask type field in the
240 | // request should verify the included field paths, and return an
241 | // `INVALID_ARGUMENT` error if any path is unmappable.
242 | message FieldMask {
243 | // The set of field mask paths.
244 | repeated string paths = 1;
245 | }
246 |
--------------------------------------------------------------------------------