Skip to content

Commit 735fd0e

Browse files
committed
Parse headers
1 parent b25dfde commit 735fd0e

File tree

4 files changed

+403
-36
lines changed

4 files changed

+403
-36
lines changed

dist/schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37254,7 +37254,7 @@
3725437254
}
3725537255
},
3725637256
"Link": {
37257-
"description": "Pagination links. See [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288) for more information.",
37257+
"description": "Pagination links for browsing older or newer results. Format: Link: <https://mastodon.example/api/v1/endpoint?max_id=7163058>; rel=\"next\", <https://mastodon.example/api/v1/endpoint?min_id=7275607>; rel=\"prev\". See [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288) for more information.",
3725837258
"schema": {
3725937259
"type": "string"
3726037260
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { HeaderParser } from '../../parsers/HeaderParser';
2+
3+
describe('HeaderParser', () => {
4+
describe('parseHeaders', () => {
5+
it('should parse all headers from documentation', () => {
6+
const headers = HeaderParser.parseHeaders();
7+
8+
// Should return 5 headers
9+
expect(headers).toHaveLength(5);
10+
11+
// Check X-RateLimit-Limit
12+
const rateLimitLimit = headers.find(
13+
(h) => h.name === 'X-RateLimit-Limit'
14+
);
15+
expect(rateLimitLimit).toBeDefined();
16+
expect(rateLimitLimit?.description).toBe(
17+
'Number of requests permitted per time period'
18+
);
19+
expect(rateLimitLimit?.schema.type).toBe('integer');
20+
21+
// Check X-RateLimit-Remaining
22+
const rateLimitRemaining = headers.find(
23+
(h) => h.name === 'X-RateLimit-Remaining'
24+
);
25+
expect(rateLimitRemaining).toBeDefined();
26+
expect(rateLimitRemaining?.description).toBe(
27+
'Number of requests you can still make'
28+
);
29+
expect(rateLimitRemaining?.schema.type).toBe('integer');
30+
31+
// Check X-RateLimit-Reset
32+
const rateLimitReset = headers.find(
33+
(h) => h.name === 'X-RateLimit-Reset'
34+
);
35+
expect(rateLimitReset).toBeDefined();
36+
expect(rateLimitReset?.description).toContain('Timestamp');
37+
expect(rateLimitReset?.schema.type).toBe('string');
38+
expect(rateLimitReset?.schema.format).toBe('date-time');
39+
40+
// Check Link header
41+
const link = headers.find((h) => h.name === 'Link');
42+
expect(link).toBeDefined();
43+
expect(link?.description).toContain('Pagination links');
44+
expect(link?.description).toContain('Format:');
45+
expect(link?.description).toContain('RFC 8288');
46+
expect(link?.schema.type).toBe('string');
47+
48+
// Check Mastodon-Async-Refresh header
49+
const asyncRefresh = headers.find(
50+
(h) => h.name === 'Mastodon-Async-Refresh'
51+
);
52+
expect(asyncRefresh).toBeDefined();
53+
expect(asyncRefresh?.description).toContain('async refresh');
54+
expect(asyncRefresh?.description).toContain('Format:');
55+
expect(asyncRefresh?.description).toContain('retry');
56+
expect(asyncRefresh?.description).toContain('result_count');
57+
expect(asyncRefresh?.schema.type).toBe('string');
58+
});
59+
60+
it('should parse rate limit headers correctly', () => {
61+
const headers = HeaderParser.parseHeaders();
62+
const rateLimitHeaders = headers.filter((h) =>
63+
h.name.startsWith('X-RateLimit-')
64+
);
65+
66+
expect(rateLimitHeaders).toHaveLength(3);
67+
68+
// All rate limit headers should have descriptions from the docs
69+
rateLimitHeaders.forEach((header) => {
70+
expect(header.description).toBeTruthy();
71+
expect(header.description.length).toBeGreaterThan(0);
72+
expect(header.schema).toBeDefined();
73+
expect(header.schema.type).toBeTruthy();
74+
});
75+
});
76+
77+
it('should include example format in Link header description', () => {
78+
const headers = HeaderParser.parseHeaders();
79+
const linkHeader = headers.find((h) => h.name === 'Link');
80+
81+
expect(linkHeader).toBeDefined();
82+
expect(linkHeader?.description).toContain('mastodon.example');
83+
expect(linkHeader?.description).toContain('max_id');
84+
expect(linkHeader?.description).toContain('min_id');
85+
expect(linkHeader?.description).toContain('rel="next"');
86+
expect(linkHeader?.description).toContain('rel="prev"');
87+
});
88+
89+
it('should include format details in Mastodon-Async-Refresh description', () => {
90+
const headers = HeaderParser.parseHeaders();
91+
const asyncRefreshHeader = headers.find(
92+
(h) => h.name === 'Mastodon-Async-Refresh'
93+
);
94+
95+
expect(asyncRefreshHeader).toBeDefined();
96+
expect(asyncRefreshHeader?.description).toContain('id=');
97+
expect(asyncRefreshHeader?.description).toContain('retry=');
98+
expect(asyncRefreshHeader?.description).toContain('result_count=');
99+
expect(asyncRefreshHeader?.description).toContain('<string>');
100+
expect(asyncRefreshHeader?.description).toContain('<int>');
101+
});
102+
});
103+
});

src/generators/SpecBuilder.ts

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11
import { readFileSync } from 'fs';
22
import { OpenAPISpec } from '../interfaces/OpenAPISchema';
33
import { OAuthScopeParser } from '../parsers/OAuthScopeParser';
4+
import { HeaderParser } from '../parsers/HeaderParser';
45
import { SUPPORTED_VERSION } from '../parsers/VersionParser';
56

67
/**
78
* Builder for OpenAPI specification with authentication setup
89
*/
910
class SpecBuilder {
11+
/**
12+
* Build header components from documentation
13+
*/
14+
private buildHeaderComponents(): Record<string, any> {
15+
const headers: Record<string, any> = {};
16+
const parsedHeaders = HeaderParser.parseHeaders();
17+
18+
for (const header of parsedHeaders) {
19+
headers[header.name] = {
20+
description: header.description,
21+
schema: header.schema,
22+
};
23+
}
24+
25+
return headers;
26+
}
27+
1028
/**
1129
* Build initial OpenAPI specification with OAuth configuration
1230
*/
@@ -106,41 +124,7 @@ class SpecBuilder {
106124
},
107125
},
108126
},
109-
headers: {
110-
'X-RateLimit-Limit': {
111-
description: 'Number of requests permitted per time period',
112-
schema: {
113-
type: 'integer',
114-
},
115-
},
116-
'X-RateLimit-Remaining': {
117-
description: 'Number of requests you can still make',
118-
schema: {
119-
type: 'integer',
120-
},
121-
},
122-
'X-RateLimit-Reset': {
123-
description: 'Timestamp when your rate limit will reset',
124-
schema: {
125-
type: 'string',
126-
format: 'date-time',
127-
},
128-
},
129-
Link: {
130-
description:
131-
'Pagination links. See [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288) for more information.',
132-
schema: {
133-
type: 'string',
134-
},
135-
},
136-
'Mastodon-Async-Refresh': {
137-
description:
138-
'Indicates an async refresh is in progress. Format: id="<string>", retry=<int>, result_count=<int>. The retry value indicates seconds to wait before retrying. The result_count is optional and indicates results already fetched.',
139-
schema: {
140-
type: 'string',
141-
},
142-
},
143-
},
127+
headers: this.buildHeaderComponents(),
144128
},
145129
};
146130
}

0 commit comments

Comments
 (0)