Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 49 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,34 @@ remain compatible. Database access uses Prisma 7 with the PostgreSQL driver
adapter. Domain events continue to be sent through the existing Bus API wrapper;
this service does not connect to Kafka directly.

The API runtime client is Prisma 7. The checked-in
`packages/challenge-prisma-client` artifact remains on Prisma 6 for its existing
downstream consumers, so the root generation command intentionally targets only
the API client. Upgrading that shared artifact requires a coordinated downstream
release.
The API runtime client and the checked-in external client use Prisma 7. The
external client has a stable package wrapper at
`packages/challenge-prisma-client`; generated Prisma files live below its
`generated` directory so regeneration cannot overwrite the public contract.

## External Prisma client

Services that need to aggregate challenge data directly can install the
`packages/challenge-prisma-client` Git subdirectory as
`@topcoder/challenge-api-v6`. The package exports all generated challenge
models, enums, Prisma helpers, and `PrismaClient`, plus this supported factory:

```ts
import { createChallengePrismaClient } from '@topcoder/challenge-api-v6';

const challenges = createChallengePrismaClient(process.env.CHALLENGE_DATABASE_URL, {
log: ['warn', 'error'],
});
```

`createChallengePrismaClient(connectionString, options?)` creates a Prisma 7
PostgreSQL driver adapter, preserves the optional `schema` query parameter in
the connection URL, and returns a disconnected client that connects lazily on
its first query. Call `$disconnect()` during application shutdown. The factory
throws `TypeError` when `connectionString` is empty or not a string; Prisma may
raise its normal configuration and database errors while creating or using the
client. Connection-defining `adapter` and `accelerateUrl` options are owned by
the factory and intentionally excluded from its options type.

## Configuration

Expand Down Expand Up @@ -90,7 +113,7 @@ configuration parameters.
Run `nvm use` before pnpm commands. Make sure `DATABASE_URL` is set before any
database operation or application startup.

1. Install dependencies and generate the Prisma client: `pnpm install`
1. Install dependencies and generate both Prisma clients: `pnpm install`
2. Build the API: `pnpm build`
3. Create or update local database tables: `pnpm create-tables`
4. Seed tables: `pnpm seed-tables`
Expand Down Expand Up @@ -130,7 +153,8 @@ database operation or application startup.
DATABASE_URL=
```

Then run `pnpm install`. The postinstall hook generates the Prisma 7 client.
Then run `pnpm install`. The postinstall hook generates both the internal and
external Prisma 7 clients.

2. 🚢 Start docker-compose with services which are required to start Topcoder Challenges API locally

Expand Down Expand Up @@ -239,6 +263,13 @@ To run unit tests alone
pnpm test
```

To smoke-test the packaged external Prisma factory without connecting to a
database:

```bash
pnpm test:external-client
```

To run unit tests with coverage report

```bash
Expand Down Expand Up @@ -310,6 +341,17 @@ Refer to the verification document `Verification.md`
bypass. Any update that starts in or transitions to a completed or cancelled status cannot change
the effective `is_test_challenge` value; omitting metadata preserves it. Normal authorization
checks still apply.
- Role-specific member competition searches use
`GET /v6/challenges?memberId={memberId}&resourceRoleId={resourceRoleUuid}`.
`resourceRoleId` is an exact UUID and requires `memberId`. The resource role is
applied before all challenge filters, global sorting, totals, and pagination;
omitting it preserves the existing any-resource behavior of `memberId`. This
public query only narrows results and never grants access: anonymous callers
retain anonymous visibility, and authenticated callers remain subject to
whitelist, group, and task rules based on the caller. For “My competitions,”
pass the configured Submitter resource-role UUID. Deploy migration
`20260813130000_add_role_to_member_access_view` before this service version,
because the generated Prisma client expects the view's new `roleId` column.
- API base configuration points to v6 in dev/local and v5 in prod (for compatibility):
- Dev: `work-manager/config/constants/development.js`.
- Local: `work-manager/config/constants/local.js`.
Expand Down
12 changes: 12 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ paths:
- Challenges
description: |
Retrieve challenges in the system.

`resourceRoleId` may be combined with `memberId` to restrict the member's
resource-associated challenges to one UUID resource role before challenge
filtering, sorting, totals, and pagination. It does not grant access: results
remain subject to the caller's whitelist, group, and task visibility. Anonymous
callers receive only challenges visible to anonymous users.
security:
- bearer: []
produces:
Expand Down Expand Up @@ -354,6 +360,12 @@ paths:
description: Filter challenges memberId has access to
required: false
type: string
- name: resourceRoleId
in: query
description: Filter the member resource association by exact resource-role UUID. Requires memberId. This only narrows results and does not grant challenge access.
required: false
type: string
format: UUID
- name: sortBy
in: query
description: Sort the results by the field.
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
"main": "dist/main.js",
"packageManager": "pnpm@11.15.1",
"scripts": {
"prebuild": "pnpm run prisma:generate",
"build": "nest build",
"start": "node dist/main.js",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main.js",
"postinstall": "pnpm run prisma:generate",
"prisma:generate": "prisma generate --generator client",
"prisma:generate": "prisma generate",
"lint": "eslint --no-error-on-unmatched-pattern \"*.ts\" \"src/**/*.ts\" \"prisma/**/*.ts\" \"test/**/*.ts\"",
"lint:fix": "pnpm lint --fix",
"format": "prettier --write \"*.ts\" \"src/**/*.ts\" \"prisma/**/*.ts\" \"test/**/*.ts\"",
Expand All @@ -21,6 +22,7 @@
"recalculate-winners": "node --require ts-node/register/transpile-only data-migration/src/scripts/recalculateChallengeWinners.js",
"recalculate-winners:csv": "node --require ts-node/register/transpile-only data-migration/src/scripts/recalculateChallengeWinners.js --csv-only",
"test": "NODE_ENV=test mocha --require ts-node/register/transpile-only \"test/unit/**/*.test.{js,ts}\" --exit",
"test:external-client": "node --test packages/challenge-prisma-client/factory.test.js",
"e2e": "NODE_ENV=test mocha --require ts-node/register/transpile-only \"test/e2e/*.test.{js,ts}\" --exit",
"test:cov": "nyc --reporter=html --reporter=text pnpm test",
"e2e:cov": "nyc --reporter=html --reporter=text pnpm e2e",
Expand Down
35 changes: 35 additions & 0 deletions packages/challenge-prisma-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# `@topcoder/challenge-api-v6`

This package is the supported external Prisma client for the Challenge API v6
schema. It re-exports the generated Prisma surface and provides
`createChallengePrismaClient(connectionString, options?)`, which configures the
Prisma 7 PostgreSQL driver adapter and honors the connection URL's optional
`schema` query parameter.

```ts
import { createChallengePrismaClient } from '@topcoder/challenge-api-v6';

const client = createChallengePrismaClient(process.env.CHALLENGE_DATABASE_URL);
const activeCount = await client.challenge.count({
where: { status: 'ACTIVE' },
});
await client.$disconnect();
```

Callers that need bounded database work can pass PostgreSQL pool settings
without constructing the Prisma 7 adapter themselves:

```ts
const client = createChallengePrismaClient(databaseUrl, {
driverOptions: {
connectionTimeoutMillis: 5000,
query_timeout: 5000,
statement_timeout: 5000,
},
});
```

The client connects lazily. Applications own its lifecycle and must disconnect
it during shutdown. An empty or non-string connection URL raises `TypeError`;
Prisma reports its normal configuration and database errors during creation or
query execution.
Loading
Loading