diff --git a/app/models/note.rb b/app/models/note.rb index 6d8ca078fa..18a104c5cc 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -26,6 +26,8 @@ class Note < ApplicationRecord has_many :subscriptions, :class_name => "NoteSubscription" has_many :subscribers, :through => :subscriptions, :source => :user + has_many :note_tags + validates :id, :uniqueness => true, :presence => { :on => :update }, :numericality => { :on => :update, :only_integer => true } validates :latitude, :longitude, :numericality => { :only_integer => true } diff --git a/app/models/note_tag.rb b/app/models/note_tag.rb new file mode 100644 index 0000000000..36d3b2c552 --- /dev/null +++ b/app/models/note_tag.rb @@ -0,0 +1,20 @@ +# == Schema Information +# +# Table name: note_tags +# +# note_id :bigint(8) not null, primary key +# k :string default(""), not null, primary key +# v :string default(""), not null +# +# Foreign Keys +# +# note_tags_id_fkey (note_id => notes.id) +# + +class NoteTag < ApplicationRecord + belongs_to :note + + validates :note, :associated => true + validates :k, :v, :allow_blank => true, :length => { :maximum => 255 }, :characters => true + validates :k, :uniqueness => { :scope => :note_id } +end diff --git a/db/migrate/20241030122707_create_note_tags.rb b/db/migrate/20241030122707_create_note_tags.rb new file mode 100644 index 0000000000..46f7cb2055 --- /dev/null +++ b/db/migrate/20241030122707_create_note_tags.rb @@ -0,0 +1,12 @@ +class CreateNoteTags < ActiveRecord::Migration[7.2] + def change + # Create a table, primary and foreign keys + create_table :note_tags, :primary_key => [:note_id, :k] do |t| + t.column "note_id", :bigint, :null => false + t.column "k", :string, :default => "", :null => false + t.column "v", :string, :default => "", :null => false + + t.foreign_key :notes, :column => :note_id, :name => "note_tags_id_fkey" + end + end +end diff --git a/db/structure.sql b/db/structure.sql index 9679e0b925..280afa536b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1061,6 +1061,17 @@ CREATE TABLE public.note_subscriptions ( ); +-- +-- Name: note_tags; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.note_tags ( + note_id bigint NOT NULL, + k character varying DEFAULT ''::character varying NOT NULL, + v character varying DEFAULT ''::character varying NOT NULL +); + + -- -- Name: notes; Type: TABLE; Schema: public; Owner: - -- @@ -2028,6 +2039,14 @@ ALTER TABLE ONLY public.note_subscriptions ADD CONSTRAINT note_subscriptions_pkey PRIMARY KEY (user_id, note_id); +-- +-- Name: note_tags note_tags_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.note_tags + ADD CONSTRAINT note_tags_pkey PRIMARY KEY (note_id, k); + + -- -- Name: notes notes_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3210,6 +3229,14 @@ ALTER TABLE ONLY public.note_comments ADD CONSTRAINT note_comments_note_id_fkey FOREIGN KEY (note_id) REFERENCES public.notes(id); +-- +-- Name: note_tags note_tags_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.note_tags + ADD CONSTRAINT note_tags_id_fkey FOREIGN KEY (note_id) REFERENCES public.notes(id); + + -- -- Name: redactions redactions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3397,6 +3424,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('23'), ('22'), ('21'), +('20241030122707'), ('20241023004427'), ('20241022141247'), ('20240913171951'), diff --git a/test/factories/note_tags.rb b/test/factories/note_tags.rb new file mode 100644 index 0000000000..f7d90e002d --- /dev/null +++ b/test/factories/note_tags.rb @@ -0,0 +1,8 @@ +FactoryBot.define do + factory :note_tag do + sequence(:k) { |n| "Key #{n}" } + sequence(:v) { |n| "Value #{n}" } + + note + end +end diff --git a/test/models/note_tag_test.rb b/test/models/note_tag_test.rb new file mode 100644 index 0000000000..058d68eb87 --- /dev/null +++ b/test/models/note_tag_test.rb @@ -0,0 +1,49 @@ +require "test_helper" + +class NoteTagTest < ActiveSupport::TestCase + def test_length_key_valid + tag = create(:note_tag) + [0, 255].each do |i| + tag.k = "k" * i + assert_predicate tag, :valid? + end + end + + def test_length_value_valid + tag = create(:note_tag) + [0, 255].each do |i| + tag.v = "v" * i + assert_predicate tag, :valid? + end + end + + def test_length_key_invalid + tag = create(:note_tag) + tag.k = "k" * 256 + assert_not_predicate tag, :valid?, "Key should be too long" + assert_predicate tag.errors[:k], :any? + end + + def test_length_value_invalid + tag = create(:note_tag) + tag.v = "v" * 256 + assert_not_predicate tag, :valid?, "Value should be too long" + assert_predicate tag.errors[:v], :any? + end + + def test_orphaned_tag_invalid + tag = create(:note_tag) + tag.note = nil + assert_not_predicate tag, :valid?, "Orphaned tag should be invalid" + assert_predicate tag.errors[:note], :any? + end + + def test_uniqueness + existing = create(:note_tag) + tag = build(:note_tag, :note => existing.note, :k => existing.k, :v => existing.v) + assert_predicate tag, :new_record? + assert_not_predicate tag, :valid? + assert_raise(ActiveRecord::RecordInvalid) { tag.save! } + assert_predicate tag, :new_record? + end +end