Skip to content

Commit

Permalink
Add service create_owner
Browse files Browse the repository at this point in the history
  • Loading branch information
straight-shoota committed May 6, 2020
1 parent 34611fc commit 0a0eda5
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 1 deletion.
24 changes: 24 additions & 0 deletions db/migrations/20200503132444_create_table_owners.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- migrate:up
CREATE TABLE owners (
id bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
resolver public.repo_resolver NOT NULL,
slug public.citext NOT NULL,
name TEXT,
shards_count INT,
created_at timestamp with time zone DEFAULT now() NOT NULL,
updated_at timestamp with time zone DEFAULT now() NOT NULL
);

CREATE TRIGGER set_timestamp BEFORE UPDATE ON public.owners FOR EACH ROW EXECUTE PROCEDURE public.trigger_set_timestamp();

CREATE UNIQUE INDEX owners_resolver_slug_idx ON public.owners USING btree (resolver, slug);
ALTER TABLE owners
ADD CONSTRAINT owners_resolver_slug_uniq UNIQUE USING INDEX owners_resolver_slug_idx;

ALTER TABLE repos
ADD COLUMN owner_id bigint REFERENCES owners(id);

-- migrate:down
ALTER TABLE repos DROP COLUMN owner_id;

DROP TABLE owners;
64 changes: 63 additions & 1 deletion db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,35 @@ ALTER TABLE public.files ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (
);


--
-- Name: owners; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.owners (
id bigint NOT NULL,
resolver public.repo_resolver NOT NULL,
slug public.citext NOT NULL,
name text,
shards_count integer,
created_at timestamp with time zone DEFAULT now() NOT NULL,
updated_at timestamp with time zone DEFAULT now() NOT NULL
);


--
-- Name: owners_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

ALTER TABLE public.owners ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (
SEQUENCE NAME public.owners_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1
);


--
-- Name: releases; Type: TABLE; Schema: public; Owner: -
--
Expand Down Expand Up @@ -615,6 +644,7 @@ CREATE TABLE public.repos (
updated_at timestamp with time zone DEFAULT now() NOT NULL,
metadata jsonb DEFAULT '{}'::jsonb NOT NULL,
sync_failed_at timestamp with time zone,
owner_id bigint,
CONSTRAINT repos_obsolete_role_shard_id_null CHECK (((role <> 'obsolete'::public.repo_role) OR (shard_id IS NULL))),
CONSTRAINT repos_resolvers_service_url CHECK (((NOT (resolver = ANY (ARRAY['github'::public.repo_resolver, 'gitlab'::public.repo_resolver, 'bitbucket'::public.repo_resolver]))) OR ((url OPERATOR(public.~) '^[A-Za-z0-9_\-.]{1,100}/[A-Za-z0-9_\-.]{1,100}$'::public.citext) AND (url OPERATOR(public.!~~) '%.git'::public.citext)))),
CONSTRAINT repos_shard_id_null_role CHECK (((shard_id IS NOT NULL) OR (role = 'canonical'::public.repo_role) OR (role = 'obsolete'::public.repo_role)))
Expand Down Expand Up @@ -824,6 +854,22 @@ ALTER TABLE ONLY public.categories
ADD CONSTRAINT name_uniq UNIQUE (name);


--
-- Name: owners owners_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.owners
ADD CONSTRAINT owners_pkey PRIMARY KEY (id);


--
-- Name: owners owners_resolver_slug_uniq; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.owners
ADD CONSTRAINT owners_resolver_slug_uniq UNIQUE (resolver, slug);


--
-- Name: releases releases_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
Expand Down Expand Up @@ -975,6 +1021,13 @@ CREATE TRIGGER releases_only_one_latest_release BEFORE INSERT OR UPDATE OF lates
CREATE TRIGGER set_timestamp BEFORE UPDATE ON public.dependencies FOR EACH ROW EXECUTE FUNCTION public.trigger_set_timestamp();


--
-- Name: owners set_timestamp; Type: TRIGGER; Schema: public; Owner: -
--

CREATE TRIGGER set_timestamp BEFORE UPDATE ON public.owners FOR EACH ROW EXECUTE FUNCTION public.trigger_set_timestamp();


--
-- Name: releases set_timestamp; Type: TRIGGER; Schema: public; Owner: -
--
Expand Down Expand Up @@ -1051,6 +1104,14 @@ ALTER TABLE ONLY public.releases
ADD CONSTRAINT releases_shard_id_fkey FOREIGN KEY (shard_id) REFERENCES public.shards(id) ON DELETE CASCADE;


--
-- Name: repos repos_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.repos
ADD CONSTRAINT repos_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES public.owners(id);


--
-- Name: repos repos_shard_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
Expand Down Expand Up @@ -1114,4 +1175,5 @@ INSERT INTO public.schema_migrations (version) VALUES
('20191102100059'),
('20191106093828'),
('20191115142944'),
('20191122122940');
('20191122122940'),
('20200503132444');
22 changes: 22 additions & 0 deletions spec/owner_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require "spec"
require "../src/repo/owner"

describe Repo::Owner do
describe ".from_repo_ref" do
it "returns nil for git" do
Repo::Owner.from_repo_ref(Repo::Ref.new("git", "foo/bar")).should be_nil
end

it "creates owner for github" do
Repo::Owner.from_repo_ref(Repo::Ref.new("github", "foo/bar")).should eq Repo::Owner.new("github", "foo")
end

it "creates owner for gitlab" do
Repo::Owner.from_repo_ref(Repo::Ref.new("gitlab", "foo/bar")).should eq Repo::Owner.new("gitlab", "foo")
end

it "creates owner for bitbucket" do
Repo::Owner.from_repo_ref(Repo::Ref.new("bitbucket", "foo/bar")).should eq Repo::Owner.new("bitbucket", "foo")
end
end
end
62 changes: 62 additions & 0 deletions spec/service/create_owner_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require "spec"
require "../support/db"
require "../../src/service/create_owner"

describe Service::CreateOwner do
describe "#perform" do
it "creates owner" do
transaction do |db|
repo_ref = Repo::Ref.new("github", "foo/bar")
service = Service::CreateOwner.new(db, repo_ref)
db.get_owner?("github", "foo").should be_nil
db.get_owner?(repo_ref).should be_nil
service.perform
db.get_owner?("github", "foo").should eq Repo::Owner.new("github", "foo", shards_count: 0)
db.get_owner?(repo_ref).should be_nil
end
end

it "assigns owner to repo" do
transaction do |db|
repo_ref = Repo::Ref.new("github", "foo/bar")
db.create_repo(Repo.new(repo_ref, shard_id: nil))
db.get_owner?(repo_ref).should be_nil

Service::CreateOwner.new(db, repo_ref).perform

db.get_owner?(repo_ref).should eq Repo::Owner.new("github", "foo", shards_count: 1)
end
end

it "picks up existing owner" do
transaction do |db|
repo_ref = Repo::Ref.new("github", "foo/bar")
db.create_repo(Repo.new(repo_ref, shard_id: nil))
db.create_owner(Repo::Owner.new("github", "foo"))
db.get_owner?(repo_ref).should be_nil

Service::CreateOwner.new(db, repo_ref).perform

db.get_owner?(repo_ref).should eq Repo::Owner.new("github", "foo", shards_count: 1)
end
end

it "sets shards count" do
transaction do |db|
repo_ref = Repo::Ref.new("github", "foo/bar")
db.create_repo(Repo.new(repo_ref, shard_id: nil))
Service::CreateOwner.new(db, repo_ref).perform

db.get_owner?(repo_ref).should eq Repo::Owner.new("github", "foo", shards_count: 1)

repo_ref_baz = Repo::Ref.new("github", "foo/baz")
db.create_repo(Repo.new(repo_ref_baz, shard_id: nil))
owner = Service::CreateOwner.new(db, repo_ref_baz).perform
owner = owner.not_nil!

db.get_owned_repos(owner.id).map(&.ref).should eq [repo_ref, repo_ref_baz]
db.get_owner?(repo_ref).should eq Repo::Owner.new("github", "foo", shards_count: 2)
end
end
end
end
94 changes: 94 additions & 0 deletions src/db.cr
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,23 @@ class ShardsDB
end
end

def get_owned_repos(owner_id : Int64)
results = connection.query_all <<-SQL, owner_id, as: {Int64, String, String, Int64?, String, String, Time?, Time?}
SELECT
id, resolver::text, url::text, shard_id, role::text, metadata::text, synced_at, sync_failed_at
FROM
repos
WHERE
owner_id = $1
ORDER BY
created_at
SQL

results.map do |id, resolver, url, shard_id, role, metadata, synced_at, sync_failed_at|
Repo.new(resolver, url, shard_id, role, Repo::Metadata.from_json(metadata), synced_at, sync_failed_at, id: id)
end
end

def create_shard(shard : Shard)
shard_id = connection.query_one <<-SQL, shard.name, shard.qualifier, shard.description, shard.archived_at, as: Int64
INSERT INTO shards
Expand Down Expand Up @@ -449,6 +466,83 @@ class ShardsDB
results
end

def create_owner(owner : Repo::Owner)
connection.query_one <<-SQL, owner.resolver, owner.slug, owner.name, owner.shards_count, as: Int64
INSERT INTO owners
(resolver, slug, name, shards_count)
VALUES
($1, $2, $3, $4)
RETURNING id
SQL
end

def get_owner?(resolver : String, slug : String)
result = connection.query_one? <<-SQL, resolver, slug, as: {String, String, String?, Int32?, Int64}
SELECT
resolver::text,
slug::text,
name,
shards_count,
id
FROM owners
WHERE resolver = $1
AND slug = $2
SQL

return unless result
resolver, owner, name, shards_count, id = result
Repo::Owner.new(resolver, owner, name, shards_count, id: id)
end

def get_owner?(repo_ref : Repo::Ref)
result = connection.query_one? <<-SQL, repo_ref.resolver, repo_ref.url, as: {String, String, String?, Int32?, Int64}
SELECT
owners.resolver::text,
owners.slug::text,
owners.name,
owners.shards_count,
owners.id
FROM owners
JOIN
repos ON repos.owner_id = owners.id
WHERE repos.resolver = $1
AND repos.url = $2
SQL

return unless result
resolver, owner, name, shards_count, id = result
Repo::Owner.new(resolver, owner, name, shards_count, id: id)
end

def set_owner(repo_ref : Repo::Ref, owner_id : Int64)
connection.exec <<-SQL, repo_ref.resolver, repo_ref.url, owner_id
UPDATE repos
SET
owner_id = $3
WHERE resolver = $1
AND url = $2
SQL
end

def update_owner_shards_count(owner_id : Int64)
connection.exec <<-SQL, owner_id
UPDATE owners
SET
shards_count = (
SELECT
COUNT(*)
FROM
repos
WHERE
owner_id = owners.id
AND
role = 'canonical'
)
WHERE
id = $1
SQL
end

def sql_array(array)
String.build do |io|
array.each_with_index do |category, i|
Expand Down
18 changes: 18 additions & 0 deletions src/repo/owner.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Repo::Owner
property resolver : String
property slug : String
property name : String?
property shards_count : Int32?
property! id : Int64

def initialize(@resolver : String, @slug : String, @name : String? = nil, @shards_count : Int32? = nil, *, @id : Int64? = nil)
end

def self.from_repo_ref(repo_ref : Ref) : Owner?
if owner = repo_ref.owner
new(repo_ref.resolver, owner)
end
end

def_equals_and_hash @resolver, @slug, @name, @shards_count
end
31 changes: 31 additions & 0 deletions src/service/create_owner.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require "../repo/owner"

struct Service::CreateOwner
def initialize(@db : ShardsDB, @repo_ref : Repo::Ref)
end

def perform
owner = Repo::Owner.from_repo_ref(@repo_ref)

return unless owner

db_owner = @db.get_owner?(owner.resolver, owner.slug)

if db_owner
# owner already exists in the database
owner = db_owner
else
# owner does not yet exist, need to insert a new entry
owner.id = @db.create_owner(owner)
end

assign_owner(owner)

owner
end

private def assign_owner(owner)
@db.set_owner(@repo_ref, owner.id)
@db.update_owner_shards_count(owner.id)
end
end

0 comments on commit 0a0eda5

Please sign in to comment.