Skip to content

Commit d18a3ea

Browse files
authored
Merge pull request #404 from abraham/copilot/fix-attribute-description-cutoff
Fix attribute description parsing for multiline content with newline characters
2 parents fdb969b + 58bc0e6 commit d18a3ea

File tree

4 files changed

+144
-8
lines changed

4 files changed

+144
-8
lines changed

dist/schema.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33083,7 +33083,7 @@
3308333083
"type": "string"
3308433084
},
3308533085
"redirect_uri": {
33086-
"description": "The registered redirection URI(s) for the application stored as a single string. Multiple URIs are separated by whitespace characters. May contain `",
33086+
"description": "The registered redirection URI(s) for the application stored as a single string. Multiple URIs are separated by whitespace characters. May contain `\\n` characters when multiple redirect URIs are registered.",
3308733087
"deprecated": true,
3308833088
"type": [
3308933089
"string",
@@ -33181,7 +33181,7 @@
3318133181
]
3318233182
},
3318333183
"redirect_uri": {
33184-
"description": "The registered redirection URI(s) for the application stored as a single string. Multiple URIs are separated by whitespace characters. May contain `",
33184+
"description": "The registered redirection URI(s) for the application stored as a single string. Multiple URIs are separated by whitespace characters. May contain `\\n` characters when multiple redirect URIs are registered.",
3318533185
"deprecated": true,
3318633186
"type": [
3318733187
"string",
@@ -35236,11 +35236,11 @@
3523635236
"description": "Represents a user's preferences.",
3523735237
"properties": {
3523835238
"posting:default:sensitive": {
35239-
"description": "Default sensitivity flag for new posts. Equivalent to [CredentialAccount#source",
35239+
"description": "Default sensitivity flag for new posts. Equivalent to [CredentialAccount#source\\[sensitive\\]]({{< relref \"entities/Account#source-sensitive\" >}}).",
3524035240
"type": "boolean"
3524135241
},
3524235242
"posting:default:visibility": {
35243-
"description": "Default visibility for new posts. Equivalent to [CredentialAccount#source",
35243+
"description": "Default visibility for new posts. Equivalent to [CredentialAccount#source\\[privacy\\]]({{< relref \"entities/Account#source-privacy\" >}}).",
3524435244
"type": "string",
3524535245
"$ref": "#/components/schemas/VisibilityEnum"
3524635246
},
@@ -35258,7 +35258,7 @@
3525835258
"type": "boolean"
3525935259
},
3526035260
"posting:default:language": {
35261-
"description": "Default language for new posts. Equivalent to [CredentialAccount#source",
35261+
"description": "Default language for new posts. Equivalent to [CredentialAccount#source\\[language\\]]({{< relref \"entities/Account#source-language\" >}})",
3526235262
"type": [
3526335263
"string",
3526435264
"null"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { EntityParser } from '../../parsers/EntityParser';
2+
3+
describe('Integration test - Application redirect_uri description', () => {
4+
it('should parse the full redirect_uri description from Application entity', () => {
5+
const entityParser = new EntityParser();
6+
const entities = entityParser.parseAllEntities();
7+
8+
const applicationEntity = entities.find(
9+
(entity) => entity.name === 'Application'
10+
);
11+
expect(applicationEntity).toBeDefined();
12+
13+
const redirectUriAttr = applicationEntity?.attributes.find(
14+
(attr) => attr.name === 'redirect_uri'
15+
);
16+
expect(redirectUriAttr).toBeDefined();
17+
18+
// Verify the full description is captured including the newline reference
19+
expect(redirectUriAttr?.description).toContain('May contain');
20+
expect(redirectUriAttr?.description).toContain('\\n');
21+
expect(redirectUriAttr?.description).toContain(
22+
'characters when multiple redirect URIs are registered'
23+
);
24+
25+
// Check that it doesn't contain any version history content
26+
expect(redirectUriAttr?.description).not.toContain('4.3.0');
27+
expect(redirectUriAttr?.description).not.toContain('deprecated in favour');
28+
});
29+
});
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { AttributeParser } from '../../parsers/AttributeParser';
2+
3+
describe('AttributeParser - Newline in description', () => {
4+
describe('Entity format', () => {
5+
it('should parse full description with newline characters for redirect_uri', () => {
6+
const content = `
7+
### \`redirect_uri\` {{%deprecated%}} {#redirect_uri}
8+
9+
**Description:** The registered redirection URI(s) for the application stored as a single string. Multiple URIs are separated by whitespace characters. May contain \`\\n\` characters when multiple redirect URIs are registered.\\
10+
**Type:** String\\
11+
**Version history:**\\
12+
0.0.0 - added\\
13+
4.3.0 - deprecated in favour of [\`redirect_uris\`]({{< relref "entities/Application#redirect_uris" >}}), since the value of this property is not a well-formed URI when multiple redirect URIs are registered
14+
`;
15+
16+
const attributes = AttributeParser.parseAttributesFromSection(content);
17+
18+
expect(attributes).toHaveLength(1);
19+
expect(attributes[0].name).toBe('redirect_uri');
20+
expect(attributes[0].description).toContain('May contain');
21+
expect(attributes[0].description).toContain('\\n');
22+
expect(attributes[0].description).toContain(
23+
'characters when multiple redirect URIs are registered'
24+
);
25+
expect(attributes[0].deprecated).toBe(true);
26+
});
27+
28+
it('should parse full description with multiline content', () => {
29+
const content = `
30+
### \`multiline_field\` {#multiline_field}
31+
32+
**Description:** This is a description that spans
33+
multiple lines and should be captured
34+
completely until the Type section.\\
35+
**Type:** String\\
36+
**Version history:**\\
37+
1.0.0 - added
38+
`;
39+
40+
const attributes = AttributeParser.parseAttributesFromSection(content);
41+
42+
expect(attributes).toHaveLength(1);
43+
expect(attributes[0].name).toBe('multiline_field');
44+
expect(attributes[0].description).toContain(
45+
'This is a description that spans'
46+
);
47+
expect(attributes[0].description).toContain(
48+
'multiple lines and should be captured'
49+
);
50+
expect(attributes[0].description).toContain(
51+
'completely until the Type section'
52+
);
53+
});
54+
});
55+
56+
describe('Method entity format', () => {
57+
it('should parse full description with newline characters in method entities', () => {
58+
const content = `
59+
#### \`redirect_uri\` {{%deprecated%}} {#redirect_uri}
60+
61+
**Description:** The registered redirection URI(s) for the application stored as a single string. Multiple URIs are separated by whitespace characters. May contain \`\\n\` characters when multiple redirect URIs are registered.\\
62+
**Type:** String\\
63+
**Version history:**\\
64+
0.0.0 - added\\
65+
4.3.0 - deprecated
66+
`;
67+
68+
const attributes = AttributeParser.parseMethodEntityAttributes(content);
69+
70+
expect(attributes).toHaveLength(1);
71+
expect(attributes[0].name).toBe('redirect_uri');
72+
expect(attributes[0].description).toContain('May contain');
73+
expect(attributes[0].description).toContain('\\n');
74+
expect(attributes[0].description).toContain(
75+
'characters when multiple redirect URIs are registered'
76+
);
77+
expect(attributes[0].deprecated).toBe(true);
78+
});
79+
80+
it('should parse full description with multiline content in method entities', () => {
81+
const content = `
82+
#### \`multiline_field\` {#multiline_field}
83+
84+
**Description:** This is a description that spans
85+
multiple lines and should be captured
86+
completely until the Type section.\\
87+
**Type:** String\\
88+
**Version history:**\\
89+
1.0.0 - added
90+
`;
91+
92+
const attributes = AttributeParser.parseMethodEntityAttributes(content);
93+
94+
expect(attributes).toHaveLength(1);
95+
expect(attributes[0].name).toBe('multiline_field');
96+
expect(attributes[0].description).toContain(
97+
'This is a description that spans'
98+
);
99+
expect(attributes[0].description).toContain(
100+
'multiple lines and should be captured'
101+
);
102+
expect(attributes[0].description).toContain(
103+
'completely until the Type section'
104+
);
105+
});
106+
});
107+
});

src/parsers/AttributeParser.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ export class AttributeParser {
5757

5858
// Look for Description and Type in this specific section
5959
const descMatch = sectionContent.match(
60-
/\*\*Description:\*\*\s*([^\n\\]+)(?:\\[^\n]*)?/
60+
/\*\*Description:\*\*\s*(.*?)\\?\s*\n\*\*Type:\*\*/s
6161
);
6262
const typeMatch = sectionContent.match(
63-
/\*\*Type:\*\*\s*([^\n\\]+)(?:\\[^\n]*)?/
63+
/\*\*Type:\*\*\s*(.*?)(?=\n(?:\*\*Version history:\*\*|###|####|`[^`]+`\s*=|\d+\.\d+\.\d+\s*-|$))/s
6464
);
6565

6666
// Look for Version history in this specific section
@@ -216,7 +216,7 @@ export class AttributeParser {
216216
// Then potentially some enum values or additional content
217217
// Then: **Version history:**\
218218
const attributeRegex =
219-
/#### `([^`]+)`[^{]*?(?:\{\{%([^%]+)%\}\})?\s*(?:\{#[^}]+\})?\s*\n\n\*\*Description:\*\*\s*([^\n]+?)\\?\s*\n\*\*Type:\*\*\s*([^\n]+?)\\?\s*\n(.*?)\*\*Version history:\*\*\\?\s*([\s\S]*?)(?=\n#### |$)/gs;
219+
/#### `([^`]+)`[^{]*?(?:\{\{%([^%]+)%\}\})?\s*(?:\{#[^}]+\})?\s*\n\n\*\*Description:\*\*\s*(.*?)\\?\s*\n\*\*Type:\*\*\s*(.*?)\\?\s*\n(.*?)\*\*Version history:\*\*\\?\s*([\s\S]*?)(?=\n#### |$)/gs;
220220

221221
let match;
222222
while ((match = attributeRegex.exec(content)) !== null) {

0 commit comments

Comments
 (0)