diff --git a/resources/functions/article/find_article_by_id.sql b/resources/functions/article/find_article_by_id.sql new file mode 100644 index 0000000..ebe2c50 --- /dev/null +++ b/resources/functions/article/find_article_by_id.sql @@ -0,0 +1,18 @@ +create or replace function find_article_by_id(in id uuid, out resource json) language plpgsql as +$$ +declare + _id alias for id; +begin + select to_json(t) + from ( + select + a.*, + find_citizen_by_id(a.created_by_id) as created_by + into resource + from article as a + where a.id = _id + ) as t; +end; +$$; + +-- drop function if exists find_article_by_id(uuid, out json); \ No newline at end of file diff --git a/resources/functions/article/find_last_article_by_version_id.sql b/resources/functions/article/find_last_article_by_version_id.sql new file mode 100644 index 0000000..795b5aa --- /dev/null +++ b/resources/functions/article/find_last_article_by_version_id.sql @@ -0,0 +1,20 @@ +create or replace function find_last_article_by_version_id(in version_id uuid, out resource json) language plpgsql as +$$ +declare + _version_id alias for version_id; +begin + select to_json(t) + from ( + select + a.*, + find_citizen_by_id(a.created_by_id) as created_by + into resource + from article as a + where a.version_id = _version_id + order by a.version_number desc + limit 1 + ) as t; +end; +$$; + +-- drop function if exists find_last_article_by_version_id(uuid, inout json); \ No newline at end of file diff --git a/resources/functions/article/upsert_article.sql b/resources/functions/article/upsert_article.sql new file mode 100644 index 0000000..4c3090c --- /dev/null +++ b/resources/functions/article/upsert_article.sql @@ -0,0 +1,23 @@ +create or replace procedure upsert_article(inout resource json) + language plpgsql as +$$ +declare + new_id uuid; +begin + insert into article (version_id, created_by_id, title, annonymous, content, description, tags) + select + version_id, + (resource#>>'{created_by, id}')::uuid, + title, + annonymous, + content, + description, + tags + from json_populate_record(null::article, resource) + returning id into new_id; + + select find_article_by_id(new_id) into resource; +end; +$$; + +-- drop procedure if exists upsert_article(inout json); \ No newline at end of file diff --git a/resources/functions/citizen/find_citizen_by_id.sql b/resources/functions/citizen/find_citizen_by_id.sql index 3b8e675..c2f5b57 100644 --- a/resources/functions/citizen/find_citizen_by_id.sql +++ b/resources/functions/citizen/find_citizen_by_id.sql @@ -7,7 +7,7 @@ begin from ( select z.*, - find_user_by_id(z.user_id) + find_user_by_id(z.user_id) as "user" from citizen as z where z.id = _id ) as t; diff --git a/resources/functions/citizen/find_citizen_by_user_id.sql b/resources/functions/citizen/find_citizen_by_user_id.sql index 3d62d1c..001a5ee 100644 --- a/resources/functions/citizen/find_citizen_by_user_id.sql +++ b/resources/functions/citizen/find_citizen_by_user_id.sql @@ -7,7 +7,7 @@ begin from ( select z.*, - find_user_by_id(z.user_id) + find_user_by_id(z.user_id) as "user" from citizen as z where z.user_id = _user_id ) as t; diff --git a/resources/functions/citizen/upsert_citizen.sql b/resources/functions/citizen/upsert_citizen.sql index 0889531..c9fd132 100644 --- a/resources/functions/citizen/upsert_citizen.sql +++ b/resources/functions/citizen/upsert_citizen.sql @@ -21,10 +21,7 @@ begin follow_annonymous = excluded.follow_annonymous returning id into new_id; - select to_json(z) - into resource - from citizen as z - where z.id = new_id; + select find_citizen_by_id(new_id) into resource; end; $$; diff --git a/resources/functions/user/find_user_by_id.sql b/resources/functions/user/find_user_by_id.sql index 0e2a5d1..99fdaa1 100644 --- a/resources/functions/user/find_user_by_id.sql +++ b/resources/functions/user/find_user_by_id.sql @@ -9,4 +9,4 @@ begin end; $$; --- drop function if exists find_user_by_id(uuid, inout json); \ No newline at end of file +-- drop function if exists find_user_by_id(uuid, out json); \ No newline at end of file diff --git a/resources/functions/user/insert_user.sql b/resources/functions/user/insert_user.sql index 803b268..b629b37 100644 --- a/resources/functions/user/insert_user.sql +++ b/resources/functions/user/insert_user.sql @@ -11,9 +11,7 @@ begin from json_populate_record(null::"user", resource) returning id into new_id; - select to_json(u) into resource - from "user" as u - where u.id = new_id; + select find_user_by_id(new_id) into resource; end; $$; diff --git a/resources/sql/migrations/0000-init_schema.up.sql b/resources/sql/migrations/0000-init_schema.up.sql index 960c2e9..0fadcbe 100644 --- a/resources/sql/migrations/0000-init_schema.up.sql +++ b/resources/sql/migrations/0000-init_schema.up.sql @@ -52,13 +52,28 @@ create table moderator user_id uuid not null references "user" (id) ); --- Article & Contitution +-- Article & Constitution -create or replace function generate_version_number(tablename regclass, version_id uuid) returns int +create or replace function generate_version_number(tablename regclass, version_id uuid, out generated_number int) language plpgsql as $$ +declare + _version_id alias for version_id; begin - return random(); -- TODO + if (tablename = 'article'::regclass) then + select version_number+1 + into generated_number + from article as t + where t.version_id = _version_id + order by version_number + limit 1; + else + raise exception '% is not implemented', tablename::text; + end if; + + if not found then + generated_number := 1; + end if; end; $$; @@ -67,6 +82,7 @@ create or replace function set_version_number() returns trigger $$ begin new.version_number = generate_version_number(tg_table_name::regclass, new.version_id); + return new; end; $$; @@ -76,18 +92,20 @@ create table article created_at timestamptz default now() not null, created_by_id uuid not null references citizen (id), version_id uuid default uuid_generate_v4() not null, - version_number int not null unique, + version_number int not null, title text not null, annonymous boolean default false not null, content text not null check ( content != '' ), description text, - tags varchar(32)[] default '{}' not null + tags varchar(32)[] default '{}' not null, + unique (version_id, version_number) ); create trigger generate_version_number_trigger before insert on article -execute procedure set_version_number(); + for each row +execute function set_version_number(); create table constitution ( diff --git a/resources/tests/article.sql b/resources/tests/article.sql new file mode 100644 index 0000000..93dd712 --- /dev/null +++ b/resources/tests/article.sql @@ -0,0 +1,53 @@ +do +$$ +declare + created_user json := '{"username": "george", "plain_password": "azerty"}'; + _user_id uuid; + _citizen_id uuid; + created_citizen json := '{"name": {"first_name":"George", "last_name":"MICHEL"}, "birthday": "2001-01-01"}'; + created_article json := '{"version_id":"933b6a1b-50c9-42b6-989f-c02a57814ef9", "title": "Love the world", "annonymous": false, "content": "bla bal bla", "tags": ["love", "test"]}'; + selected_article json; +begin + -- insert user for context + call insert_user(created_user); + _user_id := created_user->>'id'; + created_citizen := jsonb_set(created_citizen::jsonb, '{user}'::text[], jsonb_build_object('id', _user_id::text), true)::json; + assert created_citizen#>>'{user, id}' = _user_id::text, format('userId in citizen must be the same as user, %s = %s', created_citizen#>>'{user, id}', _user_id::text); + + -- insert new citizen for context + call upsert_citizen(created_citizen); + _citizen_id := created_citizen->>'id'; + created_article := jsonb_set(created_article::jsonb, '{created_by}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json; + assert created_article#>>'{created_by, id}' = _citizen_id::text, format('citizenId in article must be the same as citizen, %s != %s', created_article#>>'{created_by, id}', _citizen_id::text); + + -- upsert article + call upsert_article(created_article); + assert created_article->>'version_id' is not null, 'version_id should not be null'; + assert (created_article->>'version_number')::int = 1, format('version_number must be equal to 1, %s instead', created_article->>'version_number'); + -- try tu create new version + call upsert_article(created_article); + assert (created_article->>'version_number')::int = 2, format('version_number must be equal to 2, %s instead', created_article->>'version_number'); + + -- get article by id and check the title + select find_article_by_id((created_article->>'id')::uuid) into selected_article; + assert selected_article->>'title' = 'Love the world', format('title must be "Love the world", %s', selected_article->>'title'); + + -- get article by version_id and check the title + select find_last_article_by_version_id((created_article->>'version_id')::uuid) into selected_article; + assert selected_article->>'title' = 'Love the world', format('title must be "Love the world", %s', selected_article->>'title'); + assert (selected_article->>'version_number')::int = 2, format('version_id must be 2, %s instead', selected_article->>'version_number'); + -- check if user id is returned + assert (selected_article#>>'{created_by, user, id}')::uuid = _user_id, format('user_id must be %s instead of %s', _user_id, (selected_article#>>'{created_by, user, id}')::uuid); + + -- delete article and context + delete from article; + delete from citizen; + delete from "user"; + + -- check if find by id return null if article not exist + select find_citizen_by_user_id((created_citizen->>'id')::uuid) into selected_article; + assert selected_article is null, format('article must be null if not exist, %s', selected_article); + + raise notice 'article test pass'; +end; +$$; diff --git a/resources/tests/user.sql b/resources/tests/user.sql index ec89e82..8e5a5ea 100644 --- a/resources/tests/user.sql +++ b/resources/tests/user.sql @@ -8,7 +8,7 @@ begin -- Insert user and check if username and password is correct call insert_user(created_user); assert created_user->>'username' = 'george', 'username must be george'; - assert created_user->>'password' is not null, 'password must be generated'; + assert created_user->>'password' is null, 'password must not be returned'; -- get user by there id and check the username is correct select find_user_by_id((created_user->>'id')::uuid) into selected_user;