From bc48dd40894655ebe7ccc410b8871764f5a95aa3 Mon Sep 17 00:00:00 2001 From: jordi fita mas Date: Fri, 7 Apr 2023 21:31:35 +0200 Subject: [PATCH] Replace tag relations with array attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It all started when i wanted to try to filter invoices by multiple tags using an “AND”, instead of “OR” as it was doing until now. But something felt off and seemed to me that i was doing thing much more complex than needed, all to be able to list the tags as a suggestion in the input field—which i am not doing yet. I found this article series[0] exploring different approaches for tagging, which includes the one i was using, and comparing their performance. I have not actually tested it, but it seems that i have chosen the worst option, in both query time and storage. I attempted to try using an array attribute to each table, which is more or less the same they did in the articles but without using a separate relation for tags, and i found out that all the queries were way easier to write, and needed two joins less, so it was a no-brainer. [0]: http://www.databasesoup.com/2015/01/tag-all-things.html --- demo/demo.sql | 1 - deploy/add_contact.sql | 6 +- deploy/add_invoice.sql | 6 +- deploy/add_product.sql | 7 +- deploy/contact.sql | 4 ++ deploy/contact_tag.sql | 31 --------- deploy/edit_contact.sql | 4 +- deploy/edit_invoice.sql | 4 +- deploy/edit_product.sql | 4 +- deploy/invoice.sql | 4 ++ deploy/invoice_tag.sql | 31 --------- deploy/product.sql | 4 ++ deploy/product_tag.sql | 31 --------- deploy/tag.sql | 35 ---------- deploy/tag_contact.sql | 22 ------- deploy/tag_invoice.sql | 21 ------ deploy/tag_product.sql | 22 ------- deploy/tag_relation.sql | 32 --------- pkg/contacts.go | 25 +------- pkg/invoices.go | 25 ++------ pkg/pgtypes.go | 10 +++ pkg/products.go | 14 ++-- revert/contact_tag.sql | 7 -- revert/invoice_tag.sql | 7 -- revert/product_tag.sql | 7 -- revert/tag.sql | 7 -- revert/tag_contact.sql | 7 -- revert/tag_invoice.sql | 7 -- revert/tag_product.sql | 7 -- revert/tag_relation.sql | 7 -- sqitch.plan | 26 +++----- test/add_contact.sql | 33 ++-------- test/add_invoice.sql | 30 ++------- test/add_product.sql | 30 ++------- test/contact.sql | 8 ++- test/contact_tag.sql | 130 ------------------------------------- test/edit_contact.sql | 44 ++----------- test/edit_invoice.sql | 46 ++----------- test/edit_product.sql | 51 +++------------ test/invoice.sql | 8 ++- test/invoice_tag.sql | 136 --------------------------------------- test/product.sql | 8 ++- test/product_tag.sql | 129 ------------------------------------- test/tag.sql | 139 ---------------------------------------- test/tag_contact.sql | 124 ----------------------------------- test/tag_invoice.sql | 130 ------------------------------------- test/tag_product.sql | 124 ----------------------------------- test/tag_relation.sql | 25 -------- verify/contact.sql | 1 + verify/contact_tag.sql | 13 ---- verify/invoice.sql | 1 + verify/invoice_tag.sql | 13 ---- verify/product.sql | 1 + verify/product_tag.sql | 13 ---- verify/tag.sql | 14 ---- verify/tag_contact.sql | 7 -- verify/tag_invoice.sql | 7 -- verify/tag_product.sql | 7 -- verify/tag_relation.sql | 7 -- 59 files changed, 114 insertions(+), 1590 deletions(-) delete mode 100644 deploy/contact_tag.sql delete mode 100644 deploy/invoice_tag.sql delete mode 100644 deploy/product_tag.sql delete mode 100644 deploy/tag.sql delete mode 100644 deploy/tag_contact.sql delete mode 100644 deploy/tag_invoice.sql delete mode 100644 deploy/tag_product.sql delete mode 100644 deploy/tag_relation.sql delete mode 100644 revert/contact_tag.sql delete mode 100644 revert/invoice_tag.sql delete mode 100644 revert/product_tag.sql delete mode 100644 revert/tag.sql delete mode 100644 revert/tag_contact.sql delete mode 100644 revert/tag_invoice.sql delete mode 100644 revert/tag_product.sql delete mode 100644 revert/tag_relation.sql delete mode 100644 test/contact_tag.sql delete mode 100644 test/invoice_tag.sql delete mode 100644 test/product_tag.sql delete mode 100644 test/tag.sql delete mode 100644 test/tag_contact.sql delete mode 100644 test/tag_invoice.sql delete mode 100644 test/tag_product.sql delete mode 100644 test/tag_relation.sql delete mode 100644 verify/contact_tag.sql delete mode 100644 verify/invoice_tag.sql delete mode 100644 verify/product_tag.sql delete mode 100644 verify/tag.sql delete mode 100644 verify/tag_contact.sql delete mode 100644 verify/tag_invoice.sql delete mode 100644 verify/tag_product.sql delete mode 100644 verify/tag_relation.sql diff --git a/demo/demo.sql b/demo/demo.sql index 167c223..824105f 100644 --- a/demo/demo.sql +++ b/demo/demo.sql @@ -41,7 +41,6 @@ values (1, 1, 'Retenció 15 %', -0.15) , (1, 2, 'IVA 4 %', 0.04) ; -alter sequence tag_tag_id_seq restart; alter sequence contact_contact_id_seq restart; select add_contact (1, 'Melcior', '1', 'Rei Blanc', '0732621', 'melcio@reismags.cat', '', 'C/ Principal, 1', 'Shiraz', 'Fars', '1', 'IR', array['pesebre', 'mag']); select add_contact (1, 'Gaspar', '2', 'Rei Ros', '111', 'gaspar@reismags.cat', '', 'C/ Principal, 2', 'Nova Delhi', 'Delhi', '2', 'IN', array['pesebre', 'mag']); diff --git a/deploy/add_contact.sql b/deploy/add_contact.sql index c991cba..86d54fb 100644 --- a/deploy/add_contact.sql +++ b/deploy/add_contact.sql @@ -18,13 +18,11 @@ declare cid integer; cslug uuid; begin - insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) - values (add_contact.company_id, add_contact.business_name, (add_contact.country_code || add_contact.vatin)::vatin, add_contact.trade_name, parse_packed_phone_number(add_contact.phone, add_contact.country_code), add_contact.email, add_contact.web, add_contact.address, add_contact.city, add_contact.province, add_contact.postal_code, add_contact.country_code) + insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, tags) + values (add_contact.company_id, add_contact.business_name, (add_contact.country_code || add_contact.vatin)::vatin, add_contact.trade_name, parse_packed_phone_number(add_contact.phone, add_contact.country_code), add_contact.email, add_contact.web, add_contact.address, add_contact.city, add_contact.province, add_contact.postal_code, add_contact.country_code, add_contact.tags) returning contact_id, slug into cid, cslug; - perform tag_contact(company_id, cid, tags); - return cslug; end $$ diff --git a/deploy/add_invoice.sql b/deploy/add_invoice.sql index 6a224ac..4498243 100644 --- a/deploy/add_invoice.sql +++ b/deploy/add_invoice.sql @@ -10,7 +10,6 @@ -- requires: invoice_product_tax -- requires: next_invoice_number -- requires: tag_name --- requires: tag_invoice begin; @@ -25,12 +24,13 @@ declare ccode text; ipid integer; begin - insert into invoice (company_id, invoice_number, invoice_date, contact_id, notes, currency_code, payment_method_id) + insert into invoice (company_id, invoice_number, invoice_date, contact_id, notes, tags, currency_code, payment_method_id) select company_id , next_invoice_number(add_invoice.company, invoice_date) , invoice_date , contact_id , notes + , tags , currency_code , add_invoice.payment_method_id from company @@ -59,8 +59,6 @@ begin join unnest(product.tax) as ptax(tax_id) using (tax_id); end loop; - perform tag_invoice(company, iid, tags); - return pslug; end; $$ diff --git a/deploy/add_product.sql b/deploy/add_product.sql index fdb205b..f6e80d2 100644 --- a/deploy/add_product.sql +++ b/deploy/add_product.sql @@ -6,7 +6,6 @@ -- requires: company -- requires: currency -- requires: tag_name --- requires: tag_product begin; @@ -18,8 +17,8 @@ declare pid integer; pslug uuid; begin - insert into product (company_id, name, description, price) - select add_product.company_id, add_product.name, add_product.description, parse_price(add_product.price, decimal_digits) + insert into product (company_id, name, description, price, tags) + select add_product.company_id, add_product.name, add_product.description, parse_price(add_product.price, decimal_digits), add_product.tags from company join currency using (currency_code) where company.company_id = add_product.company_id @@ -30,8 +29,6 @@ begin select pid, tax_id from unnest(taxes) as tax(tax_id); - perform tag_product(company_id, pid, tags); - return pslug; end; $$ language plpgsql; diff --git a/deploy/contact.sql b/deploy/contact.sql index bca5c5b..906f8c1 100644 --- a/deploy/contact.sql +++ b/deploy/contact.sql @@ -7,6 +7,7 @@ -- requires: extension_uri -- requires: country_code -- requires: country +-- requires: tag_name begin; @@ -27,9 +28,12 @@ create table contact ( province text not null, postal_code text not null, country_code country_code not null references country, + tags tag_name[] not null default '{}', created_at timestamptz not null default current_timestamp ); +create index on contact using gin (tags); + grant select, insert, update, delete on table contact to invoicer; grant select, insert, update, delete on table contact to admin; diff --git a/deploy/contact_tag.sql b/deploy/contact_tag.sql deleted file mode 100644 index 6d1010e..0000000 --- a/deploy/contact_tag.sql +++ /dev/null @@ -1,31 +0,0 @@ --- Deploy numerus:contact_tag to pg --- requires: schema_numerus --- requires: tag --- requires: contact - -begin; - -set search_path to numerus, public; - -create table contact_tag ( - contact_id integer not null references contact, - tag_id integer not null references tag, - primary key (contact_id, tag_id) -); - -grant select, insert, update, delete on table contact_tag to invoicer; -grant select, insert, update, delete on table contact_tag to admin; - -alter table contact_tag enable row level security; - -create policy company_policy -on contact_tag -using ( - exists( - select 1 - from contact - where contact.contact_id = contact_tag.contact_id - ) -); - -commit; diff --git a/deploy/edit_contact.sql b/deploy/edit_contact.sql index db3a563..90e3908 100644 --- a/deploy/edit_contact.sql +++ b/deploy/edit_contact.sql @@ -5,7 +5,6 @@ -- requires: country_code -- requires: tag_name -- requires: contact --- requires: tag_contact -- requires: extension_vat -- requires: extension_pg_libphonenumber @@ -31,6 +30,7 @@ begin , province = edit_contact.province , postal_code = edit_contact.postal_code , country_code = edit_contact.country_code + , tags = edit_contact.tags where slug = contact_slug returning contact_id, company_id into cid, company @@ -40,8 +40,6 @@ begin return null; end if; - perform tag_contact(company, cid, tags); - return contact_slug; end $$ diff --git a/deploy/edit_invoice.sql b/deploy/edit_invoice.sql index ca00b65..efa771d 100644 --- a/deploy/edit_invoice.sql +++ b/deploy/edit_invoice.sql @@ -8,7 +8,6 @@ -- requires: invoice_product -- requires: invoice_product_tax -- requires: tag_name --- requires: tag_invoice begin; @@ -30,6 +29,7 @@ begin , invoice_status = edit_invoice.invoice_status , notes = edit_invoice.notes , payment_method_id = edit_invoice.payment_method_id + , tags = edit_invoice.tags where slug = invoice_slug returning invoice_id, company_id, currency_code into iid, company, ccode @@ -89,8 +89,6 @@ begin delete from invoice_product where invoice_product_id = any(products_to_delete); end if; - perform tag_invoice(company, iid, tags); - return invoice_slug; end; $$ diff --git a/deploy/edit_product.sql b/deploy/edit_product.sql index 56f298e..5e611eb 100644 --- a/deploy/edit_product.sql +++ b/deploy/edit_product.sql @@ -6,7 +6,6 @@ -- requires: company -- requires: currency -- requires: tag_name --- requires: tag_product begin; @@ -22,6 +21,7 @@ begin set name = edit_product.name , description = edit_product.description , price = parse_price(edit_product.price, decimal_digits) + , tags = edit_product.tags from company join currency using (currency_code) where product.company_id = company.company_id @@ -39,8 +39,6 @@ begin select pid, tax_id from unnest(taxes) as tax(tax_id); - perform tag_product(company, pid, tags); - return true; end; $$ language plpgsql; diff --git a/deploy/invoice.sql b/deploy/invoice.sql index dc25313..2096f23 100644 --- a/deploy/invoice.sql +++ b/deploy/invoice.sql @@ -4,6 +4,7 @@ -- requires: contact -- requires: invoice_status -- requires: currency +-- requires: tag_name begin; @@ -18,11 +19,14 @@ create table invoice ( contact_id integer not null references contact, invoice_status text not null default 'created' references invoice_status, notes text not null default '', + tags tag_name[] not null default '{}', payment_method_id integer not null references payment_method, currency_code text not null references currency, created_at timestamptz not null default current_timestamp ); +create index on invoice using gin (tags); + grant select, insert, update, delete on table invoice to invoicer; grant select, insert, update, delete on table invoice to admin; diff --git a/deploy/invoice_tag.sql b/deploy/invoice_tag.sql deleted file mode 100644 index 54f3ee2..0000000 --- a/deploy/invoice_tag.sql +++ /dev/null @@ -1,31 +0,0 @@ --- Deploy numerus:invoice_tag to pg --- requires: schema_numerus --- requires: tag --- requires: invoice - -begin; - -set search_path to numerus, public; - -create table invoice_tag ( - invoice_id integer not null references invoice, - tag_id integer not null references tag, - primary key (invoice_id, tag_id) -); - -grant select, insert, update, delete on table invoice_tag to invoicer; -grant select, insert, update, delete on table invoice_tag to admin; - -alter table invoice_tag enable row level security; - -create policy company_policy -on invoice_tag -using ( - exists( - select 1 - from invoice - where invoice.invoice_id = invoice_tag.invoice_id - ) -); - -commit; diff --git a/deploy/product.sql b/deploy/product.sql index bd2abdd..1e87a7a 100644 --- a/deploy/product.sql +++ b/deploy/product.sql @@ -2,6 +2,7 @@ -- requires: schema_numerus -- requires: company -- requires: tax +-- requires: tag_name begin; @@ -14,12 +15,15 @@ create table product ( name text not null constraint name_not_empty check(length(trim(name)) > 0), description text not null default '', price integer not null, + tags tag_name[] not null default '{}', created_at timestamptz not null default current_timestamp ); comment on column product.price is 'Price is stored in cents.'; +create index on product using gin (tags); + grant select, insert, update, delete on table product to invoicer; grant select, insert, update, delete on table product to admin; diff --git a/deploy/product_tag.sql b/deploy/product_tag.sql deleted file mode 100644 index 1a73cea..0000000 --- a/deploy/product_tag.sql +++ /dev/null @@ -1,31 +0,0 @@ --- Deploy numerus:product_tag to pg --- requires: schema_numerus --- requires: tag --- requires: product - -begin; - -set search_path to numerus, public; - -create table product_tag ( - product_id integer not null references product, - tag_id integer not null references tag, - primary key (product_id, tag_id) -); - -grant select, insert, update, delete on table product_tag to invoicer; -grant select, insert, update, delete on table product_tag to admin; - -alter table product_tag enable row level security; - -create policy company_policy -on product_tag -using ( - exists( - select 1 - from product - where product.product_id = product_tag.product_id - ) -); - -commit; diff --git a/deploy/tag.sql b/deploy/tag.sql deleted file mode 100644 index 3071316..0000000 --- a/deploy/tag.sql +++ /dev/null @@ -1,35 +0,0 @@ --- Deploy numerus:tag to pg --- requires: schema_numerus --- requires: tag_name - -begin; - -set search_path to numerus, public; - -create table tag ( - tag_id serial primary key, - company_id integer not null references company, - name tag_name not null, - unique (company_id, name) -); - -grant select, insert, update, delete on table tag to invoicer; -grant select, insert, update, delete on table tag to admin; - -grant usage on sequence tag_tag_id_seq to invoicer; -grant usage on sequence tag_tag_id_seq to admin; - -alter table tag enable row level security; - -create policy company_policy -on tag -using ( - exists( - select 1 - from company_user - join user_profile using (user_id) - where company_user.company_id = tag.company_id - ) -); - -commit; diff --git a/deploy/tag_contact.sql b/deploy/tag_contact.sql deleted file mode 100644 index 02301a5..0000000 --- a/deploy/tag_contact.sql +++ /dev/null @@ -1,22 +0,0 @@ --- Deploy numerus:tag_contact to pg --- requires: schema_numerus --- requires: tag_name --- requires: tag_relation --- requires: contact_tag - -begin; - -set search_path to numerus, public; - -create or replace function tag_contact (company_id integer, contact_id integer, tags tag_name[]) returns void as -$$ - select tag_relation('contact_tag', 'contact_id', company_id, contact_id, tags); -$$ - language sql -; - -revoke execute on function tag_contact(integer, integer, tag_name[]) from public; -grant execute on function tag_contact(integer, integer, tag_name[]) to invoicer; -grant execute on function tag_contact(integer, integer, tag_name[]) to admin; - -commit; diff --git a/deploy/tag_invoice.sql b/deploy/tag_invoice.sql deleted file mode 100644 index 17d2987..0000000 --- a/deploy/tag_invoice.sql +++ /dev/null @@ -1,21 +0,0 @@ --- Deploy numerus:tag_invoice to pg --- requires: schema_numerus --- requires: tag_name --- requires: tag_invoice --- requires: invoice_tag - -begin; - -set search_path to numerus, public; - -create or replace function tag_invoice(company_id integer, invoice_id integer, tags tag_name[]) returns void as -$$ - select tag_relation('invoice_tag', 'invoice_id', company_id, invoice_id, tags); -$$ - language sql; - -revoke execute on function tag_invoice(integer, integer, tag_name[]) from public; -grant execute on function tag_invoice(integer, integer, tag_name[]) to invoicer; -grant execute on function tag_invoice(integer, integer, tag_name[]) to admin; - -commit; diff --git a/deploy/tag_product.sql b/deploy/tag_product.sql deleted file mode 100644 index 5b1933d..0000000 --- a/deploy/tag_product.sql +++ /dev/null @@ -1,22 +0,0 @@ --- Deploy numerus:tag_product to pg --- requires: schema_numerus --- requires: tag_name --- requires: tag_relation --- requires: product_tag - -begin; - -set search_path to numerus, public; - -create or replace function tag_product (company_id integer, product_id integer, tags tag_name[]) returns void as -$$ - select tag_relation('product_tag', 'product_id', company_id, product_id, tags); -$$ - language sql -; - -revoke execute on function tag_product(integer, integer, tag_name[]) from public; -grant execute on function tag_product(integer, integer, tag_name[]) to invoicer; -grant execute on function tag_product(integer, integer, tag_name[]) to admin; - -commit; diff --git a/deploy/tag_relation.sql b/deploy/tag_relation.sql deleted file mode 100644 index 8f66275..0000000 --- a/deploy/tag_relation.sql +++ /dev/null @@ -1,32 +0,0 @@ --- Deploy numerus:tag_relation to pg --- requires: schema_numerus --- requires: tag --- requires: tag_name - -begin; - -set search_path to numerus, public; - -create or replace function tag_relation(relname regclass, attname name, company integer, rowid integer, tags tag_name[]) returns void as -$$ -begin - execute format('delete from %I where %I = $1', relname, attname) USING rowid; - - if array_length(tags, 1) > 0 then - insert into tag (company_id, name) - select company, new_tag.name - from unnest (tags) as new_tag(name) - on conflict (company_id, name) do nothing - ; - - execute format('insert into %I (%I, tag_id) select $1, tag_id from tag join unnest ($2) as new_tag(name) on company_id = $3 and tag.name = new_tag.name', relname, attname) USING rowid, tags, company; - end if; -end -$$ - language plpgsql; - -revoke execute on function tag_relation(regclass, name, integer, integer, tag_name[]) from public; -grant execute on function tag_relation(regclass, name, integer, integer, tag_name[]) to invoicer; -grant execute on function tag_relation(regclass, name, integer, integer, tag_name[]) to admin; - -commit; diff --git a/pkg/contacts.go b/pkg/contacts.go index 2e8b128..dae026b 100644 --- a/pkg/contacts.go +++ b/pkg/contacts.go @@ -131,15 +131,9 @@ func mustCollectContactEntries(ctx context.Context, conn *Conn, company *Company , business_name , email , phone - , array_agg(coalesce(tag.name::text, '')) + , tags from contact - left join contact_tag using (contact_id) - left join tag using(tag_id) - where contact.company_id = $1 and (($2 = '') or (tag.name = $2)) - group by slug - , business_name - , email - , phone + where contact.company_id = $1 and (($2 = '') or (tags @> array[$2]::tag_name[])) order by business_name `, company.Id, tag) if err != nil { @@ -337,22 +331,9 @@ func (form *contactForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s , province , postal_code , country_code - , string_agg(tag.name, ',') + , array_to_string(tags, ',') from contact - left join contact_tag using (contact_id) - left join tag using(tag_id) where slug = $1 - group by business_name - , substr(vatin::text, 3) - , trade_name - , phone - , email - , web - , address - , city - , province - , postal_code - , country_code `, slug).Scan( form.BusinessName, form.VATIN, diff --git a/pkg/invoices.go b/pkg/invoices.go index adb14a3..7d68ae7 100644 --- a/pkg/invoices.go +++ b/pkg/invoices.go @@ -79,33 +79,23 @@ func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, company *Company maybeAppendWhere("invoice_date >= $%d", filters.FromDate.String(), nil) maybeAppendWhere("invoice_date <= $%d", filters.ToDate.String(), nil) if len(filters.Tags.Tags) > 0 { - appendWhere("exists (select 1 from invoice_tag join tag using (tag_id) where invoice_tag.invoice_id = invoice.invoice_id and tag.name = any($%d))", filters.Tags) + appendWhere("invoice.tags && $%d", filters.Tags) } rows := conn.MustQuery(ctx, fmt.Sprintf(` select invoice.slug , invoice_date , invoice_number , contact.business_name - , array_agg(coalesce(tag.name::text, '')) + , invoice.tags , invoice.invoice_status , isi18n.name , to_price(total, decimal_digits) from invoice - left join invoice_tag using (invoice_id) - left join tag using(tag_id) join contact using (contact_id) join invoice_status_i18n isi18n on invoice.invoice_status = isi18n.invoice_status and isi18n.lang_tag = $1 join invoice_amount using (invoice_id) join currency using (currency_code) where (%s) - group by invoice.slug - , invoice_date - , invoice_number - , contact.business_name - , invoice.invoice_status - , isi18n.name - , total - , decimal_digits order by invoice_date desc , invoice_number desc `, strings.Join(where, ") AND (")), args...) @@ -673,16 +663,9 @@ func (form *invoiceForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s , invoice_date , notes , payment_method_id - , string_agg(tag.name, ',') + , array_to_string(tags, ',') from invoice - left join invoice_tag using (invoice_id) - left join tag using(tag_id) where slug = $1 - group by invoice_id - , contact_id - , invoice_number - , invoice_date - , notes - , payment_method_id + where slug = $1 `, slug).Scan(&invoiceId, form.InvoiceStatus, form.Customer, &form.Number, form.Date, form.Notes, form.PaymentMethod, form.Tags)) { form.PaymentMethod.Selected = selectedPaymentMethod form.InvoiceStatus.Selected = selectedInvoiceStatus diff --git a/pkg/pgtypes.go b/pkg/pgtypes.go index 1d9be1a..80bd8b9 100644 --- a/pkg/pgtypes.go +++ b/pkg/pgtypes.go @@ -72,6 +72,16 @@ func registerPgTypes(ctx context.Context, conn *pgx.Conn) error { if _, err := conn.Exec(ctx, "set role to admin"); err != nil { return err } + tagNameOID, err := registerPgType(ctx, conn, &pgtype.Text{}, "tag_name") + if err != nil { + return err + } + tagNameArray := pgtype.NewArrayType("tag_name[]", tagNameOID, func() pgtype.ValueTranscoder { + return &pgtype.Text{} + }) + if _, err := registerPgType(ctx, conn, tagNameArray, tagNameArray.TypeName()); err != nil { + return err + } discountRateOID, err := registerPgType(ctx, conn, &pgtype.Numeric{}, "discount_rate") if err != nil { return err diff --git a/pkg/products.go b/pkg/products.go index c974bca..a50b65a 100644 --- a/pkg/products.go +++ b/pkg/products.go @@ -157,16 +157,11 @@ func mustCollectProductEntries(ctx context.Context, conn *Conn, company *Company select product.slug , product.name , to_price(price, decimal_digits) - , array_agg(coalesce(tag.name::text, '')) + , tags from product join company using (company_id) join currency using (currency_code) - left join product_tag using (product_id) - left join tag using(tag_id) - where product.company_id = $1 and (($2 = '') or (tag.name = $2)) - group by product.slug - , product.name - , to_price(price, decimal_digits) + where product.company_id = $1 and (($2 = '') or (tags @> array[$2]::tag_name[])) order by name `, company.Id, tag) if err != nil { @@ -267,11 +262,9 @@ func (form *productForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s , product.description , to_price(price, decimal_digits) , array_agg(tax_id) - , string_agg(tag.name, ',') + , array_to_string(tags, ',') from product left join product_tax using (product_id) - left join product_tag using (product_id) - left join tag using(company_id, tag_id) join company using (company_id) join currency using (currency_code) where product.slug = $1 @@ -279,6 +272,7 @@ func (form *productForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s , product.name , product.description , price + , tags , decimal_digits `, slug).Scan( form.Name, diff --git a/revert/contact_tag.sql b/revert/contact_tag.sql deleted file mode 100644 index df36364..0000000 --- a/revert/contact_tag.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert numerus:contact_tag from pg - -begin; - -drop table if exists numerus.contact_tag; - -commit; diff --git a/revert/invoice_tag.sql b/revert/invoice_tag.sql deleted file mode 100644 index 49192e7..0000000 --- a/revert/invoice_tag.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert numerus:invoice_tag from pg - -begin; - -drop table if exists numerus.invoice_tag; - -commit; diff --git a/revert/product_tag.sql b/revert/product_tag.sql deleted file mode 100644 index b934c7e..0000000 --- a/revert/product_tag.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert numerus:product_tag from pg - -begin; - -drop table if exists numerus.product_tag; - -commit; diff --git a/revert/tag.sql b/revert/tag.sql deleted file mode 100644 index f936d9a..0000000 --- a/revert/tag.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert numerus:tag from pg - -begin; - -drop table if exists numerus.tag; - -commit; diff --git a/revert/tag_contact.sql b/revert/tag_contact.sql deleted file mode 100644 index 5ace225..0000000 --- a/revert/tag_contact.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert numerus:tag_contact from pg - -begin; - -drop function if exists numerus.tag_contact(integer, integer, numerus.tag_name[]); - -commit; diff --git a/revert/tag_invoice.sql b/revert/tag_invoice.sql deleted file mode 100644 index b87a3f7..0000000 --- a/revert/tag_invoice.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert numerus:tag_invoice from pg - -begin; - -drop function if exists numerus.tag_invoice(integer, integer, numerus.tag_name[]); - -commit; diff --git a/revert/tag_product.sql b/revert/tag_product.sql deleted file mode 100644 index 1639790..0000000 --- a/revert/tag_product.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert numerus:tag_product from pg - -begin; - -drop function if exists numerus.tag_product(integer, integer, numerus.tag_name[]); - -commit; diff --git a/revert/tag_relation.sql b/revert/tag_relation.sql deleted file mode 100644 index 09cb08e..0000000 --- a/revert/tag_relation.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert numerus:tag_relation from pg - -begin; - -drop function if exists numerus.tag_relation(regclass, name, integer, integer, numerus.tag_name[]); - -commit; diff --git a/sqitch.plan b/sqitch.plan index 5aa922b..7cd4850 100644 --- a/sqitch.plan +++ b/sqitch.plan @@ -41,39 +41,31 @@ company_default_payment_method [schema_numerus company payment_method] 2023-03-0 tax_class [schema_numerus company] 2023-02-28T10:13:14Z jordi fita mas # Add the relation for tax classes tax_rate [schema_numerus] 2023-01-28T11:33:39Z jordi fita mas # Add domain for tax rates tax [schema_numerus company tax_rate tax_class] 2023-01-28T11:45:47Z jordi fita mas # Add relation for taxes -contact [schema_numerus company extension_vat email extension_pg_libphonenumber extension_uri country_code country] 2023-01-29T12:59:18Z jordi fita mas # Add the relation for contacts -product [schema_numerus company tax] 2023-02-04T09:17:24Z jordi fita mas # Add relation for products +tag_name [schema_numerus] 2023-03-10T11:06:11Z jordi fita mas # Add domain for tag names +contact [schema_numerus company extension_vat email extension_pg_libphonenumber extension_uri country_code country tag_name] 2023-01-29T12:59:18Z jordi fita mas # Add the relation for contacts +product [schema_numerus company tax tag_name] 2023-02-04T09:17:24Z jordi fita mas # Add relation for products parse_price [schema_public] 2023-02-05T11:04:54Z jordi fita mas # Add function to convert from price to cents to_price [schema_numerus] 2023-02-05T11:46:31Z jordi fita mas # Add function to format cents to prices invoice_status [schema_numerus] 2023-02-07T14:50:26Z jordi fita mas # A relation of invoice status invoice_status_i18n [schema_numerus invoice_status language] 2023-02-07T14:56:18Z jordi fita mas # Add relation for invoice status’ translatable texts available_invoice_status [schema_numerus invoice_status invoice_status_i18n] 2023-02-07T15:07:06Z jordi fita mas # Add the list of available invoice status product_tax [schema_numerus product tax] 2023-02-08T11:36:49Z jordi fita mas # Add relation of product taxes -invoice [schema_numerus company contact invoice_status payment_method currency] 2023-02-09T09:52:21Z jordi fita mas # Add relation for invoice +invoice [schema_numerus company contact invoice_status payment_method currency tag_name] 2023-02-09T09:52:21Z jordi fita mas # Add relation for invoice discount_rate [schema_numerus] 2023-02-10T17:22:40Z jordi fita mas # Add domain for discount rates invoice_product [schema_numerus invoice discount_rate] 2023-02-10T17:07:08Z jordi fita mas # Add relation for invoice product -tag_name [schema_numerus] 2023-03-10T11:06:11Z jordi fita mas # Add domain for tag names -tag [schema_numerus tag_name] 2023-03-10T11:04:24Z jordi fita mas # Add relation for tags -tag_relation [schema_numerus tag tag_name] 2023-03-25T17:40:52Z jordi fita mas # Add function to tag “relations” -product_tag [schema_numerus tag product] 2023-03-26T10:54:23Z jordi fita mas # Add relation for product tag -tag_product [schema_numerus tag_name tag_relation product_tag] 2023-03-26T11:01:17Z jordi fita mas # Add function to tag products -add_product [schema_numerus product product_tax parse_price company currency tag_name tag_product] 2023-02-14T10:32:18Z jordi fita mas # Add function to add new products -edit_product [schema_numerus product product_tax parse_price company currency tag_name tag_product] 2023-02-14T11:06:03Z jordi fita mas # Add function to edit products +add_product [schema_numerus product product_tax parse_price company currency tag_name] 2023-02-14T10:32:18Z jordi fita mas # Add function to add new products +edit_product [schema_numerus product product_tax parse_price company currency tag_name] 2023-02-14T11:06:03Z jordi fita mas # Add function to edit products invoice_product_tax [schema_numerus invoice_product tax tax_rate] 2023-02-15T13:20:30Z jordi fita mas # Add relation for taxes in invoice products new_invoice_product [schema_numerus discount_rate] 2023-02-16T21:06:01Z jordi fita mas # Add type for passing products to new invoices invoice_number_counter [schema_numerus company] 2023-02-17T13:04:48Z jordi fita mas # Add relation to count invoice numbers next_invoice_number [schema_numerus invoice_number_counter] 2023-02-17T13:21:48Z jordi fita mas # Add function to retrieve the next invoice number -invoice_tag [schema_numerus tag invoice] 2023-03-10T11:37:43Z jordi fita mas # Add relation for invoice tag -tag_invoice [schema_numerus tag_name tag_relation invoice_tag] 2023-03-25T18:04:02Z jordi fita mas # Add function to tag invoices -add_invoice [schema_numerus invoice company currency parse_price new_invoice_product tax invoice_product invoice_product_tax next_invoice_number tag_name tag_invoice] 2023-02-16T21:12:46Z jordi fita mas # Add function to create new invoices +add_invoice [schema_numerus invoice company currency parse_price new_invoice_product tax invoice_product invoice_product_tax next_invoice_number tag_name] 2023-02-16T21:12:46Z jordi fita mas # Add function to create new invoices invoice_tax_amount [schema_numerus invoice_product invoice_product_tax] 2023-02-22T12:08:35Z jordi fita mas # Add view for invoice tax amount invoice_product_amount [schema_numerus invoice_product invoice_product_tax] 2023-03-01T11:18:05Z jordi fita mas # Add view for invoice product subtotal and total invoice_amount [schema_numerus invoice_product invoice_product_amount] 2023-02-22T12:58:46Z jordi fita mas # Add view to compute subtotal and total for invoices new_invoice_amount [schema_numerus] 2023-02-23T12:08:25Z jordi fita mas # Add type to return when computing new invoice amounts compute_new_invoice_amount [schema_numerus company currency tax new_invoice_product new_invoice_amount] 2023-02-23T12:20:13Z jordi fita mas # Add function to compute the subtotal, taxes, and total amounts for a new invoice edited_invoice_product [schema_numerus discount_rate] 2023-03-11T19:22:24Z jordi fita mas # Add typo for passing products to edited invoices -edit_invoice [schema_numerus invoice currency parse_price edited_invoice_product tax invoice_product invoice_product_tax tag_name tag_invoice] 2023-03-11T18:30:50Z jordi fita mas # Add function to edit invoices -contact_tag [schema_numerus tag contact] 2023-03-24T22:20:51Z jordi fita mas # Add relation for contact tag -tag_contact [schema_numerus tag_name tag_relation contact_tag] 2023-03-25T22:16:42Z jordi fita mas # Add function to tag contacts +edit_invoice [schema_numerus invoice currency parse_price edited_invoice_product tax invoice_product invoice_product_tax tag_name] 2023-03-11T18:30:50Z jordi fita mas # Add function to edit invoices add_contact [schema_numerus extension_vat email extension_pg_libphonenumber extension_uri country_code tag_name contact] 2023-03-25T22:32:37Z jordi fita mas # Add function to create new contacts -edit_contact [schema_numerus email extension_uri country_code tag_name contact tag_contact extension_vat extension_pg_libphonenumber] 2023-03-25T23:20:27Z jordi fita mas # Add function to edit contacts +edit_contact [schema_numerus email extension_uri country_code tag_name contact extension_vat extension_pg_libphonenumber] 2023-03-25T23:20:27Z jordi fita mas # Add function to edit contacts diff --git a/test/add_contact.sql b/test/add_contact.sql index 159dc15..056bd85 100644 --- a/test/add_contact.sql +++ b/test/add_contact.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(16); +select plan(14); set search_path to auth, numerus, public; @@ -21,8 +21,6 @@ select function_privs_are('numerus', 'add_contact', array ['integer', 'text', 't set client_min_messages to warning; -truncate contact_tag cascade; -truncate tag cascade; truncate contact cascade; truncate payment_method cascade; truncate company cascade; @@ -63,34 +61,15 @@ select lives_ok( ); select bag_eq( - $$ select company_id, business_name, vatin::text, trade_name, phone::text, email::text, web::text, address, city, province, postal_code, country_code::text, created_at from contact $$, - $$ values (1, 'Contact 2.1', 'ES40404040D', 'Trade Contact 2.1', '+34 777 77 77 77', 'c@c', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '17486', 'ES', CURRENT_TIMESTAMP) - , (1, 'Contact 2.2', 'ES41414141L', 'Trade Contact 2.2', '+34 888 88 88 88', 'd@d', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17487', 'ES', CURRENT_TIMESTAMP) - , (2, 'Contact 4.1', 'ES42424242Y', '', '+34 999 99 99 99', 'e@e', '', 'Yet Another Fake St., 123', 'City 4.1', 'Province 4.1', '17488', 'ES', CURRENT_TIMESTAMP) - , (1, 'Contact 2.3', 'ES43434343Q', '', '+34 000000000', 'f@f', '', 'The Last Fake St., 123', '', '', '', 'ES', CURRENT_TIMESTAMP) + $$ select company_id, business_name, vatin::text, trade_name, phone::text, email::text, web::text, address, city, province, postal_code, country_code::text, tags, created_at from contact $$, + $$ values (1, 'Contact 2.1', 'ES40404040D', 'Trade Contact 2.1', '+34 777 77 77 77', 'c@c', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '17486', 'ES', '{tag1,tag2}'::tag_name[], CURRENT_TIMESTAMP) + , (1, 'Contact 2.2', 'ES41414141L', 'Trade Contact 2.2', '+34 888 88 88 88', 'd@d', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17487', 'ES', '{}'::tag_name[], CURRENT_TIMESTAMP) + , (2, 'Contact 4.1', 'ES42424242Y', '', '+34 999 99 99 99', 'e@e', '', 'Yet Another Fake St., 123', 'City 4.1', 'Province 4.1', '17488', 'ES', '{tag2}'::tag_name[], CURRENT_TIMESTAMP) + , (1, 'Contact 2.3', 'ES43434343Q', '', '+34 000000000', 'f@f', '', 'The Last Fake St., 123', '', '', '', 'ES', '{tag2}'::tag_name[], CURRENT_TIMESTAMP) $$, 'Should have created all contacts' ); -select bag_eq( - $$ select company_id, name from tag $$, - $$ values (1, 'tag1') - , (1, 'tag2') - , (2, 'tag2') - $$, - 'Should have added all new tags once' -); - -select bag_eq( - $$ select business_name, tag.name from contact_tag join contact using (contact_id) join tag using (tag_id) $$, - $$ values ('Contact 2.1', 'tag1') - , ('Contact 2.1', 'tag2') - , ('Contact 4.1', 'tag2') - , ('Contact 2.3', 'tag2') - $$, - 'Should have assigned the tags to contacts' -); - select * from finish(); diff --git a/test/add_invoice.sql b/test/add_invoice.sql index f7120d5..7274882 100644 --- a/test/add_invoice.sql +++ b/test/add_invoice.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(17); +select plan(15); set search_path to auth, numerus, public; @@ -21,8 +21,6 @@ select function_privs_are('numerus', 'add_invoice', array ['integer', 'date', 'i set client_min_messages to warning; -truncate invoice_tag cascade; -truncate tag cascade; truncate invoice_number_counter cascade; truncate invoice_product_tax cascade; truncate invoice_product cascade; @@ -98,10 +96,10 @@ select lives_ok( ); select bag_eq( - $$ select company_id, invoice_number, invoice_date, contact_id, invoice_status, notes, payment_method_id, currency_code, created_at from invoice $$, - $$ values (1, 'F20230006', '2023-02-15'::date, 12, 'created', 'Notes 1', 111, 'EUR', current_timestamp) - , (1, 'F20230007', '2023-02-16'::date, 13, 'created', 'Notes 2', 111, 'EUR', current_timestamp) - , (2, 'INV056-23', '2023-02-14'::date, 15, 'created', 'Notes 3', 222, 'USD', current_timestamp) + $$ select company_id, invoice_number, invoice_date, contact_id, invoice_status, notes, payment_method_id, currency_code, tags, created_at from invoice $$, + $$ values (1, 'F20230006', '2023-02-15'::date, 12, 'created', 'Notes 1', 111, 'EUR', '{tag1,tag2}'::tag_name[], current_timestamp) + , (1, 'F20230007', '2023-02-16'::date, 13, 'created', 'Notes 2', 111, 'EUR', '{}'::tag_name[], current_timestamp) + , (2, 'INV056-23', '2023-02-14'::date, 15, 'created', 'Notes 3', 222, 'USD', '{tag3}'::tag_name[], current_timestamp) $$, 'Should have created all invoices' ); @@ -126,24 +124,6 @@ select bag_eq( 'Should have created all invoice product taxes' ); -select bag_eq( - $$ select company_id, name from tag $$, - $$ values (1, 'tag1') - , (1, 'tag2') - , (2, 'tag3') - $$, - 'Should have added all new tags once' -); - -select bag_eq( - $$ select invoice_number, tag.name from invoice_tag join invoice using (invoice_id) join tag using (tag_id) $$, - $$ values ('F20230006', 'tag1') - , ('F20230006', 'tag2') - , ('INV056-23', 'tag3') - $$, - 'Should have assigned the tags to invoices' -); - select * from finish(); diff --git a/test/add_product.sql b/test/add_product.sql index c4c0e46..e4cade9 100644 --- a/test/add_product.sql +++ b/test/add_product.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(16); +select plan(14); set search_path to auth, numerus, public; @@ -21,8 +21,6 @@ select function_privs_are('numerus', 'add_product', array ['integer', 'text', 't set client_min_messages to warning; -truncate product_tag cascade; -truncate tag cascade; truncate product_tax cascade; truncate product cascade; truncate tax cascade; @@ -74,10 +72,10 @@ select lives_ok( ); select bag_eq( - $$ select company_id, name, description, price, created_at from product $$, - $$ values (1, 'Product 1', 'Description 1', 1212, current_timestamp) - , (2, 'Product 2', 'Description 2', 2424, current_timestamp) - , (2, 'Product 3', 'Description 3', 3636, current_timestamp) + $$ select company_id, name, description, price, tags, created_at from product $$, + $$ values (1, 'Product 1', 'Description 1', 1212, '{tag1}'::tag_name[], current_timestamp) + , (2, 'Product 2', 'Description 2', 2424, '{tag1,tag2}'::tag_name[], current_timestamp) + , (2, 'Product 3', 'Description 3', 3636, '{}'::tag_name[], current_timestamp) $$, 'Should have added all three products' ); @@ -91,24 +89,6 @@ select bag_eq( 'Should have added the taxes for the products we told to' ); -select bag_eq( - $$ select company_id, name from tag $$, - $$ values (1, 'tag1') - , (2, 'tag1') - , (2, 'tag2') - $$, - 'Should have added all new tags once' -); - -select bag_eq( - $$ select product.name as product_name, tag.name as tag_name from product_tag join product using (product_id) join tag using (tag_id) $$, - $$ values ('Product 1', 'tag1') - , ('Product 2', 'tag1') - , ('Product 2', 'tag2') - $$, - 'Should have assigned the tags to products' -); - select * from finish(); diff --git a/test/contact.sql b/test/contact.sql index 553f83e..a47fe99 100644 --- a/test/contact.sql +++ b/test/contact.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(85); +select plan(90); set search_path to numerus, auth, public; @@ -100,6 +100,12 @@ select col_type_is('contact', 'country_code', 'country_code'); select col_not_null('contact', 'country_code'); select col_hasnt_default('contact', 'country_code'); +select has_column('contact', 'tags'); +select col_type_is('contact', 'tags', 'tag_name[]'); +select col_not_null('contact', 'tags'); +select col_has_default('contact', 'tags'); +select col_default_is('contact', 'tags', '{}'); + select has_column('contact', 'created_at'); select col_type_is('contact', 'created_at', 'timestamp with time zone'); select col_not_null('contact', 'created_at'); diff --git a/test/contact_tag.sql b/test/contact_tag.sql deleted file mode 100644 index fc03584..0000000 --- a/test/contact_tag.sql +++ /dev/null @@ -1,130 +0,0 @@ --- Test contact_tag -set client_min_messages to warning; -create extension if not exists pgtap; -reset client_min_messages; - -begin; - -select plan(23); - -set search_path to numerus, auth, public; - -select has_table('contact_tag'); -select has_pk('contact_tag' ); -select col_is_pk('contact_tag', array['contact_id', 'tag_id']); -select table_privs_are('contact_tag', 'guest', array []::text[]); -select table_privs_are('contact_tag', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']); -select table_privs_are('contact_tag', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']); -select table_privs_are('contact_tag', 'authenticator', array []::text[]); - -select has_column('contact_tag', 'contact_id'); -select col_is_fk('contact_tag', 'contact_id'); -select fk_ok('contact_tag', 'contact_id', 'contact', 'contact_id'); -select col_type_is('contact_tag', 'contact_id', 'integer'); -select col_not_null('contact_tag', 'contact_id'); -select col_hasnt_default('contact_tag', 'contact_id'); - -select has_column('contact_tag', 'tag_id'); -select col_is_fk('contact_tag', 'tag_id'); -select fk_ok('contact_tag', 'tag_id', 'tag', 'tag_id'); -select col_type_is('contact_tag', 'tag_id', 'integer'); -select col_not_null('contact_tag', 'tag_id'); -select col_hasnt_default('contact_tag', 'tag_id'); - - -set client_min_messages to warning; -truncate contact_tag cascade; -truncate contact cascade; -truncate tag cascade; -truncate contact cascade; -truncate company_user cascade; -truncate company cascade; -truncate payment_method cascade; -truncate auth."user" cascade; -reset client_min_messages; - -insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at) -values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') -; - -set constraints "company_default_payment_method_id_fkey" deferred; - -insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id) -values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222) - , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444) -; - -insert into payment_method (payment_method_id, company_id, name, instructions) -values (444, 4, 'cash', 'cash') - , (222, 2, 'cash', 'cash') -; - -set constraints "company_default_payment_method_id_fkey" immediate; - -insert into company_user (company_id, user_id) -values (2, 1) - , (4, 5) -; - -insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) -values (6, 2, 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES') - , (8, 4, 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES') -; - -insert into tag (tag_id, company_id, name) -values (14, 2, 'web') - , (15, 2, 'design') - , (16, 4, 'product') - , (17, 4, 'development') - , (18, 4, 'something-else') - , (19, 4, 'design') -; - -insert into contact_tag (contact_id, tag_id) -values (6, 14) - , (6, 15) - , (8, 18) -; - -prepare contact_tag_data as -select contact_id, tag_id -from contact_tag -; - -set role invoicer; -select is_empty('contact_tag_data', 'Should show no data when cookie is not set yet'); -reset role; - -select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'); -select bag_eq( - 'contact_tag_data', - $$ values ( 6, 14 ) - , ( 6, 15 ) - $$, - 'Should only list contact tags of the companies where demo@tandem.blog is user of' -); -reset role; - -select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'); -select bag_eq( - 'contact_tag_data', - $$ values ( 8, 18 ) - $$, - 'Should only list contact tags of the companies where admin@tandem.blog is user of' -); -reset role; - -select set_cookie('not-a-cookie'); -select throws_ok( - 'contact_tag_data', - '42501', 'permission denied for table contact_tag', - 'Should not allow select to guest users' -); - - -select * -from finish(); - -rollback; - diff --git a/test/edit_contact.sql b/test/edit_contact.sql index c13156d..bc86b38 100644 --- a/test/edit_contact.sql +++ b/test/edit_contact.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(14); +select plan(12); set search_path to auth, numerus, public; @@ -21,8 +21,6 @@ select function_privs_are('numerus', 'edit_contact', array ['uuid', 'text', 'tex set client_min_messages to warning; -truncate contact_tag cascade; -truncate tag cascade; truncate contact cascade; truncate payment_method cascade; truncate company cascade; @@ -42,22 +40,11 @@ values (111, 1, 'cash', 'cash') set constraints "company_default_payment_method_id_fkey" immediate; -insert into tag (tag_id, company_id, name) -values (10, 1, 'tag1') - , (11, 1, 'tag2') -; --- edit_contact uses the sequence and sometimes it would confict -alter sequence tag_tag_id_seq restart with 15; - -insert into contact (contact_id, company_id, slug, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) -values (12, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES') - , (13, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES') +insert into contact (contact_id, company_id, slug, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, tags) +values (12, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES', '{tag1}') + , (13, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES', '{tag2}') ; -insert into contact_tag (contact_id, tag_id) -values (12, 10) - , (13, 11) -; select lives_ok( $$ select edit_contact('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 2.1', '40404040D', 'Trade Contact 2.1', '999-999-999', 'c1@c1', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES', array['tag1']) $$, @@ -70,30 +57,13 @@ select lives_ok( ); select bag_eq( - $$ select company_id, business_name, vatin::text, trade_name, phone::text, email::text, web::text, address, city, province, postal_code, country_code::text, created_at from contact $$, - $$ values (1, 'Contact 2.1', 'ES40404040D', 'Trade Contact 2.1', '+34 999 99 99 99', 'c1@c1', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES', CURRENT_TIMESTAMP) - , (1, 'Contact 2.2', 'ES41414141L', 'Trade Contact 2.2', '+34 111111111', 'd2@d2', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17417', 'ES', CURRENT_TIMESTAMP) + $$ select company_id, business_name, vatin::text, trade_name, phone::text, email::text, web::text, address, city, province, postal_code, country_code::text, tags, created_at from contact $$, + $$ values (1, 'Contact 2.1', 'ES40404040D', 'Trade Contact 2.1', '+34 999 99 99 99', 'c1@c1', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES', '{tag1}'::tag_name[], CURRENT_TIMESTAMP) + , (1, 'Contact 2.2', 'ES41414141L', 'Trade Contact 2.2', '+34 111111111', 'd2@d2', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17417', 'ES', '{tag1,tag3}'::tag_name[], CURRENT_TIMESTAMP) $$, 'Should have updated all contacts' ); -select bag_eq( - $$ select company_id, name from tag $$, - $$ values (1, 'tag1') - , (1, 'tag2') - , (1, 'tag3') - $$, - 'Should have added all new tags' -); - -select bag_eq( - $$ select business_name, tag.name from contact_tag join contact using (contact_id) join tag using (tag_id) $$, - $$ values ('Contact 2.1', 'tag1') - , ('Contact 2.2', 'tag1') - , ('Contact 2.2', 'tag3') - $$, - 'Should have assigned the tags to contacts' -); select * from finish(); diff --git a/test/edit_invoice.sql b/test/edit_invoice.sql index 6720723..a698cb3 100644 --- a/test/edit_invoice.sql +++ b/test/edit_invoice.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(16); +select plan(14); set search_path to auth, numerus, public; @@ -21,8 +21,6 @@ select function_privs_are('numerus', 'edit_invoice', array ['uuid', 'text', 'int set client_min_messages to warning; -truncate invoice_tag cascade; -truncate tag cascade; truncate invoice_product_tax cascade; truncate invoice_product cascade; truncate invoice cascade; @@ -63,26 +61,14 @@ values ( 7, 1, 'Product 1.1', 1212) , ( 9, 1, 'Product 3.3', 3636) ; -insert into tag (tag_id, company_id, name) -values (10, 1, 'tag1') - , (11, 1, 'tag2') -; --- edit_invoice uses the sequence and sometimes it would confict -alter sequence tag_tag_id_seq restart with 15; - insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES') , (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES') ; -insert into invoice (invoice_id, company_id, slug, invoice_number, invoice_date, contact_id, payment_method_id, currency_code) -values (15, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'INV1', '2023-03-10', 12, 111, 'EUR') - , (16, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'INV2', '2023-03-09', 13, 111, 'EUR') -; - -insert into invoice_tag (invoice_id, tag_id) -values (15, 10) - , (16, 11) +insert into invoice (invoice_id, company_id, slug, invoice_number, invoice_date, contact_id, payment_method_id, currency_code, tags) +values (15, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'INV1', '2023-03-10', 12, 111, 'EUR', '{tag1}') + , (16, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'INV2', '2023-03-09', 13, 111, 'EUR', '{tag2}') ; insert into invoice_product (invoice_product_id, invoice_id, product_id, name, price) @@ -111,9 +97,9 @@ select lives_ok( ); select bag_eq( - $$ select invoice_number, invoice_date, contact_id, invoice_status, notes, payment_method_id from invoice $$, - $$ values ('INV1', '2023-03-10'::date, 13, 'paid', 'Notes 1', 112) - , ('INV2', '2023-03-09'::date, 12, 'sent', 'Notes 2', 111) + $$ select invoice_number, invoice_date, contact_id, invoice_status, notes, tags, payment_method_id from invoice $$, + $$ values ('INV1', '2023-03-10'::date, 13, 'paid', 'Notes 1', '{tag1}'::tag_name[], 112) + , ('INV2', '2023-03-09'::date, 12, 'sent', 'Notes 2', '{tag1,tag3}'::tag_name[], 111) $$, 'Should have updated all invoices' ); @@ -139,24 +125,6 @@ select bag_eq( 'Should have updated all invoice product taxes, added new ones, and removed the ones not given to the function' ); -select bag_eq( - $$ select company_id, name from tag $$, - $$ values (1, 'tag1') - , (1, 'tag2') - , (1, 'tag3') - $$, - 'Should have added all new tags' -); - -select bag_eq( - $$ select invoice_number, tag.name from invoice_tag join invoice using (invoice_id) join tag using (tag_id) $$, - $$ values ('INV1', 'tag1') - , ('INV2', 'tag1') - , ('INV2', 'tag3') - $$, - 'Should have assigned the tags to invoices' -); - select * from finish(); diff --git a/test/edit_product.sql b/test/edit_product.sql index b7460f2..840b912 100644 --- a/test/edit_product.sql +++ b/test/edit_product.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(17); +select plan(15); set search_path to auth, numerus, public; @@ -21,8 +21,6 @@ select function_privs_are('numerus', 'edit_product', array ['uuid', 'text', 'tex set client_min_messages to warning; -truncate product_tag cascade; -truncate tag cascade; truncate product_tax cascade; truncate product cascade; truncate tax cascade; @@ -57,10 +55,10 @@ values (3, 1, 11, 'IRPF -15 %', -0.15) , (6, 2, 22, 'IVA 10 %', 0.10) ; -insert into product (product_id, company_id, slug, name, description, price) -values (7, 1, 'd2300404-bd23-48b3-8e2b-2bbf86dc7bd6', 'Product 01', 'Description01', 1200) - , (8, 2, '2f085b8b-da90-41fe-b8cf-6ba8d94cfa38', 'Product 02', 'Description02', 2400) - , (9, 2, '84044d0b-af33-442a-95a6-21efc77260d5', 'Product 03', 'Description03', 3600) +insert into product (product_id, company_id, slug, name, description, price, tags) +values (7, 1, 'd2300404-bd23-48b3-8e2b-2bbf86dc7bd6', 'Product 01', 'Description01', 1200, '{tag1}') + , (8, 2, '2f085b8b-da90-41fe-b8cf-6ba8d94cfa38', 'Product 02', 'Description02', 2400, '{tag2}') + , (9, 2, '84044d0b-af33-442a-95a6-21efc77260d5', 'Product 03', 'Description03', 3600, '{tag2}') ; insert into product_tax (product_id, tax_id) @@ -70,19 +68,6 @@ values (7, 3) , (9, 6) ; -insert into tag (tag_id, company_id, name) -values (10, 1, 'tag1') - , (11, 2, 'tag2') -; --- edit_product uses the sequence and sometimes it would confict -alter sequence tag_tag_id_seq restart with 15; - -insert into product_tag (product_id, tag_id) -values (7, 10) - , (8, 11) - , (9, 11) -; - select is( edit_product('d2300404-bd23-48b3-8e2b-2bbf86dc7bd6', 'Product 1', 'Description 1', '12.12', array[3, 4], array['tag1']), @@ -109,10 +94,10 @@ select is( ); select bag_eq( - $$ select product_id, company_id, name, description, price from product $$, - $$ values (7, 1, 'Product 1', 'Description 1', 1212) - , (8, 2, 'Product 2', 'Description 2', 2424) - , (9, 2, 'Product 3', 'Description 3', 3636) + $$ select product_id, company_id, name, description, price, tags from product $$, + $$ values (7, 1, 'Product 1', 'Description 1', 1212, '{tag1}'::tag_name[]) + , (8, 2, 'Product 2', 'Description 2', 2424, '{tag1,tag3}'::tag_name[]) + , (9, 2, 'Product 3', 'Description 3', 3636, '{}'::tag_name[]) $$, 'Should have edited all three products' ); @@ -126,24 +111,6 @@ select bag_eq( 'Should have updated the taxes for the products we told to' ); -select bag_eq( - $$ select company_id, name from tag $$, - $$ values (1, 'tag1') - , (2, 'tag1') - , (2, 'tag2') - , (2, 'tag3') - $$, - 'Should have added all new tags' -); - -select bag_eq( - $$ select product.name as product_name, tag.name as tag_name from product_tag join product using (product_id) join tag using (tag_id) $$, - $$ values ('Product 1', 'tag1') - , ('Product 2', 'tag1') - , ('Product 2', 'tag3') - $$, - 'Should have assigned the tags to products' -); select * from finish(); diff --git a/test/invoice.sql b/test/invoice.sql index 4793be5..377d54a 100644 --- a/test/invoice.sql +++ b/test/invoice.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(78); +select plan(83); set search_path to numerus, auth, public; @@ -75,6 +75,12 @@ select col_not_null('invoice', 'notes'); select col_has_default('invoice', 'notes'); select col_default_is('invoice', 'notes', ''); +select has_column('invoice', 'tags'); +select col_type_is('invoice', 'tags', 'tag_name[]'); +select col_not_null('invoice', 'tags'); +select col_has_default('invoice', 'tags'); +select col_default_is('invoice', 'tags', '{}'); + select has_column('invoice', 'payment_method_id'); select col_is_fk('invoice', 'payment_method_id'); select fk_ok('invoice', 'payment_method_id', 'payment_method', 'payment_method_id'); diff --git a/test/invoice_tag.sql b/test/invoice_tag.sql deleted file mode 100644 index f97ab73..0000000 --- a/test/invoice_tag.sql +++ /dev/null @@ -1,136 +0,0 @@ --- Test invoice_tag -set client_min_messages to warning; -create extension if not exists pgtap; -reset client_min_messages; - -begin; - -select plan(23); - -set search_path to numerus, auth, public; - -select has_table('invoice_tag'); -select has_pk('invoice_tag' ); -select col_is_pk('invoice_tag', array['invoice_id', 'tag_id']); -select table_privs_are('invoice_tag', 'guest', array []::text[]); -select table_privs_are('invoice_tag', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']); -select table_privs_are('invoice_tag', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']); -select table_privs_are('invoice_tag', 'authenticator', array []::text[]); - -select has_column('invoice_tag', 'invoice_id'); -select col_is_fk('invoice_tag', 'invoice_id'); -select fk_ok('invoice_tag', 'invoice_id', 'invoice', 'invoice_id'); -select col_type_is('invoice_tag', 'invoice_id', 'integer'); -select col_not_null('invoice_tag', 'invoice_id'); -select col_hasnt_default('invoice_tag', 'invoice_id'); - -select has_column('invoice_tag', 'tag_id'); -select col_is_fk('invoice_tag', 'tag_id'); -select fk_ok('invoice_tag', 'tag_id', 'tag', 'tag_id'); -select col_type_is('invoice_tag', 'tag_id', 'integer'); -select col_not_null('invoice_tag', 'tag_id'); -select col_hasnt_default('invoice_tag', 'tag_id'); - - -set client_min_messages to warning; -truncate invoice_tag cascade; -truncate invoice cascade; -truncate tag cascade; -truncate contact cascade; -truncate company_user cascade; -truncate company cascade; -truncate payment_method cascade; -truncate auth."user" cascade; -reset client_min_messages; - -insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at) -values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') -; - -set constraints "company_default_payment_method_id_fkey" deferred; - -insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id) -values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222) - , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444) -; - -insert into payment_method (payment_method_id, company_id, name, instructions) -values (444, 4, 'cash', 'cash') - , (222, 2, 'cash', 'cash') -; - -set constraints "company_default_payment_method_id_fkey" immediate; - -insert into company_user (company_id, user_id) -values (2, 1) - , (4, 5) -; - -insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) -values (6, 2, 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES') - , (8, 4, 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES') -; - -insert into invoice (invoice_id, company_id, invoice_number, contact_id, currency_code, payment_method_id) -values (10, 2, 'INV020001', 6, 'EUR', 222) - , (12, 4, 'INV040001', 8, 'EUR', 444) -; - -insert into tag (tag_id, company_id, name) -values (14, 2, 'web') - , (15, 2, 'design') - , (16, 4, 'product') - , (17, 4, 'development') - , (18, 4, 'something-else') - , (19, 4, 'design') -; - -insert into invoice_tag (invoice_id, tag_id) -values (10, 14) - , (10, 15) - , (12, 18) -; - -prepare invoice_tag_data as -select invoice_id, tag_id -from invoice_tag -; - -set role invoicer; -select is_empty('invoice_tag_data', 'Should show no data when cookie is not set yet'); -reset role; - -select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'); -select bag_eq( - 'invoice_tag_data', - $$ values ( 10, 14 ) - , ( 10, 15 ) - $$, - 'Should only list invoice tags of the companies where demo@tandem.blog is user of' -); -reset role; - -select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'); -select bag_eq( - 'invoice_tag_data', - $$ values ( 12, 18 ) - $$, - 'Should only list invoice tags of the companies where admin@tandem.blog is user of' -); -reset role; - -select set_cookie('not-a-cookie'); -select throws_ok( - 'invoice_tag_data', - '42501', 'permission denied for table invoice_tag', - 'Should not allow select to guest users' -); - - -reset role; -select * -from finish(); - -rollback; - diff --git a/test/product.sql b/test/product.sql index 499c657..4651992 100644 --- a/test/product.sql +++ b/test/product.sql @@ -5,7 +5,7 @@ reset client_min_messages; begin; -select plan(51); +select plan(56); set search_path to numerus, auth, public; @@ -58,6 +58,12 @@ select col_type_is('product', 'price', 'integer'); select col_not_null('product', 'price'); select col_hasnt_default('product', 'price'); +select has_column('product', 'tags'); +select col_type_is('product', 'tags', 'tag_name[]'); +select col_not_null('product', 'tags'); +select col_has_default('product', 'tags'); +select col_default_is('product', 'tags', '{}'); + select has_column('product', 'created_at'); select col_type_is('product', 'created_at', 'timestamp with time zone'); select col_not_null('product', 'created_at'); diff --git a/test/product_tag.sql b/test/product_tag.sql deleted file mode 100644 index aaa7f4e..0000000 --- a/test/product_tag.sql +++ /dev/null @@ -1,129 +0,0 @@ --- Test product_tag -set client_min_messages to warning; -create extension if not exists pgtap; -reset client_min_messages; - -begin; - -select plan(23); - -set search_path to numerus, auth, public; - -select has_table('product_tag'); -select has_pk('product_tag'); -select col_is_pk('product_tag', array['product_id', 'tag_id']); -select table_privs_are('product_tag', 'guest', array []::text[]); -select table_privs_are('product_tag', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']); -select table_privs_are('product_tag', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']); -select table_privs_are('product_tag', 'authenticator', array []::text[]); - -select has_column('product_tag', 'product_id'); -select col_is_fk('product_tag', 'product_id'); -select fk_ok('product_tag', 'product_id', 'product', 'product_id'); -select col_type_is('product_tag', 'product_id', 'integer'); -select col_not_null('product_tag', 'product_id'); -select col_hasnt_default('product_tag', 'product_id'); - -select has_column('product_tag', 'tag_id'); -select col_is_fk('product_tag', 'tag_id'); -select fk_ok('product_tag', 'tag_id', 'tag', 'tag_id'); -select col_type_is('product_tag', 'tag_id', 'integer'); -select col_not_null('product_tag', 'tag_id'); -select col_hasnt_default('product_tag', 'tag_id'); - - -set client_min_messages to warning; -truncate product_tag cascade; -truncate product cascade; -truncate tag cascade; -truncate company_user cascade; -truncate company cascade; -truncate payment_method cascade; -truncate auth."user" cascade; -reset client_min_messages; - -insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at) -values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') -; - -set constraints "company_default_payment_method_id_fkey" deferred; - -insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id) -values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222) - , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444) -; - -insert into payment_method (payment_method_id, company_id, name, instructions) -values (444, 4, 'cash', 'cash') - , (222, 2, 'cash', 'cash') -; - -set constraints "company_default_payment_method_id_fkey" immediate; - -insert into company_user (company_id, user_id) -values (2, 1) - , (4, 5) -; - -insert into product (product_id, company_id, name, description, price) -values (6, 2, 'Product 1', 'Description 1', 1200) - , (8, 4, 'Product 2', 'Description 2', 2400) -; - -insert into tag (tag_id, company_id, name) -values (14, 2, 'web') - , (15, 2, 'design') - , (16, 4, 'product') - , (17, 4, 'development') - , (18, 4, 'something-else') - , (19, 4, 'design') -; - -insert into product_tag (product_id, tag_id) -values (6, 14) - , (6, 15) - , (8, 18) -; - -prepare product_tag_data as -select product_id, tag_id -from product_tag -; - -set role invoicer; -select is_empty('product_tag_data', 'Should show no data when cookie is not set yet'); -reset role; - -select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'); -select bag_eq( - 'product_tag_data', - $$ values ( 6, 14 ) - , ( 6, 15 ) - $$, - 'Should only list product tags of the companies where demo@tandem.blog is user of' -); -reset role; - -select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'); -select bag_eq( - 'product_tag_data', - $$ values ( 8, 18 ) - $$, - 'Should only list product tags of the companies where admin@tandem.blog is user of' -); -reset role; - -select set_cookie('not-a-cookie'); -select throws_ok( - 'product_tag_data', - '42501', 'permission denied for table product_tag', - 'Should not allow select to guest users' -); - - -select * -from finish(); - -rollback; - diff --git a/test/tag.sql b/test/tag.sql deleted file mode 100644 index 4163d56..0000000 --- a/test/tag.sql +++ /dev/null @@ -1,139 +0,0 @@ --- Test tag -set client_min_messages to warning; -create extension if not exists pgtap; -reset client_min_messages; - -begin; - -select plan(33); - -set search_path to numerus, auth, public; - -select has_table('tag'); -select has_pk('tag' ); -select table_privs_are('tag', 'guest', array []::text[]); -select table_privs_are('tag', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']); -select table_privs_are('tag', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']); -select table_privs_are('tag', 'authenticator', array []::text[]); - -select has_sequence('tag_tag_id_seq'); -select sequence_privs_are('tag_tag_id_seq', 'guest', array[]::text[]); -select sequence_privs_are('tag_tag_id_seq', 'invoicer', array['USAGE']); -select sequence_privs_are('tag_tag_id_seq', 'admin', array['USAGE']); -select sequence_privs_are('tag_tag_id_seq', 'authenticator', array[]::text[]); - -select has_column('tag', 'tag_id'); -select col_is_pk('tag', 'tag_id'); -select col_type_is('tag', 'tag_id', 'integer'); -select col_not_null('tag', 'tag_id'); -select col_has_default('tag', 'tag_id'); -select col_default_is('tag', 'tag_id', 'nextval(''tag_tag_id_seq''::regclass)'); - -select has_column('tag', 'company_id'); -select col_is_fk('tag', 'company_id'); -select fk_ok('tag', 'company_id', 'company', 'company_id'); -select col_type_is('tag', 'company_id', 'integer'); -select col_not_null('tag', 'company_id'); -select col_hasnt_default('tag', 'company_id'); - -select has_column('tag', 'name'); -select col_type_is('tag', 'name', 'tag_name'); -select col_not_null('tag', 'name'); -select col_hasnt_default('tag', 'name'); -select col_is_unique('tag', array['company_id', 'name']); - - -set client_min_messages to warning; -truncate tag cascade; -truncate company_user cascade; -truncate company cascade; -truncate payment_method cascade; -truncate auth."user" cascade; -reset client_min_messages; - -insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at) -values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') - , (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') -; - -set constraints "company_default_payment_method_id_fkey" deferred; - -insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id) -values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222) - , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444) -; - -insert into payment_method (payment_method_id, company_id, name, instructions) -values (444, 4, 'cash', 'cash') - , (222, 2, 'cash', 'cash') -; - -set constraints "company_default_payment_method_id_fkey" immediate; - -insert into company_user (company_id, user_id) -values (2, 1) - , (4, 5) -; - -insert into tag (company_id, name) -values (2, 'web') - , (2, 'design') - , (4, 'product') - , (4, 'development') - , (4, 'something-else') - , (4, 'design') -; - -prepare tag_data as -select company_id, name -from tag -; - -set role invoicer; -select is_empty('tag_data', 'Should show no data when cookie is not set yet'); -reset role; - -select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'); -select bag_eq( - 'tag_data', - $$ values ( 2, 'web' ) - , ( 2, 'design' ) - $$, - 'Should only list tags of the companies where demo@tandem.blog is user of' -); -reset role; - -select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'); -select bag_eq( - 'tag_data', - $$ values (4, 'product' ) - , (4, 'development' ) - , (4, 'something-else' ) - , (4, 'design' ) - $$, - 'Should only list tags of the companies where admin@tandem.blog is user of' -); -reset role; - -select set_cookie('not-a-cookie'); -select throws_ok( - 'tag_data', - '42501', 'permission denied for table tag', - 'Should not allow select to guest users' -); -reset role; - -select throws_ok( $$ - insert into tag (company_id, name) - values (2, 'web') - $$, - '23505', 'duplicate key value violates unique constraint "tag_company_id_name_key"', - 'Should not allow repeated tag names within the same company' -); - - -select * -from finish(); - -rollback; - diff --git a/test/tag_contact.sql b/test/tag_contact.sql deleted file mode 100644 index e4f1761..0000000 --- a/test/tag_contact.sql +++ /dev/null @@ -1,124 +0,0 @@ --- Test tag_contact -set client_min_messages to warning; -create extension if not exists pgtap; -reset client_min_messages; - -begin; - -select plan(17); - -set search_path to numerus, auth, public; - -select has_function('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]']); -select function_lang_is('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'sql'); -select function_returns('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'void'); -select isnt_definer('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]']); -select volatility_is('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'volatile'); -select function_privs_are('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'guest', array []::text[]); -select function_privs_are('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'invoicer', array ['EXECUTE']); -select function_privs_are('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'admin', array ['EXECUTE']); -select function_privs_are('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'authenticator', array []::text[]); - - -set client_min_messages to warning; -truncate contact_tag cascade; -truncate tag cascade; -truncate contact cascade; -truncate payment_method cascade; -truncate company cascade; -reset client_min_messages; - -set constraints "company_default_payment_method_id_fkey" deferred; - -insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id) -values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111) -; - -insert into payment_method (payment_method_id, company_id, name, instructions) -values (111, 1, 'cash', 'cash') - , (112, 1, 'bank', 'send money to my bank account') -; - -set constraints "company_default_payment_method_id_fkey" immediate; - -insert into tag (tag_id, company_id, name) -values (10, 1, 'tag1') - , (11, 1, 'tag2') -; --- tag_contact uses the sequence and sometimes it would confict -alter sequence tag_tag_id_seq restart with 15; - -insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) -values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES') - , (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES') -; - -insert into contact_tag (contact_id, tag_id) -values (12, 10) - , (13, 11) -; - -prepare current_tags as -select contact_id, tag.name -from contact -join contact_tag using (contact_id) -join tag using (tag_id); - -select lives_ok( - $$ select tag_contact(1, 12, array['tag1']) $$, - 'Should be able to keep the same tags to the contact' -); - -select bag_eq( - 'current_tags', - $$ values (12, 'tag1') - , (13, 'tag2') - $$, - 'Should not have changed any contact tag' -); - -select lives_ok( - $$ select tag_contact(1, 12, array['tag1', 'tag2']) $$, - 'Should be able to add tag2 contact' -); - -select bag_eq( - 'current_tags', - $$ values (12, 'tag1') - , (12, 'tag2') - , (13, 'tag2') - $$, - 'Should have added tag2 to contact' -); - -select lives_ok( - $$ select tag_contact(1, 13, array['tag3']) $$, - 'Should be able to replace all tags of an contact with a new one' -); - -select bag_eq( - 'current_tags', - $$ values (12, 'tag1') - , (12, 'tag2') - , (13, 'tag3') - $$, - 'Should have set tag3 to contact' -); - -select lives_ok( - $$ select tag_contact(1, 12, array[]::tag_name[]) $$, - 'Should be able to remove all tags from an contact' -); - -select bag_eq( - 'current_tags', - $$ values (13, 'tag3') - $$, - 'Should have remove all tags from contact' -); - - -select * -from finish(); - -rollback; diff --git a/test/tag_invoice.sql b/test/tag_invoice.sql deleted file mode 100644 index fdf7862..0000000 --- a/test/tag_invoice.sql +++ /dev/null @@ -1,130 +0,0 @@ --- Test tag_invoice -set client_min_messages to warning; -create extension if not exists pgtap; -reset client_min_messages; - -begin; - -select plan(17); - -set search_path to numerus, auth, public; - -select has_function('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]']); -select function_lang_is('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'sql'); -select function_returns('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'void'); -select isnt_definer('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]']); -select volatility_is('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'volatile'); -select function_privs_are('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'guest', array []::text[]); -select function_privs_are('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'invoicer', array ['EXECUTE']); -select function_privs_are('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'admin', array ['EXECUTE']); -select function_privs_are('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'authenticator', array []::text[]); - - -set client_min_messages to warning; -truncate invoice_tag cascade; -truncate tag cascade; -truncate invoice cascade; -truncate contact cascade; -truncate payment_method cascade; -truncate company cascade; -reset client_min_messages; - -set constraints "company_default_payment_method_id_fkey" deferred; - -insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id) -values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111) -; - -insert into payment_method (payment_method_id, company_id, name, instructions) -values (111, 1, 'cash', 'cash') - , (112, 1, 'bank', 'send money to my bank account') -; - -set constraints "company_default_payment_method_id_fkey" immediate; - -insert into tag (tag_id, company_id, name) -values (10, 1, 'tag1') - , (11, 1, 'tag2') -; --- tag_invoice uses the sequence and sometimes it would confict -alter sequence tag_tag_id_seq restart with 15; - -insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) -values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES') - , (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES') -; - -insert into invoice (invoice_id, company_id, slug, invoice_number, invoice_date, contact_id, payment_method_id, currency_code) -values (15, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'INV1', '2023-03-10', 12, 111, 'EUR') - , (16, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'INV2', '2023-03-09', 13, 111, 'EUR') -; - -insert into invoice_tag (invoice_id, tag_id) -values (15, 10) - , (16, 11) -; - -prepare current_tags as -select invoice_id, tag.name -from invoice -join invoice_tag using (invoice_id) -join tag using (tag_id); - -select lives_ok( - $$ select tag_invoice(1, 15, array['tag1']) $$, - 'Should be able to keep the same tags to the invoice' -); - -select bag_eq( - 'current_tags', - $$ values (15, 'tag1') - , (16, 'tag2') - $$, - 'Should not have changed any invoice tag' -); - -select lives_ok( - $$ select tag_invoice(1, 15, array['tag1', 'tag2']) $$, - 'Should be able to add tag2 invoice' -); - -select bag_eq( - 'current_tags', - $$ values (15, 'tag1') - , (15, 'tag2') - , (16, 'tag2') - $$, - 'Should have added tag2 to invoice' -); - -select lives_ok( - $$ select tag_invoice(1, 16, array['tag3']) $$, - 'Should be able to replace all tags of an invoice with a new one' -); - -select bag_eq( - 'current_tags', - $$ values (15, 'tag1') - , (15, 'tag2') - , (16, 'tag3') - $$, - 'Should have set tag3 to invoice' -); - -select lives_ok( - $$ select tag_invoice(1, 15, array[]::tag_name[]) $$, - 'Should be able to remove all tags from an invoice' -); - -select bag_eq( - 'current_tags', - $$ values (16, 'tag3') - $$, - 'Should have remove all tags from invoice' -); - - -select * -from finish(); - -rollback; diff --git a/test/tag_product.sql b/test/tag_product.sql deleted file mode 100644 index 306c11f..0000000 --- a/test/tag_product.sql +++ /dev/null @@ -1,124 +0,0 @@ --- Test tag_product -set client_min_messages to warning; -create extension if not exists pgtap; -reset client_min_messages; - -begin; - -select plan(17); - -set search_path to numerus, auth, public; - -select has_function('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]']); -select function_lang_is('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'sql'); -select function_returns('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'void'); -select isnt_definer('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]']); -select volatility_is('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'volatile'); -select function_privs_are('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'guest', array []::text[]); -select function_privs_are('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'invoicer', array ['EXECUTE']); -select function_privs_are('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'admin', array ['EXECUTE']); -select function_privs_are('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'authenticator', array []::text[]); - - -set client_min_messages to warning; -truncate product_tag cascade; -truncate tag cascade; -truncate product cascade; -truncate payment_method cascade; -truncate company cascade; -reset client_min_messages; - -set constraints "company_default_payment_method_id_fkey" deferred; - -insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id) -values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111) -; - -insert into payment_method (payment_method_id, company_id, name, instructions) -values (111, 1, 'cash', 'cash') - , (112, 1, 'bank', 'send money to my bank account') -; - -set constraints "company_default_payment_method_id_fkey" immediate; - -insert into tag (tag_id, company_id, name) -values (10, 1, 'tag1') - , (11, 1, 'tag2') -; --- tag_product uses the sequence and sometimes it would confict -alter sequence tag_tag_id_seq restart with 15; - - -insert into product (product_id, company_id, name, description, price) -values (12, 1, 'Product 1', 'Description 1', 1200) - , (13, 1, 'Product 2', 'Description 2', 2400) -; - -insert into product_tag (product_id, tag_id) -values (12, 10) - , (13, 11) -; - -prepare current_tags as -select product_id, tag.name -from product -join product_tag using (product_id) -join tag using (tag_id); - -select lives_ok( - $$ select tag_product(1, 12, array['tag1']) $$, - 'Should be able to keep the same tags to the product' -); - -select bag_eq( - 'current_tags', - $$ values (12, 'tag1') - , (13, 'tag2') - $$, - 'Should not have changed any product tag' -); - -select lives_ok( - $$ select tag_product(1, 12, array['tag1', 'tag2']) $$, - 'Should be able to add tag2 product' -); - -select bag_eq( - 'current_tags', - $$ values (12, 'tag1') - , (12, 'tag2') - , (13, 'tag2') - $$, - 'Should have added tag2 to product' -); - -select lives_ok( - $$ select tag_product(1, 13, array['tag3']) $$, - 'Should be able to replace all tags of an product with a new one' -); - -select bag_eq( - 'current_tags', - $$ values (12, 'tag1') - , (12, 'tag2') - , (13, 'tag3') - $$, - 'Should have set tag3 to product' -); - -select lives_ok( - $$ select tag_product(1, 12, array[]::tag_name[]) $$, - 'Should be able to remove all tags from an product' -); - -select bag_eq( - 'current_tags', - $$ values (13, 'tag3') - $$, - 'Should have remove all tags from product' -); - -select * -from finish(); - -rollback; diff --git a/test/tag_relation.sql b/test/tag_relation.sql deleted file mode 100644 index 91f21d9..0000000 --- a/test/tag_relation.sql +++ /dev/null @@ -1,25 +0,0 @@ --- Test tag_relation -set client_min_messages to warning; -create extension if not exists pgtap; -reset client_min_messages; - -begin; - -set search_path to numerus, public; - -select plan(9); - -select has_function('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]']); -select function_lang_is('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'plpgsql'); -select function_returns('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'void'); -select isnt_definer('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]']); -select volatility_is('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'volatile'); -select function_privs_are('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'guest', array []::text[]); -select function_privs_are('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'invoicer', array ['EXECUTE']); -select function_privs_are('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'admin', array ['EXECUTE']); -select function_privs_are('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'authenticator', array []::text[]); - -select * -from finish(); - -rollback; diff --git a/verify/contact.sql b/verify/contact.sql index 75d9ea1..ef84481 100644 --- a/verify/contact.sql +++ b/verify/contact.sql @@ -16,6 +16,7 @@ select contact_id , province , postal_code , country_code + , tags , created_at from numerus.contact where false; diff --git a/verify/contact_tag.sql b/verify/contact_tag.sql deleted file mode 100644 index 34883a1..0000000 --- a/verify/contact_tag.sql +++ /dev/null @@ -1,13 +0,0 @@ --- Verify numerus:contact_tag on pg - -begin; - -select contact_id - , tag_id -from numerus.contact_tag -where false; - -select 1 / count(*) from pg_class where oid = 'numerus.contact_tag'::regclass and relrowsecurity; -select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.contact_tag'::regclass; - -rollback; diff --git a/verify/invoice.sql b/verify/invoice.sql index 88efecc..9321471 100644 --- a/verify/invoice.sql +++ b/verify/invoice.sql @@ -10,6 +10,7 @@ select invoice_id , contact_id , invoice_status , notes + , tags , payment_method_id , currency_code , created_at diff --git a/verify/invoice_tag.sql b/verify/invoice_tag.sql deleted file mode 100644 index 5a59afb..0000000 --- a/verify/invoice_tag.sql +++ /dev/null @@ -1,13 +0,0 @@ --- Verify numerus:invoice_tag on pg - -begin; - -select invoice_id - , tag_id -from numerus.invoice_tag -where false; - -select 1 / count(*) from pg_class where oid = 'numerus.invoice_tag'::regclass and relrowsecurity; -select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.invoice_tag'::regclass; - -rollback; diff --git a/verify/product.sql b/verify/product.sql index a791537..82ae26e 100644 --- a/verify/product.sql +++ b/verify/product.sql @@ -8,6 +8,7 @@ select product_id , name , description , price + , tags , created_at from numerus.product where false; diff --git a/verify/product_tag.sql b/verify/product_tag.sql deleted file mode 100644 index a008d85..0000000 --- a/verify/product_tag.sql +++ /dev/null @@ -1,13 +0,0 @@ --- Verify numerus:product_tag on pg - -begin; - -select product_id - , tag_id -from numerus.product_tag -where false; - -select 1 / count(*) from pg_class where oid = 'numerus.product_tag'::regclass and relrowsecurity; -select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.product_tag'::regclass; - -rollback; diff --git a/verify/tag.sql b/verify/tag.sql deleted file mode 100644 index e22603e..0000000 --- a/verify/tag.sql +++ /dev/null @@ -1,14 +0,0 @@ --- Verify numerus:tag on pg - -begin; - -select tag_id - , company_id - , name -from numerus.tag -where false; - -select 1 / count(*) from pg_class where oid = 'numerus.tag'::regclass and relrowsecurity; -select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.tag'::regclass; - -rollback; diff --git a/verify/tag_contact.sql b/verify/tag_contact.sql deleted file mode 100644 index b3f8bdf..0000000 --- a/verify/tag_contact.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Verify numerus:tag_contact on pg - -begin; - -select has_function_privilege('numerus.tag_contact(integer, integer, numerus.tag_name[])', 'execute'); - -rollback; diff --git a/verify/tag_invoice.sql b/verify/tag_invoice.sql deleted file mode 100644 index 6f47f03..0000000 --- a/verify/tag_invoice.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Verify numerus:tag_invoice on pg - -begin; - -select has_function_privilege('numerus.tag_invoice(integer, integer, numerus.tag_name[])', 'execute'); - -rollback; diff --git a/verify/tag_product.sql b/verify/tag_product.sql deleted file mode 100644 index cf3af5e..0000000 --- a/verify/tag_product.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Verify numerus:tag_product on pg - -begin; - -select has_function_privilege('numerus.tag_product(integer, integer, numerus.tag_name[])', 'execute'); - -rollback; diff --git a/verify/tag_relation.sql b/verify/tag_relation.sql deleted file mode 100644 index 2346fc2..0000000 --- a/verify/tag_relation.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Verify numerus:tag_relation on pg - -begin; - -select has_function_privilege('numerus.tag_relation(regclass, name, integer, integer, numerus.tag_name[])', 'execute'); - -rollback;