From c2beed416e793d24e97e9b0824603db6f8457d69 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Mon, 16 Sep 2019 22:47:37 +0200 Subject: [PATCH] replace Article entity by Article request for the HTTP request Add draft --- .../kotlin/fr/dcproject/entity/Article.kt | 2 + .../fr/dcproject/entity/Constitution.kt | 2 + .../fr/dcproject/entity/request/Article.kt | 41 +++++++++ .../fr/dcproject/entity/request/Request.kt | 3 + .../kotlin/fr/dcproject/routes/Article.kt | 5 +- src/main/resources/openApi.yaml | 16 ++++ .../resources/sql/fixtures/04-article.sql | 2 +- .../sql/functions/article/find_articles.sql | 4 +- .../find_last_article_by_version_id.sql | 2 +- .../sql/functions/article/upsert_article.sql | 49 ++++++---- .../constitution/upsert_constitution.sql | 6 +- .../sql/migrations/0000-init_schema.up.sql | 90 ++++++++++--------- src/test/sql/article.sql | 8 +- 13 files changed, 157 insertions(+), 73 deletions(-) create mode 100644 src/main/kotlin/fr/dcproject/entity/request/Article.kt create mode 100644 src/main/kotlin/fr/dcproject/entity/request/Request.kt diff --git a/src/main/kotlin/fr/dcproject/entity/Article.kt b/src/main/kotlin/fr/dcproject/entity/Article.kt index 1e003cb..ee5b863 100644 --- a/src/main/kotlin/fr/dcproject/entity/Article.kt +++ b/src/main/kotlin/fr/dcproject/entity/Article.kt @@ -10,6 +10,8 @@ class Article( var content: String?, var description: String?, var tags: List = emptyList(), + var draft: Boolean = false, + var lastVersion: Boolean = false, createdBy: Citizen? ): UuidEntity(id), diff --git a/src/main/kotlin/fr/dcproject/entity/Constitution.kt b/src/main/kotlin/fr/dcproject/entity/Constitution.kt index 935c037..4449f2b 100644 --- a/src/main/kotlin/fr/dcproject/entity/Constitution.kt +++ b/src/main/kotlin/fr/dcproject/entity/Constitution.kt @@ -8,6 +8,8 @@ class Constitution( var title: String?, var anonymous: Boolean? = true, var titles: List = listOf(), + var draft: Boolean = false, + var lastVersion: Boolean = false, createdBy: Citizen? ): UuidEntity(id), EntityVersioning<UUID, Int> by UuidEntityVersioning(), diff --git a/src/main/kotlin/fr/dcproject/entity/request/Article.kt b/src/main/kotlin/fr/dcproject/entity/request/Article.kt new file mode 100644 index 0000000..a7630e8 --- /dev/null +++ b/src/main/kotlin/fr/dcproject/entity/request/Article.kt @@ -0,0 +1,41 @@ +package fr.dcproject.entity.request + +import fr.dcproject.entity.Citizen +import java.util.* +import fr.dcproject.entity.Article as ArticleEntity + +class Article( + val id: UUID?, + val title: String, + val anonymous: Boolean? = true, + val content: String, + val description: String, + val tags: List<String> = emptyList(), + val draft: Boolean = false, + val versionId: UUID? +): + Request { + + fun merge(article: ArticleEntity) { + article.title = this.title + article.content = this.content + article.description = this.description + article.tags = this.tags.distinct() + article.anonymous = this.anonymous + article.draft = this.draft + article.versionId = this.versionId ?: UUID.randomUUID() + } + + fun create(createdBy: Citizen): ArticleEntity { + return ArticleEntity( + id ?: UUID.randomUUID(), + title, + anonymous, + content, + description, + tags, + draft, + createdBy = createdBy + ).apply { this.versionId = this@Article.versionId ?: UUID.randomUUID() } + } +} diff --git a/src/main/kotlin/fr/dcproject/entity/request/Request.kt b/src/main/kotlin/fr/dcproject/entity/request/Request.kt new file mode 100644 index 0000000..2da16d0 --- /dev/null +++ b/src/main/kotlin/fr/dcproject/entity/request/Request.kt @@ -0,0 +1,3 @@ +package fr.dcproject.entity.request + +interface Request \ No newline at end of file diff --git a/src/main/kotlin/fr/dcproject/routes/Article.kt b/src/main/kotlin/fr/dcproject/routes/Article.kt index 42014cd..d52eaa7 100644 --- a/src/main/kotlin/fr/dcproject/routes/Article.kt +++ b/src/main/kotlin/fr/dcproject/routes/Article.kt @@ -14,6 +14,7 @@ import io.ktor.request.receive import io.ktor.response.respond import io.ktor.routing.Route import fr.dcproject.entity.Article as ArticleEntity +import fr.dcproject.entity.request.Article as ArticleEntityRequest import fr.dcproject.repository.Article as ArticleRepository @KtorExperimentalLocationsAPI @@ -53,8 +54,8 @@ fun Route.article(repo: ArticleRepository) { } post<ArticlesPaths.PostArticleRequest> { - val article = call.receive<ArticleEntity>() - article.createdBy = citizen + val request = call.receive<ArticleEntityRequest>() + val article = request.create(citizen) assertCan(CREATE, article) diff --git a/src/main/resources/openApi.yaml b/src/main/resources/openApi.yaml index 721ff06..fde5d8c 100644 --- a/src/main/resources/openApi.yaml +++ b/src/main/resources/openApi.yaml @@ -560,6 +560,13 @@ components: version_id: $ref: '#/components/schemas/UUID' + lastVersion: + properties: + last_version: + type: boolean + required: false + default: false + Paginated: properties: result: @@ -696,6 +703,10 @@ components: type: boolean required: false default: true + draft: + type: boolean + required: false + default: false - $ref: '#/components/schemas/versionId' ArticleResponse: type: object @@ -704,6 +715,7 @@ components: - $ref: '#/components/schemas/UuidEntity' - $ref: '#/components/schemas/CreatedBy' - $ref: '#/components/schemas/CreatedAt' + - $ref: '#/components/schemas/lastVersion' ArticleRequest: $ref: '#/components/schemas/ArticleBase' @@ -727,6 +739,10 @@ components: type: boolean required: false default: true + draft: + type: boolean + required: false + default: false - $ref: '#/components/schemas/versionId' ConstitutionResponse: type: object diff --git a/src/main/resources/sql/fixtures/04-article.sql b/src/main/resources/sql/fixtures/04-article.sql index ee260c2..719c03e 100644 --- a/src/main/resources/sql/fixtures/04-article.sql +++ b/src/main/resources/sql/fixtures/04-article.sql @@ -13,7 +13,7 @@ begin delete from article_relations; delete from article; - insert into article (id, version_id, created_by_id, title, anonymous, content, description, tags, created_at, is_draft) + insert into article (id, version_id, created_by_id, title, anonymous, content, description, tags, created_at, draft) select uuid_in(md5('article'||row_number() over ())::cstring), uuid_in(md5('article_v'||row_number() over () % (_citizen_count / 2))::cstring), diff --git a/src/main/resources/sql/functions/article/find_articles.sql b/src/main/resources/sql/functions/article/find_articles.sql index 8d9464a..e242d14 100644 --- a/src/main/resources/sql/functions/article/find_articles.sql +++ b/src/main/resources/sql/functions/article/find_articles.sql @@ -9,7 +9,7 @@ create or replace function find_articles( ) language plpgsql as $$ begin - select json_agg(t), (select count(id) from article a where (_search is null or _search = '' or a ==> dsl.multi_match('{title^3, content, description}', _search)) and a.is_last_version = true) + select json_agg(t), (select count(id) from article a where (_search is null or _search = '' or a ==> dsl.multi_match('{title^3, content, description}', _search)) and a.last_version = true) into resource, total from ( select @@ -21,7 +21,7 @@ begin _search is null or _search = '' or a ==> dsl.multi_match('{title^3, content, description}', _search) - ) and a.is_last_version = true + ) and a.last_version = true order by _score desc, case direction when 'asc' then diff --git a/src/main/resources/sql/functions/article/find_last_article_by_version_id.sql b/src/main/resources/sql/functions/article/find_last_article_by_version_id.sql index 2f03dee..6e0c923 100644 --- a/src/main/resources/sql/functions/article/find_last_article_by_version_id.sql +++ b/src/main/resources/sql/functions/article/find_last_article_by_version_id.sql @@ -11,7 +11,7 @@ begin into resource from article as a where a.version_id = _version_id - and a.is_draft = false + and a.draft = false and a.deleted_at is null order by a.version_number desc limit 1 diff --git a/src/main/resources/sql/functions/article/upsert_article.sql b/src/main/resources/sql/functions/article/upsert_article.sql index ae749c2..e4db6ee 100644 --- a/src/main/resources/sql/functions/article/upsert_article.sql +++ b/src/main/resources/sql/functions/article/upsert_article.sql @@ -4,29 +4,46 @@ $$ declare new_id uuid; _id_exist boolean; + _existing_draft article = ( + select a from article a + where a.version_id = (resource->>'version_id')::uuid + and a.draft = true + ); begin -- check if version id already exist select count(*) >= 1 into _id_exist from article where (resource->>'id')::uuid is not null - and id = (resource->>'id')::uuid --- and draft = false - ; + and id = (resource->>'id')::uuid; - insert into article (id, version_id, created_by_id, title, anonymous, content, description, tags) - select - case when _id_exist then uuid_generate_v4() - else coalesce(id, uuid_generate_v4()) end, - coalesce(version_id, uuid_generate_v4()), - (resource#>>'{created_by, id}')::uuid, - title, - anonymous, - content, - description, - tags - from json_populate_record(null::article, resource) - returning id into new_id; + if (_existing_draft.id is not null) then + update article a2 set + title = a.title, + anonymous = a.anonymous, + content = a.content, + description = a.description, + tags = a.tags, + draft = a.draft + from json_populate_record(null::article, resource) a + where a2.id = (_existing_draft.id)::uuid + returning a2.id into new_id; + else + insert into article (id, version_id, created_by_id, title, anonymous, content, description, tags, draft) + select + case when _id_exist then uuid_generate_v4() + else coalesce(id, uuid_generate_v4()) end, + coalesce(version_id, uuid_generate_v4()), + (resource#>>'{created_by, id}')::uuid, + title, + anonymous, + content, + description, + tags, + draft + from json_populate_record(null::article, resource) + returning id into new_id; + end if; if resource->>'relations' is not null then delete from article_relations diff --git a/src/main/resources/sql/functions/constitution/upsert_constitution.sql b/src/main/resources/sql/functions/constitution/upsert_constitution.sql index 1d93364..3e8cdf6 100644 --- a/src/main/resources/sql/functions/constitution/upsert_constitution.sql +++ b/src/main/resources/sql/functions/constitution/upsert_constitution.sql @@ -13,11 +13,7 @@ begin into _id_exist from constitution where (resource->>'id')::uuid is not null - and id = (resource->>'id')::uuid --- and draft = false - ; - - raise notice '%', _id_exist; + and id = (resource->>'id')::uuid; insert into constitution (id, version_id, created_by_id, title, anonymous) select diff --git a/src/main/resources/sql/migrations/0000-init_schema.up.sql b/src/main/resources/sql/migrations/0000-init_schema.up.sql index 6316c77..271c3ea 100644 --- a/src/main/resources/sql/migrations/0000-init_schema.up.sql +++ b/src/main/resources/sql/migrations/0000-init_schema.up.sql @@ -94,12 +94,14 @@ declare begin if (tablename = 'article'::regclass) then update article a - set is_last_version = false - where a.version_id = _version_id and a.is_last_version = true; + set last_version = false + where a.version_id = _version_id + and a.last_version = true; elseif (tablename = 'constitution'::regclass) then update constitution c - set is_last_version = false - where c.version_id = _version_id and c.is_last_version = true; + set last_version = false + where c.version_id = _version_id + and c.last_version = true; else raise exception '% is not implemented', tablename::text; end if; @@ -116,28 +118,32 @@ begin if (tablename = 'article'::regclass) then update article a1 - set is_last_version = true + set last_version = true from ( - select id from article a2 + select id + from article a2 where a2.version_id = _version_id - and a2.is_draft = false + and a2.draft = false and a2.deleted_at is null order by version_number desc limit 1 ) as a3 - where a1.version_id = _version_id and a1.id = a3.id; + where a1.version_id = _version_id + and a1.id = a3.id; elseif (tablename = 'constitution'::regclass) then update constitution c1 - set is_last_version = true + set last_version = true from ( - select id from constitution c2 + select id + from constitution c2 where c2.version_id = _version_id - and c2.is_draft = false + and c2.draft = false and c2.deleted_at is null order by version_number desc limit 1 ) as c3 - where c1.version_id = _version_id and c1.id = c3.id; + where c1.version_id = _version_id + and c1.id = c3.id; else raise exception '% is not implemented', tablename::text; end if; @@ -158,11 +164,11 @@ create or replace function set_to_last_version() returns trigger language plpgsql as $$ begin - if (new.is_draft = false and new.deleted_at is null) then + if (new.draft = false and new.deleted_at is null) then perform set_all_version_to_old(tg_table_name::regclass, new.version_id); - new.is_last_version = true; + new.last_version = true; else - new.is_last_version = false; + new.last_version = false; end if; return new; end; @@ -172,7 +178,7 @@ create or replace function set_last_version() returns trigger language plpgsql as $$ begin - if (new.is_draft != old.is_draft or new.deleted_at != old.deleted_at) then + if (new.draft != old.draft or new.deleted_at != old.deleted_at) then perform set_correct_last_version(tg_table_name::regclass, new.version_id); end if; return new; @@ -184,23 +190,23 @@ $$; ------------- create table article ( - id uuid default uuid_generate_v4() not null primary key, - 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, - title text not null check ( length(title) < 128 ), - anonymous boolean default false not null, - content text not null check ( content != '' and length(content) < 4096 ), - description text null check ( description != '' and length(description) < 4096 ), - tags varchar(32)[] default '{}' not null, - deleted_at timestamptz default null null, - is_draft boolean default false not null, - is_last_version boolean default false not null, + id uuid default uuid_generate_v4() not null primary key, + 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, + title text not null check ( length(title) < 128 ), + anonymous boolean default false not null, + content text not null check ( content != '' and length(content) < 4096 ), + description text null check ( description != '' and length(description) < 4096 ), + tags varchar(32)[] default '{}' not null, + deleted_at timestamptz default null null, + draft boolean default false not null, + last_version boolean default false not null, unique (version_id, version_number) ); -create unique index last_version_article_idx on article (is_last_version, version_id) where is_last_version = true; +create unique index last_version_article_idx on article (last_version, version_id) where last_version = true; create trigger generate_version_number_trigger before insert @@ -226,21 +232,21 @@ execute function set_last_version(); create table constitution ( - id uuid default uuid_generate_v4() not null primary key, - 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, - title text not null check ( length(title) < 128 ), - intro text null check ( length(intro) < 4096 ), - anonymous boolean default false not null, - deleted_at timestamptz default null null, - is_draft boolean default false not null, - is_last_version boolean default false not null, + id uuid default uuid_generate_v4() not null primary key, + 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, + title text not null check ( length(title) < 128 ), + intro text null check ( length(intro) < 4096 ), + anonymous boolean default false not null, + deleted_at timestamptz default null null, + draft boolean default false not null, + last_version boolean default false not null, unique (version_id, version_number) ); -create unique index last_version_constitution_idx on constitution (is_last_version, version_id) where is_last_version = true; +create unique index last_version_constitution_idx on constitution (last_version, version_id) where last_version = true; create trigger generate_version_number_trigger before insert diff --git a/src/test/sql/article.sql b/src/test/sql/article.sql index 43e9653..372a750 100644 --- a/src/test/sql/article.sql +++ b/src/test/sql/article.sql @@ -28,13 +28,13 @@ begin select upsert_article(created_article) into 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'); - assert (created_article->>'is_last_version')::bool = true, 'The first insert must be set to the last version'; + assert (created_article->>'last_version')::bool = true, 'The first insert must be set to the last version'; first_article_id = (created_article->>'id')::uuid; -- try to create new version select upsert_article(created_article) into created_article_v2; assert (created_article_v2->>'version_number')::int = 2, format('version_number must be equal to 2, %s instead', created_article_v2->>'version_number'); - assert (created_article_v2->>'is_last_version')::bool = true, 'The second insert must be set to the last version'; + assert (created_article_v2->>'last_version')::bool = true, 'The second insert must be set to the last version'; second_article_id = (created_article_v2->>'id')::uuid; -- get articles versions by version_id @@ -60,14 +60,14 @@ begin -- update to draft, then the last_version column must be change update article - set is_draft = true + set draft = true where id = second_article_id; select find_last_article_by_version_id((created_article_v2->>'version_id')::uuid) into selected_article; assert (selected_article->>'version_number')::int = 1, format('version_id must be 1, %s instead', selected_article->>'version_number'); update article - set is_draft = false + set draft = false where id = second_article_id; select find_last_article_by_version_id((created_article_v2->>'version_id')::uuid) into selected_article;