Skip to content

feat(artist): add CRUD endpoints for artists#51

Merged
alikia2x merged 2 commits into
developfrom
feat/alikia-43-artists
Apr 20, 2026
Merged

feat(artist): add CRUD endpoints for artists#51
alikia2x merged 2 commits into
developfrom
feat/alikia-43-artists

Conversation

@alikia2x

@alikia2x alikia2x commented Apr 20, 2026

Copy link
Copy Markdown
Member

Changes

Implement a complete CRUD API for managing artists in the catalog.

  • Add GET /v2/artist/:id endpoint for retrieving artist details
  • Add POST /v2/artist endpoint for creating new artists (auth required)
  • Add PATCH /v2/artist/:id endpoint for updating artist metadata (auth required)
  • Add DELETE /v2/artist/:id endpoint for soft-deleting artists (auth required)
  • Add Zod schemas for request/response validation in packages/core
  • Add E2E tests covering all endpoints with 15 test cases

Related

Implement a complete CRUD API for managing artists in the catalog.

Changes:
- Add GET /v2/artist/:id endpoint for retrieving artist details
- Add POST /v2/artist endpoint for creating new artists (auth required)
- Add PATCH /v2/artist/:id endpoint for updating artist metadata (auth required)
- Add DELETE /v2/artist/:id endpoint for soft-deleting artists (auth required)
- Add Zod schemas for request/response validation in packages/core
- Add E2E tests covering all endpoints with 15 test cases
@coderabbitai

coderabbitai Bot commented Apr 20, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@alikia2x has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 55 minutes and 23 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 55 minutes and 23 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b5b75ede-c69f-4c95-b6e6-673840a914c1

📥 Commits

Reviewing files that changed from the base of the PR and between 5c370aa and 2bc4cdb.

📒 Files selected for processing (2)
  • apps/backend/src/handlers/catalog/artist/create.ts
  • apps/backend/tests/artist.test.ts
📝 Walkthrough

Walkthrough

This PR implements complete CRUD functionality for artists by adding four REST endpoints (GET /v2/artist/:id, POST /v2/artist, PATCH /v2/artist/:id, DELETE /v2/artist/:id), supporting business logic layers (service, repository, DTOs), and comprehensive E2E test coverage. The handlers are wired into the main application and follow the existing architectural patterns.

Changes

Cohort / File(s) Summary
Artist Route Handlers
apps/backend/src/handlers/catalog/artist/create.ts, delete.ts, get.ts, update.ts
Added four authenticated/documented Elysia route handlers for artist CRUD operations with request validation (Zod schemas), tracing instrumentation, and OpenAPI metadata. Create returns 201, retrieve/update return 200, delete returns 204.
Handler Composition & Integration
apps/backend/src/handlers/catalog/artist/index.ts, apps/backend/src/handlers/index.ts, apps/backend/src/index.ts
Created artistHandler aggregate, re-exported from handlers index, and registered in main app's Elysia chain after engineHandler.
Artist Core Module
packages/core/src/modules/catalog/artist/dto.ts, repository.interface.ts, repository.ts, service.ts, container.ts, index.ts
Implemented data transfer objects with Zod schemas, repository interface with CRUD method signatures, concrete repository using Prisma with soft-delete support, service layer with error handling and tracing, and dependency injection container wiring all components.
Catalog Module Export
packages/core/src/modules/catalog/index.ts
Added re-export of new artist module alongside existing song and engine modules.
E2E Test Suite
apps/backend/tests/artist.test.ts
Comprehensive test coverage for all four endpoints including authentication, validation, soft-delete behavior, localized fields (name/description/language), alias array handling, and 404/401 error scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The pull request successfully implements all CRUD endpoints specified in issue #43, including GET, POST, PATCH, and DELETE routes with proper authentication, validation schemas, DTOs, and comprehensive E2E tests.
Out of Scope Changes check ✅ Passed All code changes are directly aligned with the linked issue #43 objectives. The pull request adds the artist module infrastructure, handlers, schemas, repository, service, and tests without introducing unrelated modifications.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/alikia-43-artists

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/backend/src/handlers/catalog/artist/create.ts`:
- Around line 1-33: The file is not formatted to the project's code style; run
the repository formatter on the new handler to match CI expectations and commit
the result. Specifically, format this file containing artistCreateHandler (which
uses authMiddleware and calls artistService.create) with the project's
configured formatter (e.g., run the configured Prettier/format script or your
editor's format command), re-run lint/tests, and stage the formatted changes so
the updated file passes the Test and Lint check.

In `@apps/backend/tests/artist.test.ts`:
- Line 1: The file's import line ("import { afterAll, beforeAll, describe,
expect, test } from \"bun:test\";") is not formatted per the project's
configured formatter; run the repository's formatter (e.g., the project's format
script or configured tool) on this test file, apply the resulting changes, and
commit them so the file matches the formatter output.
- Around line 16-20: The test teardown in afterAll currently removes sessions
and users but not artists, causing leaked artist rows; update the afterAll block
to also delete artists by adding an awaited prisma.artist.deleteMany() call
(e.g., call await prisma.artist.deleteMany(); alongside await
prisma.session.deleteMany() and await prisma.user.deleteMany()) before awaiting
prisma.$disconnect() so all created artist rows are cleaned up after the suite.

In `@packages/core/src/modules/catalog/artist/dto.ts`:
- Around line 24-26: ArtistResponseSchema currently only omits deletedAt but
still exposes userId (and possibly the user relation) which leaks account
association; update ArtistResponseSchema (and ensure ArtistDetailsResponseSchema
if it aliases it) to omit userId and, if ArtistSchema exposes a user relation,
omit user as well by changing the .omit(...) call to include userId and user so
public GET /v2/artist/:id responses never include those fields.

In `@packages/core/src/modules/catalog/artist/repository.ts`:
- Around line 45-62: The update() and softDelete() repository methods currently
use id-only where clauses (in methods update and softDelete), which permits a
race where a concurrent soft-delete slips in; change their Prisma update where
filters to include active-record predicate deletedAt: null (e.g., where: { id,
deletedAt: null }) so the write only succeeds on non-deleted rows, or
alternatively refactor to a single atomic repository operation that both checks
deletedAt is null and performs the update/soft-delete in one call (preserve
usage of update(id: ArtistId, ...) and softDelete(id: ArtistId, ...) signatures
and the client variable tx ?? this.prisma).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 290f7798-44dc-4b32-adc7-bdf26cde8d7d

📥 Commits

Reviewing files that changed from the base of the PR and between 0a714c2 and 5c370aa.

📒 Files selected for processing (15)
  • apps/backend/src/handlers/catalog/artist/create.ts
  • apps/backend/src/handlers/catalog/artist/delete.ts
  • apps/backend/src/handlers/catalog/artist/get.ts
  • apps/backend/src/handlers/catalog/artist/index.ts
  • apps/backend/src/handlers/catalog/artist/update.ts
  • apps/backend/src/handlers/index.ts
  • apps/backend/src/index.ts
  • apps/backend/tests/artist.test.ts
  • packages/core/src/modules/catalog/artist/container.ts
  • packages/core/src/modules/catalog/artist/dto.ts
  • packages/core/src/modules/catalog/artist/index.ts
  • packages/core/src/modules/catalog/artist/repository.interface.ts
  • packages/core/src/modules/catalog/artist/repository.ts
  • packages/core/src/modules/catalog/artist/service.ts
  • packages/core/src/modules/catalog/index.ts

Comment thread apps/backend/src/handlers/catalog/artist/create.ts
Comment thread apps/backend/tests/artist.test.ts
Comment on lines +16 to +20
afterAll(async () => {
await prisma.session.deleteMany();
await prisma.user.deleteMany();
await prisma.$disconnect();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clean up artists in afterAll as well.

This suite creates artist rows but only removes them in beforeAll, so later test suites sharing the same database can observe leaked state.

Proposed cleanup
 afterAll(async () => {
 	await prisma.session.deleteMany();
+	await prisma.artist.deleteMany();
 	await prisma.user.deleteMany();
 	await prisma.$disconnect();
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
afterAll(async () => {
await prisma.session.deleteMany();
await prisma.user.deleteMany();
await prisma.$disconnect();
});
afterAll(async () => {
await prisma.session.deleteMany();
await prisma.artist.deleteMany();
await prisma.user.deleteMany();
await prisma.$disconnect();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/backend/tests/artist.test.ts` around lines 16 - 20, The test teardown in
afterAll currently removes sessions and users but not artists, causing leaked
artist rows; update the afterAll block to also delete artists by adding an
awaited prisma.artist.deleteMany() call (e.g., call await
prisma.artist.deleteMany(); alongside await prisma.session.deleteMany() and
await prisma.user.deleteMany()) before awaiting prisma.$disconnect() so all
created artist rows are cleaned up after the suite.

Comment on lines +24 to +26
export const ArtistResponseSchema = ArtistSchema.omit({ deletedAt: true });

export const ArtistDetailsResponseSchema = ArtistResponseSchema;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not expose userId in public artist responses.

ArtistResponseSchema only strips deletedAt, but the Artist model also includes userId. Since GET /v2/artist/:id is public, this can leak the account/user association for an artist unless the service manually removes it everywhere.

🛡️ Proposed response schema tightening
-export const ArtistResponseSchema = ArtistSchema.omit({ deletedAt: true });
+export const ArtistResponseSchema = ArtistSchema.omit({
+	deletedAt: true,
+	userId: true,
+});

If ArtistSchema also exposes the user relation, omit that here as well.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const ArtistResponseSchema = ArtistSchema.omit({ deletedAt: true });
export const ArtistDetailsResponseSchema = ArtistResponseSchema;
export const ArtistResponseSchema = ArtistSchema.omit({
deletedAt: true,
userId: true,
});
export const ArtistDetailsResponseSchema = ArtistResponseSchema;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/modules/catalog/artist/dto.ts` around lines 24 - 26,
ArtistResponseSchema currently only omits deletedAt but still exposes userId
(and possibly the user relation) which leaks account association; update
ArtistResponseSchema (and ensure ArtistDetailsResponseSchema if it aliases it)
to omit userId and, if ArtistSchema exposes a user relation, omit user as well
by changing the .omit(...) call to include userId and user so public GET
/v2/artist/:id responses never include those fields.

Comment on lines +45 to +62
async update(id: ArtistId, input: UpdateArtistRequestDto, tx?: TxClient) {
const client = tx ?? this.prisma;

return transformPrismaResult(
await client.artist.update({
where: { id },
data: input,
omit: { deletedAt: true },
})
);
}

async softDelete(id: ArtistId, tx?: TxClient) {
const client = tx ?? this.prisma;
await client.artist.update({
where: { id },
data: { deletedAt: new Date() },
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether artist write operations are guarded by deletedAt/null checks.
# Expected: current output shows update writes using id-only where clauses.
rg -n -C4 'client\.artist\.update\(|where:\s*\{\s*id\s*\}|deletedAt' packages/core/src/modules/catalog/artist/repository.ts packages/core/src/modules/catalog/artist/service.ts

Repository: project-cvsa/cvsa

Length of output: 3984


🏁 Script executed:

cat packages/core/src/modules/catalog/artist/service.ts

Repository: project-cvsa/cvsa

Length of output: 1664


Add active-record predicates to update and softDelete writes.

The update() and softDelete() methods use id-only where clauses, creating a race condition. The service layer checks existence via getById() (which includes deletedAt: null), then calls the repository write unconditionally. A concurrent delete between these steps causes the original request to succeed against an already soft-deleted record. Add deletedAt: null to both where clauses, or combine the existence check and write into a single atomic repository operation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/modules/catalog/artist/repository.ts` around lines 45 - 62,
The update() and softDelete() repository methods currently use id-only where
clauses (in methods update and softDelete), which permits a race where a
concurrent soft-delete slips in; change their Prisma update where filters to
include active-record predicate deletedAt: null (e.g., where: { id, deletedAt:
null }) so the write only succeeds on non-deleted rows, or alternatively
refactor to a single atomic repository operation that both checks deletedAt is
null and performs the update/soft-delete in one call (preserve usage of
update(id: ArtistId, ...) and softDelete(id: ArtistId, ...) signatures and the
client variable tx ?? this.prisma).

@alikia2x alikia2x merged commit 754117c into develop Apr 20, 2026
2 checks passed
@alikia2x alikia2x deleted the feat/alikia-43-artists branch April 20, 2026 10:56
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.

1 participant