Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions internal/app/contribution/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,15 @@ type ContributionScore struct {
CreatedAt time.Time
UpdatedAt time.Time
}

type Transaction struct {
Id int `db:"id"`
UserId int `db:"user_id"`
ContributionId int `db:"contribution_id"`
IsRedeemed bool `db:"is_redeemed"`
IsGained bool `db:"is_gained"`
TransactedBalance int `db:"transacted_balance"`
TransactedAt time.Time `db:"transacted_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
83 changes: 45 additions & 38 deletions internal/app/contribution/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/joshsoftware/code-curiosity-2025/internal/app/bigquery"
repoService "github.com/joshsoftware/code-curiosity-2025/internal/app/repository"
"github.com/joshsoftware/code-curiosity-2025/internal/app/transaction"
"github.com/joshsoftware/code-curiosity-2025/internal/app/user"
"github.com/joshsoftware/code-curiosity-2025/internal/pkg/apperrors"
"github.com/joshsoftware/code-curiosity-2025/internal/repository"
Expand Down Expand Up @@ -52,24 +53,28 @@ type service struct {
contributionRepository repository.ContributionRepository
repositoryService repoService.Service
userService user.Service
transactionService transaction.Service
httpClient *http.Client
}

type Service interface {
ProcessFetchedContributions(ctx context.Context) error
ProcessEachContribution(ctx context.Context, contribution ContributionResponse) error
GetContributionType(ctx context.Context, contribution ContributionResponse) (string, error)
CreateContribution(ctx context.Context, contributionType string, contributionDetails ContributionResponse, repositoryId int, userId int) (Contribution, error)
HandleContributionCreation(ctx context.Context, repositoryID int, contribution ContributionResponse) (Contribution, error)
GetContributionScoreDetailsByContributionType(ctx context.Context, contributionType string) (ContributionScore, error)
FetchUserContributions(ctx context.Context) ([]Contribution, error)
GetContributionByGithubEventId(ctx context.Context, githubEventId string) (Contribution, error)
}

func NewService(bigqueryService bigquery.Service, contributionRepository repository.ContributionRepository, repositoryService repoService.Service, userService user.Service, httpClient *http.Client) Service {
func NewService(bigqueryService bigquery.Service, contributionRepository repository.ContributionRepository, repositoryService repoService.Service, userService user.Service, transactionService transaction.Service, httpClient *http.Client) Service {
return &service{
bigqueryService: bigqueryService,
contributionRepository: contributionRepository,
repositoryService: repositoryService,
userService: userService,
transactionService: transactionService,
httpClient: httpClient,
}
}
Expand Down Expand Up @@ -112,48 +117,28 @@ func (s *service) ProcessFetchedContributions(ctx context.Context) error {
}

func (s *service) ProcessEachContribution(ctx context.Context, contribution ContributionResponse) error {
_, err := s.GetContributionByGithubEventId(ctx, contribution.ID)
if err == nil {
return nil
}

if err != apperrors.ErrContributionNotFound {
slog.Error("error fetching contribution by github event id", "error", err)
return err
}

var repositoryId int
repoFetched, err := s.repositoryService.GetRepoByGithubId(ctx, contribution.RepoID)
if err == nil {
repositoryId = repoFetched.Id
} else if err == apperrors.ErrRepoNotFound {
repositoryCreated, err := s.repositoryService.CreateRepository(ctx, contribution.RepoID, contribution.RepoUrl)
if err != nil {
slog.Error("error creating repository", "error", err)
obtainedContribution, err := s.GetContributionByGithubEventId(ctx, contribution.ID)
if err != nil {
if err == apperrors.ErrContributionNotFound {
obtainedRepository, err := s.repositoryService.HandleRepositoryCreation(ctx, repoService.ContributionResponse(contribution))
if err != nil {
slog.Error("error handling repository creation", "error", err)
return err
}
obtainedContribution, err = s.HandleContributionCreation(ctx, obtainedRepository.Id, contribution)
if err != nil {
slog.Error("error handling contribution creation", "error", err)
return err
}
} else {
slog.Error("error fetching contribution by github event id", "error", err)
return err
}

repositoryId = repositoryCreated.Id
} else {
slog.Error("error fetching repo by repo id", "error", err)
return err
}

user, err := s.userService.GetUserByGithubId(ctx, contribution.ActorID)
if err != nil {
slog.Error("error getting user id", "error", err)
return err
}

contributionType, err := s.GetContributionType(ctx, contribution)
if err != nil {
slog.Error("error getting contribution type", "error", err)
return err
}

_, err = s.CreateContribution(ctx, contributionType, contribution, repositoryId, user.Id)
_, err = s.transactionService.HandleTransactionCreation(ctx, transaction.Contribution(obtainedContribution))
if err != nil {
slog.Error("error creating contribution", "error", err)
slog.Error("error handling transaction creation", "error", err)
return err
}

Expand Down Expand Up @@ -246,6 +231,28 @@ func (s *service) CreateContribution(ctx context.Context, contributionType strin
return Contribution(contributionResponse), nil
}

func (s *service) HandleContributionCreation(ctx context.Context, repositoryID int, contribution ContributionResponse) (Contribution, error) {
user, err := s.userService.GetUserByGithubId(ctx, contribution.ActorID)
if err != nil {
slog.Error("error getting user id", "error", err)
return Contribution{}, err
}

contributionType, err := s.GetContributionType(ctx, contribution)
if err != nil {
slog.Error("error getting contribution type", "error", err)
return Contribution{}, err
}

obtainedContribution, err := s.CreateContribution(ctx, contributionType, contribution, repositoryID, user.Id)
if err != nil {
slog.Error("error creating contribution", "error", err)
return Contribution{}, err
}

return obtainedContribution, nil
}

func (s *service) GetContributionScoreDetailsByContributionType(ctx context.Context, contributionType string) (ContributionScore, error) {
contributionScoreDetails, err := s.contributionRepository.GetContributionScoreDetailsByContributionType(ctx, nil, contributionType)
if err != nil {
Expand Down
7 changes: 5 additions & 2 deletions internal/app/dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/joshsoftware/code-curiosity-2025/internal/app/contribution"
"github.com/joshsoftware/code-curiosity-2025/internal/app/github"
repoService "github.com/joshsoftware/code-curiosity-2025/internal/app/repository"
"github.com/joshsoftware/code-curiosity-2025/internal/app/transaction"
"github.com/joshsoftware/code-curiosity-2025/internal/app/user"
"github.com/joshsoftware/code-curiosity-2025/internal/config"

Expand All @@ -29,17 +30,19 @@ func InitDependencies(db *sqlx.DB, appCfg config.AppConfig, client config.Bigque
userRepository := repository.NewUserRepository(db)
contributionRepository := repository.NewContributionRepository(db)
repositoryRepository := repository.NewRepositoryRepository(db)
transactionRepository := repository.NewTransactionRepository(db)

userService := user.NewService(userRepository)
authService := auth.NewService(userService, appCfg)
bigqueryService := bigquery.NewService(client, userRepository)
githubService := github.NewService(appCfg, httpClient)
repositoryService := repoService.NewService(repositoryRepository, githubService)
contributionService := contribution.NewService(bigqueryService, contributionRepository, repositoryService, userService, httpClient)
transactionService := transaction.NewService(transactionRepository, userService)
contributionService := contribution.NewService(bigqueryService, contributionRepository, repositoryService, userService, transactionService, httpClient)

authHandler := auth.NewHandler(authService, appCfg)
userHandler := user.NewHandler(userService)
repositoryHandler := repoService.NewHandler(repositoryService)
repositoryHandler := repoService.NewHandler(repositoryService, githubService)
contributionHandler := contribution.NewHandler(contributionService)

return Dependencies{
Expand Down
8 changes: 4 additions & 4 deletions internal/app/github/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ type service struct {
type Service interface {
configureGithubApiHeaders() map[string]string
FetchRepositoryDetails(ctx context.Context, getUserRepoDetailsUrl string) (FetchRepositoryDetailsResponse, error)
FetchRepositoryLanguages(ctx context.Context, client *http.Client, getRepoLanguagesURL string) (RepoLanguages, error)
FetchRepositoryContributors(ctx context.Context, client *http.Client, getRepoContributorsURl string) ([]FetchRepoContributorsResponse, error)
FetchRepositoryLanguages(ctx context.Context, getRepoLanguagesURL string) (RepoLanguages, error)
FetchRepositoryContributors(ctx context.Context, getRepoContributorsURl string) ([]FetchRepoContributorsResponse, error)
}

func NewService(appCfg config.AppConfig, httpClient *http.Client) Service {
Expand Down Expand Up @@ -54,7 +54,7 @@ func (s *service) FetchRepositoryDetails(ctx context.Context, getUserRepoDetails
return repoDetails, nil
}

func (s *service) FetchRepositoryLanguages(ctx context.Context, client *http.Client, getRepoLanguagesURL string) (RepoLanguages, error) {
func (s *service) FetchRepositoryLanguages(ctx context.Context, getRepoLanguagesURL string) (RepoLanguages, error) {
headers := s.configureGithubApiHeaders()

body, err := utils.DoGet(s.httpClient, getRepoLanguagesURL, headers)
Expand All @@ -73,7 +73,7 @@ func (s *service) FetchRepositoryLanguages(ctx context.Context, client *http.Cli
return repoLanguages, nil
}

func (s *service) FetchRepositoryContributors(ctx context.Context, client *http.Client, getRepoContributorsURl string) ([]FetchRepoContributorsResponse, error) {
func (s *service) FetchRepositoryContributors(ctx context.Context, getRepoContributorsURl string) ([]FetchRepoContributorsResponse, error) {
headers := s.configureGithubApiHeaders()

body, err := utils.DoGet(s.httpClient, getRepoContributorsURl, headers)
Expand Down
12 changes: 12 additions & 0 deletions internal/app/repository/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ type FetchUsersContributedReposResponse struct {
TotalCoinsEarned int
}

type ContributionResponse struct {
ID string `bigquery:"id"`
Type string `bigquery:"type"`
ActorID int `bigquery:"actor_id"`
ActorLogin string `bigquery:"actor_login"`
RepoID int `bigquery:"repo_id"`
RepoName string `bigquery:"repo_name"`
RepoUrl string `bigquery:"repo_url"`
Payload string `bigquery:"payload"`
CreatedAt time.Time `bigquery:"created_at"`
}

type Contribution struct {
Id int
UserId int
Expand Down
11 changes: 4 additions & 7 deletions internal/app/repository/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ type Handler interface {
FetchLanguagePercentInRepo(w http.ResponseWriter, r *http.Request)
}

func NewHandler(repositoryService Service) Handler {
func NewHandler(repositoryService Service, githubService github.Service) Handler {
return &handler{
repositoryService: repositoryService,
githubService: githubService,
}
}

Expand Down Expand Up @@ -69,8 +70,6 @@ func (h *handler) FetchParticularRepoDetails(w http.ResponseWriter, r *http.Requ
func (h *handler) FetchParticularRepoContributors(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

client := &http.Client{}

repoIdPath := r.PathValue("repo_id")
repoId, err := strconv.Atoi(repoIdPath)
if err != nil {
Expand All @@ -88,7 +87,7 @@ func (h *handler) FetchParticularRepoContributors(w http.ResponseWriter, r *http
return
}

repoContributors, err := h.githubService.FetchRepositoryContributors(ctx, client, repoDetails.ContributorsUrl)
repoContributors, err := h.githubService.FetchRepositoryContributors(ctx, repoDetails.ContributorsUrl)
if err != nil {
slog.Error("error fetching repo contributors", "error", err)
status, errorMessage := apperrors.MapError(err)
Expand Down Expand Up @@ -125,8 +124,6 @@ func (h *handler) FetchUserContributionsInRepo(w http.ResponseWriter, r *http.Re
func (h *handler) FetchLanguagePercentInRepo(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

client := &http.Client{}

repoIdPath := r.PathValue("repo_id")
repoId, err := strconv.Atoi(repoIdPath)
if err != nil {
Expand All @@ -144,7 +141,7 @@ func (h *handler) FetchLanguagePercentInRepo(w http.ResponseWriter, r *http.Requ
return
}

repoLanguages, err := h.githubService.FetchRepositoryLanguages(ctx, client, repoDetails.LanguagesUrl)
repoLanguages, err := h.githubService.FetchRepositoryLanguages(ctx, repoDetails.LanguagesUrl)
if err != nil {
slog.Error("error fetching particular repo languages", "error", err)
status, errorMessage := apperrors.MapError(err)
Expand Down
22 changes: 21 additions & 1 deletion internal/app/repository/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http"

"github.com/joshsoftware/code-curiosity-2025/internal/app/github"
"github.com/joshsoftware/code-curiosity-2025/internal/pkg/apperrors"
"github.com/joshsoftware/code-curiosity-2025/internal/repository"
)

Expand All @@ -19,6 +20,7 @@ type Service interface {
GetRepoByGithubId(ctx context.Context, githubRepoId int) (Repository, error)
GetRepoByRepoId(ctx context.Context, repoId int) (Repository, error)
CreateRepository(ctx context.Context, repoGithubId int, ContributionRepoDetailsUrl string) (Repository, error)
HandleRepositoryCreation(ctx context.Context, contribution ContributionResponse) (Repository, error)
FetchUsersContributedRepos(ctx context.Context, client *http.Client) ([]FetchUsersContributedReposResponse, error)
FetchUserContributionsInRepo(ctx context.Context, githubRepoId int) ([]Contribution, error)
CalculateLanguagePercentInRepo(ctx context.Context, repoLanguages RepoLanguages) ([]LanguagePercent, error)
Expand Down Expand Up @@ -77,6 +79,24 @@ func (s *service) CreateRepository(ctx context.Context, repoGithubId int, Contri
return Repository(repositoryCreated), nil
}

func (s *service) HandleRepositoryCreation(ctx context.Context, contribution ContributionResponse) (Repository, error) {
obtainedRepository, err := s.GetRepoByGithubId(ctx, contribution.RepoID)
if err != nil {
if err == apperrors.ErrRepoNotFound {
obtainedRepository, err = s.CreateRepository(ctx, contribution.RepoID, contribution.RepoUrl)
if err != nil {
slog.Error("error creating repository", "error", err)
return Repository{}, err
}
} else {
slog.Error("error fetching repo by repo id", "error", err)
return Repository{}, err
}
}

return obtainedRepository, nil
}

func (s *service) FetchUsersContributedRepos(ctx context.Context, client *http.Client) ([]FetchUsersContributedReposResponse, error) {
usersContributedRepos, err := s.repositoryRepository.FetchUsersContributedRepos(ctx, nil)
if err != nil {
Expand All @@ -89,7 +109,7 @@ func (s *service) FetchUsersContributedRepos(ctx context.Context, client *http.C
for i, usersContributedRepo := range usersContributedRepos {
fetchUsersContributedReposResponse[i].Repository = Repository(usersContributedRepo)

contributedRepoLanguages, err := s.githubService.FetchRepositoryLanguages(ctx, client, usersContributedRepo.LanguagesUrl)
contributedRepoLanguages, err := s.githubService.FetchRepositoryLanguages(ctx, usersContributedRepo.LanguagesUrl)
if err != nil {
slog.Error("error fetching languages for repository", "error", err)
return nil, err
Expand Down
1 change: 1 addition & 0 deletions internal/app/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func NewRouter(deps Dependencies) http.Handler {

router.HandleFunc("PATCH /api/v1/user/email", middleware.Authentication(deps.UserHandler.UpdateUserEmail, deps.AppCfg))

router.HandleFunc("GET /api/v1/user/contributions/all", middleware.Authentication(deps.ContributionHandler.FetchUserContributions, deps.AppCfg))

router.HandleFunc("GET /api/v1/user/repositories", middleware.Authentication(deps.RepositoryHandler.FetchUsersContributedRepos, deps.AppCfg))
router.HandleFunc("GET /api/v1/user/repositories/{repo_id}", middleware.Authentication(deps.RepositoryHandler.FetchParticularRepoDetails, deps.AppCfg))
Expand Down
28 changes: 28 additions & 0 deletions internal/app/transaction/domain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package transaction

import "time"

type Transaction struct {
Id int `db:"id"`
UserId int `db:"user_id"`
ContributionId int `db:"contribution_id"`
IsRedeemed bool `db:"is_redeemed"`
IsGained bool `db:"is_gained"`
TransactedBalance int `db:"transacted_balance"`
TransactedAt time.Time `db:"transacted_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}

type Contribution struct {
Id int
UserId int
RepositoryId int
ContributionScoreId int
ContributionType string
BalanceChange int
ContributedAt time.Time
GithubEventId string
CreatedAt time.Time
UpdatedAt time.Time
}
Loading