Only index cards and comments if their card is `published?`, using a
new method `searchable?` that is implemented on both Card and Comment.
This prevents draft cards and their comments from being indexed.
When drafts are published, the existing `update_in_search_index`
callback creates the record via `upsert!`, or else ensures unpublished
records are removed from the index.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Card#commentable? method that returns true only for published cards.
- CardsController#create ensures that the card is commentable.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
We're about to make a change to assert that draft cards are not added
to the search index, and so let's make the intention behind these
tests clear first.
To prevent overflowing the columns in the search index tables (and also
just to avoid creating massive indexes), let's limit the searchable
content to the first 32KB.
- Update `Card::Closeable` to destroy associated "not now" state during close.
- Add new tests to verify closure behavior for different column types, including "not now".
As the tests for it could lead to confusion where it seems drafted cards
are not accessible to someone with access to the board and with the
direct drafted card URL.
* Add test coverage for with_golden_first scope
Test verifies that the with_golden_first scope correctly orders
golden cards before non-golden cards in query results.
* Refactor golden test: use instance variables, add subordering coverage
Extract @golden and @non_golden fixtures into setup for reuse across
all tests. Simplify with_golden_first test to verify both primary
ordering (golden before non-golden) and subordering preservation.
---------
Co-authored-by: Jeremy Daer <jeremy@37signals.com>
* Allow Card#last_updated_at to be set
This is useful when doing an import from another system. I'm currently
working on a script to import our Github issues into Fizzy.
This is discussed in
https://github.com/basecamp/fizzy/pull/2056#discussion_r2609560246
* Add nil fallback and expand test coverage for last_active_at
Adds a safety fallback to Time.current if created_at is unexpectedly nil
during card creation.
Test coverage to verify:
* last_active_at defaults to created_at when not provided
* last_active_at can be updated via API on existing cards
* import workflow where last_active_at is restored after comments
* publishing doesn't overwrite explicit last_active_at values
---------
Co-authored-by: Jeremy Daer <jeremy@37signals.com>
We missed this one when we went to MySQL. This can results in cards tagged with cards
from other accounts. No data leaked though: the symptom is that you see the card
tagged as expected but you don't see the tag in the menu.
- Adds a button in Account Settings where you can request a ZIP export of your
Fizzy data
- Export files are created in the background. When ready, a link to
download them is sent to the requester.
- Exports expire after 24 hours. And are limited to 10 per day.
The problem was that publishing a card with `#publish` was tracking the event
after updating the status, which was clearing the saved changes and preventing the
code from detecting the mention.
See:
https://app.fizzy.do/5986089/cards/2835
* main: (116 commits)
Ensure avatar thumbnails are square
Update useragent to recognize twitterbot/facebot
Add defensive styles for non-square avatar images
Update test for copy changes
Missed commit
AI: standardize on https://agents.md
Make it clear this is just notifications, not comprehensive activity
AI: configure MCP servers for Chrome, Grafana, and Sentry (#1727)
Allow requests from Google Image Proxy
Update to basecamp's useragent fork
Clean up a little bit the CSRF reporting code
Claude: production observability guidance (#1725)
Prevent autoscroll to the root columns container to prevent jump on page load
Include full name string so you can type your name to filter
Prioritize current user and assigned users in assignment dropdown
Check and report on Sec-Fetch-Site header for forgery protection
bundle update
Bump bootsnap from 1.18.6 to 1.19.0
Bump rails from `077c3ad` to `17f6e00`
Fix cards getting stuck in edit mode
...
It doesn't actually work, and even if we could make it work reliably
we are better off if the records always know to go to the right shard.
It does make the interface a bit more complicated as we need to select
the right shard class with `for(account_id)`.
Instead we'll compute the table name dynamically based on
Current.account where needed. Also we'll prevent searchable records
from being saved if Current.account is not set, otherwise the after
commit callbacks will fail.
The check-then-act pattern in `register_activity_spike` has been
replaced with `find_or_create_by!` to eliminate the race condition
that could lead to creating multiple activity spikes for a card. To
support this change, the `card_id` index on `cards_activity_spikes`
has been made unique.
ref: https://app.fizzy.do/5986089/cards/3063
Lean on ActiveRecord models for searching and strip out the raw SQL.
Replaces the search_index_* tables with sharded search_records_* tables
as that allows us to use a Search::Record model name.
A Class is dynamically created for each record table shard so that we
and we can access it via the Search::Record.for_account(account_id)
method.
In order for model ordering to work as expected in tests, we need to
keep two properties:
- Fixtures are all created in the past
- Models sort in the order that they were created
This allows us to do things like this:
post cards_path, params: { ... }
created_card = Card.last
When using UUIDv7 PKs rather than sequential integers, we have to make
sure a couple of things happen in order for this still to be true:
- Fixtures should generate deterministic IDs that translate to UUIDs
that would have been created in the past (i.e. before today)
- Newly created objects must have enough precision in their timestamps
so that they sort in the order they were created, and their random
component doesn't come into play.
To solve this, we use the deterministic numeric ID as a number of
milliseconds after an early year. And we ensure that the new timestamps
we create have sub-millisecond precision.
These are mainly because the fixture's UUIDs are deterministic rather
than time-sortable, and more places where we need to correct the fixture
IDs as well.
Create search_index_0 to search_index_15 tables and shard each index by
account id. MySQL has no ability to pre-filter fulltext indexes by
another field so this is the best bet for improving performance.
Each fulltest index internally creates 11 sub tables (see
https://dev.mysql.com/doc/refman/8.4/en/innodb-fulltext-index.html) so
actually we have 192 tables in total here.
The search_index table name is generated dynamically based on the
account_id.
Removes the creating status completely as well as the abandoned cards system. It will
always resume drafts if they exist, for a given collection and user.
https://app.box-car.com/5986089/cards/2489