From c78fa593fd10b3942d61d6bf33bad690bfcff552 Mon Sep 17 00:00:00 2001 From: Soumya Ray Date: Sun, 29 Oct 2017 19:07:16 +0800 Subject: [PATCH] Infrastructure holds database and github --- .pryrc | 2 + Rakefile | 2 +- app.rb | 10 +-- config/app.yml | 4 +- domain/database_repositories/collaborators.rb | 43 +++++++++++ domain/database_repositories/for.rb | 10 +++ domain/database_repositories/init.rb | 5 ++ domain/database_repositories/repos.rb | 70 ++++++++++++++++++ {entities => domain/entities}/collaborator.rb | 0 {entities => domain/entities}/init.rb | 0 {entities => domain/entities}/repo.rb | 0 .../github_mappers}/collaborator_mapper.rb | 0 .../mappers => domain/github_mappers}/init.rb | 0 .../github_mappers}/repo_mapper.rb | 0 domain/init.rb | 6 ++ .../migrations/001_repos_create.rb | 0 .../migrations/002_collaborators_create.rb | 0 .../003_repos_contributors_join_create.rb | 0 .../database/orm/collaborator_orm.rb | 18 +++++ infrastructure/{db => database}/orm/init.rb | 0 infrastructure/database/orm/orm.rb | 10 +++ infrastructure/database/orm/repo_orm.rb | 18 +++++ infrastructure/db/orm/collaborator_orm.rb | 49 ------------- infrastructure/db/orm/orm.rb | 10 --- infrastructure/db/orm/repo_orm.rb | 72 ------------------- {lib => infrastructure/github}/github_api.rb | 0 {lib => infrastructure/github}/init.rb | 1 - infrastructure/init.rb | 5 +- init.rb | 2 +- spec/api_spec.rb | 41 +++++++---- 30 files changed, 225 insertions(+), 153 deletions(-) create mode 100644 domain/database_repositories/collaborators.rb create mode 100644 domain/database_repositories/for.rb create mode 100644 domain/database_repositories/init.rb create mode 100644 domain/database_repositories/repos.rb rename {entities => domain/entities}/collaborator.rb (100%) rename {entities => domain/entities}/init.rb (100%) rename {entities => domain/entities}/repo.rb (100%) rename {lib/mappers => domain/github_mappers}/collaborator_mapper.rb (100%) rename {lib/mappers => domain/github_mappers}/init.rb (100%) rename {lib/mappers => domain/github_mappers}/repo_mapper.rb (100%) create mode 100644 domain/init.rb rename infrastructure/{db => database}/migrations/001_repos_create.rb (100%) rename infrastructure/{db => database}/migrations/002_collaborators_create.rb (100%) rename infrastructure/{db => database}/migrations/003_repos_contributors_join_create.rb (100%) create mode 100644 infrastructure/database/orm/collaborator_orm.rb rename infrastructure/{db => database}/orm/init.rb (100%) create mode 100644 infrastructure/database/orm/orm.rb create mode 100644 infrastructure/database/orm/repo_orm.rb delete mode 100644 infrastructure/db/orm/collaborator_orm.rb delete mode 100644 infrastructure/db/orm/orm.rb delete mode 100644 infrastructure/db/orm/repo_orm.rb rename {lib => infrastructure/github}/github_api.rb (100%) rename {lib => infrastructure/github}/init.rb (65%) diff --git a/.pryrc b/.pryrc index dbd7be3..888fe30 100644 --- a/.pryrc +++ b/.pryrc @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'hirb' Hirb.enable diff --git a/Rakefile b/Rakefile index b7e07bf..6a1c365 100644 --- a/Rakefile +++ b/Rakefile @@ -52,7 +52,7 @@ namespace :db do desc 'Run migrations' task :migrate do puts "Migrating #{app.environment} database to latest" - Sequel::Migrator.run(app.DB, 'infrastructure/db/migrations') + Sequel::Migrator.run(app.DB, 'infrastructure/database/migrations') end desc 'Drop all tables' diff --git a/app.rb b/app.rb index 9edee6f..3982b6d 100644 --- a/app.rb +++ b/app.rb @@ -23,7 +23,7 @@ class Api < Roda routing.on 'repo', String, String do |ownername, reponame| # GET /api/v0.1/repo/:ownername/:reponame request routing.get do - repo = Database::ORM[Entity::Repo] + repo = Repository::For[Entity::Repo] .find_full_name(ownername, reponame) routing.halt(404, error: 'Repository not found') unless repo @@ -33,13 +33,15 @@ class Api < Roda # POST '/api/v0.1/repo/:ownername/:reponame routing.post do begin - github_repo = Github::RepoMapper.new(app.config) - repo = github_repo.load(ownername, reponame) + repo = Github::RepoMapper.new(app.config) + .load(ownername, reponame) rescue StandardError routing.halt(404, error: "Repo not found") end - stored_repo = Database::ORM[Entity::Repo].find_or_create(repo) + stored_repo = Repository::For[repo.class].find_or_create(repo) + response.status = 201 + response['Location'] = "/api/v0.1/repo/#{ownername}/#{reponame}" stored_repo.to_h end end diff --git a/config/app.yml b/config/app.yml index 5db0b72..ab8c560 100644 --- a/config/app.yml +++ b/config/app.yml @@ -1,6 +1,6 @@ --- development: - db_filename: infrastructure/db/dev.db + db_filename: infrastructure/database/dev.db test: - db_filename: infrastructure/db/test.db \ No newline at end of file + db_filename: infrastructure/database/test.db \ No newline at end of file diff --git a/domain/database_repositories/collaborators.rb b/domain/database_repositories/collaborators.rb new file mode 100644 index 0000000..f6e5d31 --- /dev/null +++ b/domain/database_repositories/collaborators.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module CodePraise + module Repository + # Repository for Collaborators + class Collaborators + def self.find_id(id) + db_record = Database::CollaboratorOrm.first(id: id) + rebuild_entity(db_record) + end + + def self.find_username(username) + db_record = Database::CollaboratorOrm.first(username: username) + rebuild_entity(db_record) + end + + def self.find_or_create(entity) + find_username(entity.username) || create_from(entity) + end + + def self.create_from(entity) + db_collaborator = Database::CollaboratorOrm.create( + origin_id: entity.origin_id, + username: entity.username, + email: entity.email + ) + + self.rebuild_entity(db_collaborator) + end + + def self.rebuild_entity(db_record) + return nil unless db_record + + Entity::Collaborator.new( + id: db_record.id, + origin_id: db_record.origin_id, + username: db_record.username, + email: db_record.email + ) + end + end + end +end diff --git a/domain/database_repositories/for.rb b/domain/database_repositories/for.rb new file mode 100644 index 0000000..27fb404 --- /dev/null +++ b/domain/database_repositories/for.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module CodePraise + module Repository + For = { + Entity::Repo => Repos, + Entity::Collaborator => Collaborators + }.freeze + end +end diff --git a/domain/database_repositories/init.rb b/domain/database_repositories/init.rb new file mode 100644 index 0000000..746ca92 --- /dev/null +++ b/domain/database_repositories/init.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative 'repos.rb' +require_relative 'collaborators.rb' +require_relative 'for.rb' \ No newline at end of file diff --git a/domain/database_repositories/repos.rb b/domain/database_repositories/repos.rb new file mode 100644 index 0000000..d978134 --- /dev/null +++ b/domain/database_repositories/repos.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module CodePraise + module Repository + # Repository for Repo Entities + class Repos + def self.find_full_name(ownername, reponame) + # SELECT * FROM `repos` LEFT JOIN `collaborators` + # ON (`collaborators`.`id` = `repos`.`owner_id`) + # WHERE ((`username` = 'owername') AND (`name` = 'reponame')) + db_repo = Database::RepoOrm.left_join(:collaborators, id: :owner_id) + .where(username: ownername, name: reponame) + .first + rebuild_entity(db_repo) + end + + def self.find_id(id) + Database::RepoOrm.first(id: id)&.rebuild_entity + end + + def self.find_origin_id(origin_id) + db_record = Database::RepoOrm.first(origin_id: origin_id) + rebuild_entity(db_record) + end + + def self.find_or_create(entity) + find_origin_id(entity.origin_id) || create_from(entity) + end + + def self.create_from(entity) + new_owner = Collaborators.find_or_create(entity.owner) + db_owner = Database::CollaboratorOrm.first(id: new_owner.id) + + db_repo = Database::RepoOrm.create( + origin_id: entity.origin_id, + name: entity.name, + size: entity.size, + git_url: entity.git_url, + owner: db_owner + ) + + entity.contributors.each do |contrib| + stored_contrib = Collaborators.find_or_create(contrib) + contrib = Database::CollaboratorOrm.first(id: stored_contrib.id) + db_repo.add_contributor(contrib) + end + + rebuild_entity(db_repo) + end + + def self.rebuild_entity(db_record) + return nil unless db_record + + contribs = db_record.contributors.map do |db_contrib| + Collaborators.rebuild_entity(db_contrib) + end + + Entity::Repo.new( + id: db_record.id, + origin_id: db_record.origin_id, + name: db_record.name, + size: db_record.size, + git_url: db_record.git_url, + owner: Collaborators.rebuild_entity(db_record.owner), + contributors: contribs + ) + end + end + end +end diff --git a/entities/collaborator.rb b/domain/entities/collaborator.rb similarity index 100% rename from entities/collaborator.rb rename to domain/entities/collaborator.rb diff --git a/entities/init.rb b/domain/entities/init.rb similarity index 100% rename from entities/init.rb rename to domain/entities/init.rb diff --git a/entities/repo.rb b/domain/entities/repo.rb similarity index 100% rename from entities/repo.rb rename to domain/entities/repo.rb diff --git a/lib/mappers/collaborator_mapper.rb b/domain/github_mappers/collaborator_mapper.rb similarity index 100% rename from lib/mappers/collaborator_mapper.rb rename to domain/github_mappers/collaborator_mapper.rb diff --git a/lib/mappers/init.rb b/domain/github_mappers/init.rb similarity index 100% rename from lib/mappers/init.rb rename to domain/github_mappers/init.rb diff --git a/lib/mappers/repo_mapper.rb b/domain/github_mappers/repo_mapper.rb similarity index 100% rename from lib/mappers/repo_mapper.rb rename to domain/github_mappers/repo_mapper.rb diff --git a/domain/init.rb b/domain/init.rb new file mode 100644 index 0000000..c5a811e --- /dev/null +++ b/domain/init.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +folders = %w[entities database_repositories github_mappers] +folders.each do |folder| + require_relative "#{folder}/init.rb" +end diff --git a/infrastructure/db/migrations/001_repos_create.rb b/infrastructure/database/migrations/001_repos_create.rb similarity index 100% rename from infrastructure/db/migrations/001_repos_create.rb rename to infrastructure/database/migrations/001_repos_create.rb diff --git a/infrastructure/db/migrations/002_collaborators_create.rb b/infrastructure/database/migrations/002_collaborators_create.rb similarity index 100% rename from infrastructure/db/migrations/002_collaborators_create.rb rename to infrastructure/database/migrations/002_collaborators_create.rb diff --git a/infrastructure/db/migrations/003_repos_contributors_join_create.rb b/infrastructure/database/migrations/003_repos_contributors_join_create.rb similarity index 100% rename from infrastructure/db/migrations/003_repos_contributors_join_create.rb rename to infrastructure/database/migrations/003_repos_contributors_join_create.rb diff --git a/infrastructure/database/orm/collaborator_orm.rb b/infrastructure/database/orm/collaborator_orm.rb new file mode 100644 index 0000000..446dd67 --- /dev/null +++ b/infrastructure/database/orm/collaborator_orm.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module CodePraise + module Database + # Object-Relational Mapper for Collaborators + class CollaboratorOrm < Sequel::Model(:collaborators) + one_to_many :owned_repos, + class: :'CodePraise::Database::RepoOrm', + key: :owner_id + + many_to_many :contributed_repos, + join_table: :repos_contributors, + left_key: :contributor_id, right_key: :repo_id + + plugin :timestamps, update_on_create: true + end + end +end diff --git a/infrastructure/db/orm/init.rb b/infrastructure/database/orm/init.rb similarity index 100% rename from infrastructure/db/orm/init.rb rename to infrastructure/database/orm/init.rb diff --git a/infrastructure/database/orm/orm.rb b/infrastructure/database/orm/orm.rb new file mode 100644 index 0000000..898f629 --- /dev/null +++ b/infrastructure/database/orm/orm.rb @@ -0,0 +1,10 @@ +# # frozen_string_literal: true + +# module CodePraise +# module Database +# ORM = { +# CodePraise::Entity::Repo => RepoOrm, +# CodePraise::Entity::Collaborator => CollaboratorOrm +# }.freeze +# end +# end diff --git a/infrastructure/database/orm/repo_orm.rb b/infrastructure/database/orm/repo_orm.rb new file mode 100644 index 0000000..849bad3 --- /dev/null +++ b/infrastructure/database/orm/repo_orm.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module CodePraise + module Database + # Object Relational Mapper for Repo Entities + class RepoOrm < Sequel::Model(:repos) + many_to_one :owner, + class: :'CodePraise::Database::CollaboratorOrm' + + many_to_many :contributors, + class: :'CodePraise::Database::CollaboratorOrm', + join_table: :repos_contributors, + left_key: :repo_id, right_key: :collaborator_id + + plugin :timestamps, update_on_create: true + end + end +end diff --git a/infrastructure/db/orm/collaborator_orm.rb b/infrastructure/db/orm/collaborator_orm.rb deleted file mode 100644 index 403716e..0000000 --- a/infrastructure/db/orm/collaborator_orm.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -module CodePraise - module Database - # Object-Relational Mapper for Collaborators - class CollaboratorOrm < Sequel::Model(:collaborators) - one_to_many :owned_repos, - class: :'CodePraise::Database::RepoOrm', - key: :owner_id - - many_to_many :contributed_repos, - join_table: :repos_contributors, - left_key: :contributor_id, right_key: :repo_id - - plugin :timestamps, update_on_create: true - - def self.find_id(id) - CollaboratorOrm.first(id: id)&.to_entity - end - - def self.find_username(username) - CollaboratorOrm.first(username: username)&.to_entity - end - - def self.find_or_create(entity) - find_username(entity.username) || create_from(entity) - end - - def self.create_from(entity) - stored = create( - origin_id: entity.origin_id, - username: entity.username, - email: entity.email - ) - - stored.to_entity - end - - def to_entity(entity_class = CodePraise::Entity::Collaborator) - entity_class.new( - id: id, - origin_id: origin_id, - username: username, - email: email - ) - end - end - end -end diff --git a/infrastructure/db/orm/orm.rb b/infrastructure/db/orm/orm.rb deleted file mode 100644 index af18a1c..0000000 --- a/infrastructure/db/orm/orm.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module CodePraise - module Database - ORM = { - CodePraise::Entity::Repo => RepoOrm, - CodePraise::Entity::Collaborator => CollaboratorOrm - }.freeze - end -end diff --git a/infrastructure/db/orm/repo_orm.rb b/infrastructure/db/orm/repo_orm.rb deleted file mode 100644 index b4f2bd1..0000000 --- a/infrastructure/db/orm/repo_orm.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true - -module CodePraise - module Database - # Object Relational Mapper for Repo Entities - class RepoOrm < Sequel::Model(:repos) - many_to_one :owner, - class: :'CodePraise::Database::CollaboratorOrm' - - many_to_many :contributors, - class: :'CodePraise::Database::CollaboratorOrm', - join_table: :repos_contributors, - left_key: :repo_id, right_key: :collaborator_id - - plugin :timestamps, update_on_create: true - - def self.find_full_name(ownername, reponame) - # SELECT * FROM `repos` LEFT JOIN `collaborators` - # ON (`collaborators`.`id` = `repos`.`owner_id`) - # WHERE ((`username` = 'owername') AND (`name` = 'reponame')) - RepoOrm.left_join(:collaborators, id: :owner_id) - .where(username: ownername, name: reponame) - .first&.to_entity - end - - def self.find_id(id) - RepoOrm.first(id: id)&.to_entity - end - - def self.find_origin_id(origin_id) - RepoOrm.first(origin_id: origin_id)&.to_entity - end - - def self.find_or_create(entity) - find_origin_id(entity.origin_id) || create_from(entity) - end - - def self.create_from(entity) - new_owner = CollaboratorOrm.find_or_create(entity.owner) - db_owner = CollaboratorOrm.first(id: new_owner.id) - - stored_repo = create( - origin_id: entity.origin_id, - name: entity.name, - size: entity.size, - git_url: entity.git_url, - owner: db_owner, - ) - - db_contributors = entity.contributors.each do |contrib| - stored_contrib = CollaboratorOrm.find_or_create(contrib) - contrib = CollaboratorOrm.first(id: stored_contrib.id) - stored_repo.add_contributor(contrib) - end - - stored_repo.to_entity - end - - def to_entity(entity_class = CodePraise::Entity::Repo) - entity_class.new( - id: id, - origin_id: origin_id, - name: name, - size: size, - git_url: git_url, - owner: owner.to_entity, - contributors: contributors.map(&:to_entity) - ) - end - end - end -end diff --git a/lib/github_api.rb b/infrastructure/github/github_api.rb similarity index 100% rename from lib/github_api.rb rename to infrastructure/github/github_api.rb diff --git a/lib/init.rb b/infrastructure/github/init.rb similarity index 65% rename from lib/init.rb rename to infrastructure/github/init.rb index 61e803a..bda26ca 100644 --- a/lib/init.rb +++ b/infrastructure/github/init.rb @@ -1,4 +1,3 @@ # frozen_string_literal: false require_relative 'github_api.rb' -require_relative 'mappers/init.rb' diff --git a/infrastructure/init.rb b/infrastructure/init.rb index c3a3aaf..8b9b4d3 100644 --- a/infrastructure/init.rb +++ b/infrastructure/init.rb @@ -1,3 +1,6 @@ # frozen_string_literal: false -require_relative 'db/orm/init.rb' +folders = %w[github database/orm] +folders.each do |folder| + require_relative "#{folder}/init.rb" +end \ No newline at end of file diff --git a/init.rb b/init.rb index 54f3da9..c78bdf0 100644 --- a/init.rb +++ b/init.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -folders = %w[config entities infrastructure lib] +folders = %w[config infrastructure domain] folders.each do |folder| require_relative "#{folder}/init.rb" end diff --git a/spec/api_spec.rb b/spec/api_spec.rb index 44033e3..92755db 100644 --- a/spec/api_spec.rb +++ b/spec/api_spec.rb @@ -22,20 +22,37 @@ Rake::Task['db:reset'].invoke end - it 'HAPPY: should retrieve and store repo and collaborators' do - post "#{API_VER}/repo/#{USERNAME}/#{REPO_NAME}" - _(last_response.status).must_equal 200 - repo_data = JSON.parse last_response.body - _(repo_data.size).must_be :>, 0 + describe "POSTting to create entities from Github" do + it 'HAPPY: should retrieve and store repo and collaborators' do + post "#{API_VER}/repo/#{USERNAME}/#{REPO_NAME}" + _(last_response.status).must_equal 201 + _(last_response.header['Location'].size).must_be :>, 0 + repo_data = JSON.parse last_response.body + _(repo_data.size).must_be :>, 0 + end + + it 'SAD: should report error if no Github repo found' do + post "#{API_VER}/repo/#{USERNAME}/sad_repo_name" + _(last_response.status).must_equal 404 + end end - it 'HAPPY: should find stored repo and collaborators' do - post "#{API_VER}/repo/#{USERNAME}/#{REPO_NAME}" - - get "#{API_VER}/repo/#{USERNAME}/#{REPO_NAME}" - _(last_response.status).must_equal 200 - repo_data = JSON.parse last_response.body - _(repo_data.size).must_be :>, 0 + describe "GETing database entities" do + before do + post "#{API_VER}/repo/#{USERNAME}/#{REPO_NAME}" + end + + it 'HAPPY: should find stored repo and collaborators' do + get "#{API_VER}/repo/#{USERNAME}/#{REPO_NAME}" + _(last_response.status).must_equal 200 + repo_data = JSON.parse last_response.body + _(repo_data.size).must_be :>, 0 + end + + it 'SAD: should report error if no database repo entity found' do + get "#{API_VER}/repo/#{USERNAME}/sad_repo_name" + _(last_response.status).must_equal 404 + end end end end