Skip to content

Commit c6024e6

Browse files
authored
Merge pull request #861 from gitroomhq/feat/handle-errors
Better error messages
2 parents cc6bf71 + 4ddea79 commit c6024e6

File tree

9 files changed

+651
-249
lines changed

9 files changed

+651
-249
lines changed

libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,11 @@ export class IntegrationService {
163163
await this.informAboutRefreshError(orgId, integration);
164164
}
165165

166-
async informAboutRefreshError(orgId: string, integration: Integration) {
166+
async informAboutRefreshError(orgId: string, integration: Integration, err = '') {
167167
await this._notificationService.inAppNotification(
168168
orgId,
169-
`Could not refresh your ${integration.providerIdentifier} channel`,
170-
`Could not refresh your ${integration.providerIdentifier} channel. Please go back to the system and connect it again ${process.env.FRONTEND_URL}/launches`,
169+
`Could not refresh your ${integration.providerIdentifier} channel ${err}`,
170+
`Could not refresh your ${integration.providerIdentifier} channel ${err}. Please go back to the system and connect it again ${process.env.FRONTEND_URL}/launches`,
171171
true
172172
);
173173
}

libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts

Lines changed: 16 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -299,16 +299,10 @@ export class PostsService {
299299
}
300300

301301
try {
302-
const finalPost =
303-
firstPost.integration?.type === 'article'
304-
? await this.postArticle(firstPost.integration!, [
305-
firstPost,
306-
...morePosts,
307-
])
308-
: await this.postSocial(firstPost.integration!, [
309-
firstPost,
310-
...morePosts,
311-
]);
302+
const finalPost = await this.postSocial(firstPost.integration!, [
303+
firstPost,
304+
...morePosts,
305+
]);
312306

313307
if (firstPost?.intervalInDays) {
314308
this._workerServiceProducer.emit('post', {
@@ -333,31 +327,16 @@ export class PostsService {
333327

334328
return;
335329
}
336-
337-
if (firstPost.submittedForOrderId) {
338-
this._workerServiceProducer.emit('submit', {
339-
payload: {
340-
id: firstPost.id,
341-
releaseURL: finalPost.releaseURL,
342-
},
343-
});
344-
}
345330
} catch (err: any) {
346331
await this._postRepository.changeState(firstPost.id, 'ERROR', err);
347-
await this._notificationService.inAppNotification(
348-
firstPost.organizationId,
349-
`Error posting on ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name}`,
350-
`An error occurred while posting on ${
351-
firstPost.integration?.providerIdentifier
352-
} ${
353-
!process.env.NODE_ENV || process.env.NODE_ENV === 'development'
354-
? err
355-
: ''
356-
}`,
357-
true
358-
);
359-
360332
if (err instanceof BadBody) {
333+
await this._notificationService.inAppNotification(
334+
firstPost.organizationId,
335+
`Error posting on ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name}`,
336+
`An error occurred while posting on ${firstPost.integration?.providerIdentifier}${err?.message ? `: ${err?.message}` : ``}`,
337+
true
338+
);
339+
361340
console.error(
362341
'[Error] posting on',
363342
firstPost.integration?.providerIdentifier,
@@ -366,15 +345,9 @@ export class PostsService {
366345
err.body,
367346
err
368347
);
369-
370-
return;
371348
}
372349

373-
console.error(
374-
'[Error] posting on',
375-
firstPost.integration?.providerIdentifier,
376-
err
377-
);
350+
return;
378351
}
379352
}
380353

@@ -403,7 +376,8 @@ export class PostsService {
403376
private async postSocial(
404377
integration: Integration,
405378
posts: Post[],
406-
forceRefresh = false
379+
forceRefresh = false,
380+
err = ''
407381
): Promise<Partial<{ postId: string; releaseURL: string }>> {
408382
const getIntegration = this._integrationManager.getSocialIntegration(
409383
integration.providerIdentifier
@@ -533,7 +507,7 @@ export class PostsService {
533507
};
534508
} catch (err) {
535509
if (err instanceof RefreshToken) {
536-
return this.postSocial(integration, posts, true);
510+
return this.postSocial(integration, posts, true, err?.message || '');
537511
}
538512

539513
throw err;
@@ -627,41 +601,6 @@ export class PostsService {
627601
}
628602
}
629603

630-
private async postArticle(
631-
integration: Integration,
632-
posts: Post[]
633-
): Promise<any> {
634-
const getIntegration = this._integrationManager.getArticlesIntegration(
635-
integration.providerIdentifier
636-
);
637-
if (!getIntegration) {
638-
return;
639-
}
640-
641-
const newPosts = await this.updateTags(integration.organizationId, posts);
642-
643-
const { postId, releaseURL } = await getIntegration.post(
644-
integration.token,
645-
newPosts.map((p) => p.content).join('\n\n'),
646-
JSON.parse(newPosts[0].settings || '{}')
647-
);
648-
649-
await this._notificationService.inAppNotification(
650-
integration.organizationId,
651-
`Your article has been published on ${capitalize(
652-
integration.providerIdentifier
653-
)}`,
654-
`Your article has been published at ${releaseURL}`,
655-
true
656-
);
657-
await this._postRepository.updatePost(newPosts[0].id, postId, releaseURL);
658-
659-
return {
660-
postId,
661-
releaseURL,
662-
};
663-
}
664-
665604
async deletePost(orgId: string, group: string) {
666605
const post = await this._postRepository.deletePost(orgId, group);
667606
if (post?.id) {
@@ -738,7 +677,7 @@ export class PostsService {
738677
);
739678

740679
if (!posts?.length) {
741-
return;
680+
return [] as any[];
742681
}
743682

744683
await this._workerServiceProducer.delete(

libraries/nestjs-libraries/src/integrations/integration.manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export class IntegrationManager {
8989
plugs: (
9090
Reflect.getMetadata('custom:plug', p.constructor.prototype) || []
9191
)
92-
.filter((f) => !f.disabled)
92+
.filter((f: any) => !f.disabled)
9393
.map((p: any) => ({
9494
...p,
9595
fields: p.fields.map((c: any) => ({
@@ -111,7 +111,7 @@ export class IntegrationManager {
111111
'custom:internal_plug',
112112
p.constructor.prototype
113113
) || []
114-
).filter((f) => !f.disabled) || [],
114+
).filter((f: any) => !f.disabled) || [],
115115
};
116116
}
117117

libraries/nestjs-libraries/src/integrations/social.abstract.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ export class RefreshToken {
55
constructor(
66
public identifier: string,
77
public json: string,
8-
public body: BodyInit
8+
public body: BodyInit,
9+
public message = '',
910
) {}
1011
}
1112
export class BadBody {
1213
constructor(
1314
public identifier: string,
1415
public json: string,
15-
public body: BodyInit
16+
public body: BodyInit,
17+
public message = ''
1618
) {}
1719
}
1820

@@ -22,14 +24,18 @@ export class NotEnoughScopes {
2224

2325
const pThrottleInstance = pThrottle({
2426
limit: 1,
25-
interval: 2000
27+
interval: 5000
2628
});
2729

2830
export abstract class SocialAbstract {
2931
private fetchInstance = pThrottleInstance(
3032
(url: RequestInfo, options?: RequestInit) => fetch(url, options)
3133
);
3234

35+
public handleErrors(body: string): {type: 'refresh-token' | 'bad-body', value: string}|undefined {
36+
return {type: 'bad-body', value: 'bad request'};
37+
}
38+
3339
async fetch(
3440
url: string,
3541
options: RequestInit = {},
@@ -55,28 +61,21 @@ export abstract class SocialAbstract {
5561
}
5662

5763
if (json.includes('rate_limit_exceeded') || json.includes('Rate limit')) {
58-
await timer(2000);
64+
await timer(5000);
5965
return this.fetch(url, options, identifier, totalRetries + 1);
6066
}
6167

62-
if (
63-
request.status === 401 ||
64-
(json.includes('OAuthException') &&
65-
!json.includes('The user is not an Instagram Business') &&
66-
!json.includes('Unsupported format') &&
67-
!json.includes('2207018') &&
68-
!json.includes('352') &&
69-
!json.includes('REVOKED_ACCESS_TOKEN'))
70-
) {
71-
throw new RefreshToken(identifier, json, options.body!);
68+
const handleError = this.handleErrors(json);
69+
70+
if (request.status === 401 || handleError?.type === 'refresh-token') {
71+
throw new RefreshToken(identifier, json, options.body!, handleError?.value);
7272
}
7373

7474
if (totalRetries < 2) {
75-
await timer(2000);
7675
return this.fetch(url, options, identifier, totalRetries + 1);
7776
}
7877

79-
throw new BadBody(identifier, json, options.body!);
78+
throw new BadBody(identifier, json, options.body!, handleError?.value);
8079
}
8180

8281
checkScopes(required: string[], got: string | string[]) {

libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,110 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider {
2121
'pages_read_engagement',
2222
'read_insights',
2323
];
24+
25+
override handleErrors(body: string): {
26+
type: 'refresh-token' | 'bad-body';
27+
value: string;
28+
} | undefined {
29+
// Access token validation errors - require re-authentication
30+
if (body.indexOf('Error validating access token') > -1) {
31+
return {
32+
type: 'refresh-token' as const,
33+
value: 'Please re-authenticate your Facebook account',
34+
};
35+
}
36+
37+
if (body.indexOf('490') > -1) {
38+
return {
39+
type: 'refresh-token' as const,
40+
value: 'Access token expired, please re-authenticate',
41+
};
42+
}
43+
44+
if (body.indexOf('REVOKED_ACCESS_TOKEN') > -1) {
45+
return {
46+
type: 'refresh-token' as const,
47+
value: 'Access token has been revoked, please re-authenticate',
48+
};
49+
}
50+
51+
if (body.indexOf('1390008') > -1) {
52+
return {
53+
type: 'bad-body' as const,
54+
value: 'You are posting too fast, please slow down',
55+
};
56+
}
57+
58+
// Content policy violations
59+
if (body.indexOf('1346003') > -1) {
60+
return {
61+
type: 'bad-body' as const,
62+
value: 'Content flagged as abusive by Facebook',
63+
};
64+
}
65+
66+
if (body.indexOf('1404102') > -1) {
67+
return {
68+
type: 'bad-body' as const,
69+
value: 'Content violates Facebook Community Standards',
70+
};
71+
}
72+
73+
// Permission errors
74+
if (body.indexOf('1404078') > -1) {
75+
return {
76+
type: 'refresh-token' as const,
77+
value: 'Page publishing authorization required, please re-authenticate',
78+
};
79+
}
80+
81+
if (body.indexOf('1609008') > -1) {
82+
return {
83+
type: 'bad-body' as const,
84+
value: 'Cannot post Facebook.com links',
85+
};
86+
}
87+
88+
// Parameter validation errors
89+
if (body.indexOf('2061006') > -1) {
90+
return {
91+
type: 'bad-body' as const,
92+
value: 'Invalid URL format in post content',
93+
};
94+
}
95+
96+
if (body.indexOf('1349125') > -1) {
97+
return {
98+
type: 'bad-body' as const,
99+
value: 'Invalid content format',
100+
};
101+
}
102+
103+
if (body.indexOf('Name parameter too long') > -1) {
104+
return {
105+
type: 'bad-body' as const,
106+
value: 'Post content is too long',
107+
};
108+
}
109+
110+
// Service errors - checking specific subcodes first
111+
if (body.indexOf('1363047') > -1) {
112+
return {
113+
type: 'bad-body' as const,
114+
value: 'Facebook service temporarily unavailable',
115+
};
116+
}
117+
118+
if (body.indexOf('1609010') > -1) {
119+
return {
120+
type: 'bad-body' as const,
121+
value: 'Facebook service temporarily unavailable',
122+
};
123+
}
124+
125+
return undefined;
126+
}
127+
24128
async refreshToken(refresh_token: string): Promise<AuthTokenDetails> {
25129
return {
26130
refreshToken: '',

0 commit comments

Comments
 (0)