Skip to content

Commit d0daa09

Browse files
committed
chore(tg-bot): LicenseChain API response normalization helper
1 parent 727b4d3 commit d0daa09

3 files changed

Lines changed: 90 additions & 2 deletions

File tree

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## Summary
2+
3+
<!-- What changed (commands, client, DB). -->
4+
5+
## Related issue
6+
7+
Closes #____
8+
9+
## API alignment
10+
11+
- [ ] Uses `POST /v1/licenses/verify` with body field `key`.
12+
- [ ] List/aggregate commands do not expose other users’ licenses.
13+
14+
## Vendored API helper
15+
16+
- [ ] If editing `src/client/licensechainApiNormalize.js`: apply the same change to `LicenseChain-Discord-Bot` and update `api/src/contracts/bot-license-contracts.ts` + tests if shapes change.
17+
18+
## Verification
19+
20+
- [ ] Smoke test: `/validate` and `/list` against staging API.
21+
- [ ] CI / tests green.

src/client/LicenseChainClient.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
const axios = require('axios');
66
const crypto = require('crypto');
7+
const { normalizeAxiosError, normalizeVerifyPayload } = require('./licensechainApiNormalize');
78

89
class LicenseChainClient {
910
constructor(config) {
@@ -66,9 +67,9 @@ class LicenseChainClient {
6667
try {
6768
// Use verify endpoint to get license info
6869
const response = await this.client.post('/v1/licenses/verify', { key: licenseKey });
69-
return response.data;
70+
return normalizeVerifyPayload(response.data);
7071
} catch (error) {
71-
throw new Error(`Failed to get license: ${error.response?.data?.message || error.message}`);
72+
throw normalizeAxiosError(error, 'Failed to get license');
7273
}
7374
}
7475

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Vendored copy — keep in sync with:
3+
* - LicenseChain-Discord-Bot/src/client/licensechainApiNormalize.js (identical)
4+
* - api/src/contracts/bot-license-contracts.ts
5+
*/
6+
7+
function parseAxiosLikeError(err) {
8+
const status = err?.response?.status ?? null;
9+
const data = err?.response?.data;
10+
let message =
11+
(typeof data === 'object' && data !== null && typeof data.message === 'string' && data.message) ||
12+
(typeof data === 'object' && data !== null && typeof data.error === 'string' && data.error) ||
13+
err?.message ||
14+
'Request failed';
15+
const code =
16+
typeof data === 'object' && data !== null && typeof data.code === 'string' ? data.code : undefined;
17+
return { httpStatus: status, message: String(message), body: data ?? null, code };
18+
}
19+
20+
class LicenseChainApiError extends Error {
21+
constructor(context, parsed) {
22+
super(`${context}: ${parsed.message}`);
23+
this.name = 'LicenseChainApiError';
24+
this.context = context;
25+
this.httpStatus = parsed.httpStatus;
26+
this.apiBody = parsed.body;
27+
this.apiCode = parsed.code;
28+
}
29+
}
30+
31+
function normalizeAxiosError(err, context) {
32+
const parsed = parseAxiosLikeError(err);
33+
return new LicenseChainApiError(context, parsed);
34+
}
35+
36+
function normalizeVerifyPayload(data) {
37+
if (!data || typeof data !== 'object') {
38+
return { valid: false, reason: 'invalid_api_payload' };
39+
}
40+
const o = data;
41+
return {
42+
...o,
43+
valid: Boolean(o.valid),
44+
reason: typeof o.reason === 'string' ? o.reason : o.reason != null ? String(o.reason) : undefined,
45+
status: typeof o.status === 'string' ? o.status : o.status != null ? String(o.status) : undefined,
46+
expiresAt:
47+
typeof o.expiresAt === 'string'
48+
? o.expiresAt
49+
: o.expiresAt instanceof Date
50+
? o.expiresAt.toISOString()
51+
: o.expiresAt === null
52+
? null
53+
: o.expiresAt,
54+
email: typeof o.email === 'string' ? o.email : o.email,
55+
verificationType: typeof o.verificationType === 'string' ? o.verificationType : o.verificationType,
56+
error: typeof o.error === 'string' ? o.error : o.error,
57+
hardwareId: typeof o.hardwareId === 'string' ? o.hardwareId : o.hardwareId,
58+
};
59+
}
60+
61+
module.exports = {
62+
LicenseChainApiError,
63+
parseAxiosLikeError,
64+
normalizeAxiosError,
65+
normalizeVerifyPayload,
66+
};

0 commit comments

Comments
 (0)