Skip to content

feat(fingerprint): support caller-supplied fingerprint ID via x-fingerprint-id header#164

Merged
ShankarSinghC merged 1 commit into
mainfrom
feat/fingerprint-caller-supplied-id
Jun 3, 2026
Merged

feat(fingerprint): support caller-supplied fingerprint ID via x-fingerprint-id header#164
ShankarSinghC merged 1 commit into
mainfrom
feat/fingerprint-caller-supplied-id

Conversation

@ShankarSinghC

@ShankarSinghC ShankarSinghC commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Add an optional x-fingerprint-id request header to POST /data/fingerprint.

Behaviour:

  • Header absent, hash not found -> generate ID, insert, return (existing flow unchanged)
  • Header absent, hash found -> return stored ID
  • Header present (valid), hash not found -> insert using caller-supplied ID, return it
  • Header present (valid), hash found -> return stored ID, header ignored
  • Header present (invalid) -> 422 ValidationError before any DB call

Header format: exactly 20 alphanumeric characters (0-9 a-z A-Z).

Test cases

Scenario A — hash not found, no header (server generates ID)

curl -X POST http://localhost:3000/data/fingerprint \
  -H "Content-Type: application/json" \
  -H "x-tenant-id: public" \
  -d '{"data":"4111111111111111","key":"mysecretkey"}'

Response 200 OK

{"fingerprint_id":"DxgSyhrG6RTnCUZmcLky"}

The server generated and stored a fresh 20-char ID for this (data, key) pair.


Scenario B — hash not found, caller supplies ID

curl -X POST http://localhost:3000/data/fingerprint \
  -H "Content-Type: application/json" \
  -H "x-tenant-id: public" \
  -H "x-fingerprint-id: MyCustomId0123456789" \
  -d '{"data":"5500005555555559","key":"mysecretkey"}'

Response 200 OK

{"fingerprint_id":"MyCustomId0123456789"}

The record was inserted using the caller-supplied ID.


Scenario C — hash already exists, no header (returns stored ID)

Same request as Scenario A:

curl -X POST http://localhost:3000/data/fingerprint \
  -H "Content-Type: application/json" \
  -H "x-tenant-id: public" \
  -d '{"data":"4111111111111111","key":"mysecretkey"}'

Response 200 OK

{"fingerprint_id":"DxgSyhrG6RTnCUZmcLky"}

Hash was found; the originally stored ID is returned and nothing is inserted.


Scenario D — hash already exists, header present (header ignored)

curl -X POST http://localhost:3000/data/fingerprint \
  -H "Content-Type: application/json" \
  -H "x-tenant-id: public" \
  -H "x-fingerprint-id: SomeOtherId123456789" \
  -d '{"data":"4111111111111111","key":"mysecretkey"}'

Response 200 OK

{"fingerprint_id":"DxgSyhrG6RTnCUZmcLky"}

The x-fingerprint-id header is ignored because the hash already has a stored record.


Scenario E — invalid header value

Too short — TooShort123456789 (17 chars)

curl -X POST http://localhost:3000/data/fingerprint \
  -H "Content-Type: application/json" \
  -H "x-tenant-id: public" \
  -H "x-fingerprint-id: TooShort123456789" \
  -d '{"data":"4111111111111111","key":"mysecretkey"}'

Response

{"code":"TE_03","message":"Failed while validation: x-fingerprint-id must be exactly 20 alphanumeric characters","data":null}

Non-alphanumeric characters — bad-id-val-here12345 (20 chars, contains -)

curl -X POST http://localhost:3000/data/fingerprint \
  -H "Content-Type: application/json" \
  -H "x-tenant-id: public" \
  -H "x-fingerprint-id: bad-id-val-here12345" \
  -d '{"data":"4111111111111111","key":"mysecretkey"}'

Response

{"code":"TE_03","message":"Failed while validation: x-fingerprint-id must be exactly 20 alphanumeric characters","data":null}

…rprint-id header

Add an optional x-fingerprint-id request header to POST /data/fingerprint.

Behaviour:
- Header absent,  hash not found -> generate ID, insert, return (existing flow unchanged)
- Header absent,  hash found     -> return stored ID
- Header present (valid),   hash not found -> insert using caller-supplied ID, return it
- Header present (valid),   hash found     -> return stored ID, header ignored
- Header present (invalid)                -> 422 ValidationError before any DB call

Header format: exactly 20 alphanumeric characters (0-9 a-z A-Z).

Changes:
- src/storage/consts.rs: add X_FINGERPRINT_ID header constant
- src/custom_extractors.rs: add OptionalFingerprintId extractor with validation;
  extract validate_fingerprint_id() for unit-testability; add 11 unit tests
- src/storage.rs: add optional fingerprint_id param to FingerprintInterface trait
- src/storage/db.rs: use caller-supplied ID on insert; harden concurrent insert
  race (UniqueViolation now re-reads and returns winner row instead of erroring)
- src/storage/caching/fingerprint.rs: forward new param through caching wrapper
- src/routes/data.rs: consume OptionalFingerprintId extractor in handler
@ShankarSinghC ShankarSinghC self-assigned this Jun 2, 2026
@ShankarSinghC ShankarSinghC merged commit c2768d0 into main Jun 3, 2026
10 checks passed
@ShankarSinghC ShankarSinghC deleted the feat/fingerprint-caller-supplied-id branch June 3, 2026 05:21
su-shivanshmathur pushed a commit that referenced this pull request Jun 16, 2026
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