Skip to content

[POLY-25] Add tags field to listing schema with validation#30

Merged
jaydonkc merged 3 commits intodevfrom
feature/POLY-25-tag-w-validation
Feb 4, 2026
Merged

[POLY-25] Add tags field to listing schema with validation#30
jaydonkc merged 3 commits intodevfrom
feature/POLY-25-tag-w-validation

Conversation

@haiixin
Copy link
Copy Markdown
Collaborator

@haiixin haiixin commented Jan 31, 2026

Linked Issues

Linear: POLY-25

Summary

  • Added tags field to listing schema
  • Added validation functions for tags field
  • Tested the tags field is used correctly by creating test cases in listings.test.ts

How to Test

Steps to verify locally:

  • Run npm test to make sure that all test cases pass, especially test cases from listings.test.ts

Checklist

  • Tests added/updated (if applicable)
  • Lint/tests pass locally (npm run lint)
  • Docs updated (README/ADR/changelog if needed)
  • Follows conventional commit format
  • No merge conflicts with dev

Screenshots / Demos

(if UI or visible behavior - attach images, videos, or GIFs)

Summary by CodeRabbit

  • New Features

    • Listings now support optional tags for organization and discovery.
    • Tags are normalized (trimmed, lowercased) and deduplicated on submit.
    • Tags are limited to 5 per listing, max 20 characters each, and empty tags are rejected.
    • Tags are indexed to enable tag-based lookups.
  • Tests

    • Added comprehensive tests covering normalization, deduplication, validation, persistence, and retrieval of tags.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 31, 2026

📝 Walkthrough

Walkthrough

Adds optional tags to listings: schema, types, mutation args; enforces constraints (max 5 tags, 1–20 chars), normalizes (trim, lowercase, dedupe), persists normalized tags, and expands tests to cover validation and normalization behaviors.

Changes

Cohort / File(s) Summary
Schema & Types
backend/convex/schema.ts, packages/shared/types/listing.ts
Added optional tags: string[] to listings table and by_tag index; updated Listing and CreateListingInput interfaces to include optional tags.
Mutations & Validation
backend/convex/listings.ts
Added exported TAG_CONSTRAINTS, normalizeTags helper, and tag handling on createListing/updateListing (accept optional tags, validate, normalize, store normalizedTags, throw runtime errors for violations).
Tests
backend/convex/__tests__/listings.test.ts, backend/convex/__tests__/schema.test.ts
Introduced comprehensive tests for tag normalization, deduplication, empty-array acceptance, max-count/length validations, persistence checks; added tags to schema test required fields.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant API as Convex Mutation
    participant DB as Convex DB

    rect rgba(0,128,0,0.5)
    Client->>API: call createListing/updateListing(tags?)
    end

    rect rgba(0,0,255,0.5)
    API->>API: validate tags against TAG_CONSTRAINTS\n(normalize: trim, lowercase, dedupe, filter, cap)
    API-->>Client: error if validation fails
    end

    rect rgba(255,165,0,0.5)
    API->>DB: persist listing with normalizedTags
    DB-->>API: confirmation
    end

    API-->>Client: return created/updated listing (with normalized tags)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰✨ I hopped through fields and trimmed each tag,
Lowercased whispers, duplicates I bag,
Five little friends, twenty chars or less,
Stored neat and humble in the listing nest. 🏷️

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description covers the summary and testing instructions but is missing the issue reference in the correct format and has incomplete checklist items. Update the Linked Issues section to use the format 'Closes #' and mark applicable checklist items as completed or update their status.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a tags field to the listing schema with validation, which matches the primary objective of this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/POLY-25-tag-w-validation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/convex/__tests__/schema.test.ts (1)

44-55: ⚠️ Potential issue | 🟡 Minor

Add coverage for the new by_tag index.
The schema adds by_tag, but the test doesn’t assert it yet.

Suggested update
     expect(indexNames).toContain('by_status');
     expect(indexNames).toContain('by_category');
     expect(indexNames).toContain('by_status_category');
     expect(indexNames).toContain('by_status_createdAt');
+    expect(indexNames).toContain('by_tag');
@@
     const categoryIndex = listings.indexes.find((i: any) => i.indexDescriptor === 'by_category');
     expect(categoryIndex.fields).toEqual(['category']);
+
+    const tagIndex = listings.indexes.find((i: any) => i.indexDescriptor === 'by_tag');
+    expect(tagIndex.fields).toEqual(['tags']);
🤖 Fix all issues with AI agents
In `@backend/convex/__tests__/listings.test.ts`:
- Around line 286-354: The tests named like "updateListing removes duplicate
tags" etc. currently call api.listings.createListing; change them to exercise
the update path by first creating a listing (using createListing) to obtain
listingId and then calling api.listings.updateListing with that listingId and
the test-specific tags; update the mutation calls in each affected test to
asOwner.mutation(api.listings.updateListing, { listingId, ... }) and keep
assertions on ctx.db.get(listingId) the same so the update validations are
actually executed.
🧹 Nitpick comments (2)
backend/convex/listings.ts (2)

75-84: Use TAG_CONSTRAINTS in normalization + fix typo.
This avoids drift between constraints and normalization, and fixes the comment typo.

Suggested change
-// Normalize tages to lowercase and within 1-20 characters exclusive
+// Normalize tags to lowercase and within allowed lengths
 function normalizeTags(tags: string[]): string[] {
   return [
     ...new Set(
       tags
         .map((tag) => tag.trim().toLowerCase())
-        .filter((tag) => tag.length >= 1 && tag.length <= 20)
+        .filter(
+          (tag) =>
+            tag.length >= TAG_CONSTRAINTS.MIN_TAG_LENGTH &&
+            tag.length <= TAG_CONSTRAINTS.MAX_TAG_LENGTH
+        )
     ),
-  ].slice(0, 5);
+  ].slice(0, TAG_CONSTRAINTS.MAX_TAGS);
 }

217-252: DRY tag validation across create/update.
The validation block is duplicated; consider extracting a helper to prevent drift and reduce maintenance.

Suggested refactor
+function validateTags(tags?: string[]) {
+  if (!tags) return;
+  if (tags.length > TAG_CONSTRAINTS.MAX_TAGS) {
+    throw new Error(`Maximum ${TAG_CONSTRAINTS.MAX_TAGS} tags allowed`);
+  }
+  for (const tag of tags) {
+    const trimmed = tag.trim();
+    if (!trimmed) {
+      throw new Error('Empty tags are not allowed');
+    }
+    if (trimmed.length > TAG_CONSTRAINTS.MAX_TAG_LENGTH) {
+      throw new Error(`Tags must be ${TAG_CONSTRAINTS.MAX_TAG_LENGTH} characters or less`);
+    }
+  }
+}
-    if (args.tags) {
-      if (args.tags.length > TAG_CONSTRAINTS.MAX_TAGS) {
-        throw new Error(`Maximum ${TAG_CONSTRAINTS.MAX_TAGS} tags allowed`);
-      }
-
-      for (const tag of args.tags) {
-        const trimmed = tag.trim();
-        if (!trimmed) {
-          throw new Error('Empty tags are not allowed');
-        }
-        if (trimmed.length > TAG_CONSTRAINTS.MAX_TAG_LENGTH) {
-          throw new Error(`Tags must be ${TAG_CONSTRAINTS.MAX_TAG_LENGTH} characters or less`);
-        }
-      }
-    }
+    validateTags(args.tags);
-    if (args.tags) {
-      if (args.tags.length > TAG_CONSTRAINTS.MAX_TAGS) {
-        throw new Error(`Maximum ${TAG_CONSTRAINTS.MAX_TAGS} tags allowed`);
-      }
-
-      for (const tag of args.tags) {
-        const trimmed = tag.trim();
-        if (!trimmed) {
-          throw new Error('Empty tags are not allowed');
-        }
-        if (trimmed.length > TAG_CONSTRAINTS.MAX_TAG_LENGTH) {
-          throw new Error(`Tags must be ${TAG_CONSTRAINTS.MAX_TAG_LENGTH} characters or less`);
-        }
-      }
-    }
+    validateTags(args.tags);

Also applies to: 266-324

Comment thread backend/convex/__tests__/listings.test.ts
@jaydonkc jaydonkc merged commit dc89a08 into dev Feb 4, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants