@agnocon/piece-bitly 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.MIT-AP +24 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/actions/archive-bitlink.d.ts +11 -0
- package/dist/lib/actions/archive-bitlink.d.ts.map +1 -0
- package/dist/lib/actions/archive-bitlink.js +54 -0
- package/dist/lib/actions/archive-bitlink.js.map +1 -0
- package/dist/lib/actions/create-bitlink.d.ts +34 -0
- package/dist/lib/actions/create-bitlink.d.ts.map +1 -0
- package/dist/lib/actions/create-bitlink.js +134 -0
- package/dist/lib/actions/create-bitlink.js.map +1 -0
- package/dist/lib/actions/create-qr-code.d.ts +39 -0
- package/dist/lib/actions/create-qr-code.d.ts.map +1 -0
- package/dist/lib/actions/create-qr-code.js +383 -0
- package/dist/lib/actions/create-qr-code.js.map +1 -0
- package/dist/lib/actions/get-bitlink-details.d.ts +11 -0
- package/dist/lib/actions/get-bitlink-details.d.ts.map +1 -0
- package/dist/lib/actions/get-bitlink-details.js +50 -0
- package/dist/lib/actions/get-bitlink-details.js.map +1 -0
- package/dist/lib/actions/update-bitlink.d.ts +18 -0
- package/dist/lib/actions/update-bitlink.d.ts.map +1 -0
- package/dist/lib/actions/update-bitlink.js +135 -0
- package/dist/lib/actions/update-bitlink.js.map +1 -0
- package/dist/lib/common/auth.d.ts +4 -0
- package/dist/lib/common/auth.d.ts.map +1 -0
- package/dist/lib/common/auth.js +44 -0
- package/dist/lib/common/auth.js.map +1 -0
- package/dist/lib/common/client.d.ts +13 -0
- package/dist/lib/common/client.d.ts.map +1 -0
- package/dist/lib/common/client.js +69 -0
- package/dist/lib/common/client.js.map +1 -0
- package/dist/lib/common/props.d.ts +10 -0
- package/dist/lib/common/props.d.ts.map +1 -0
- package/dist/lib/common/props.js +118 -0
- package/dist/lib/common/props.js.map +1 -0
- package/dist/lib/triggers/new-bitlink-created.d.ts +43 -0
- package/dist/lib/triggers/new-bitlink-created.d.ts.map +1 -0
- package/dist/lib/triggers/new-bitlink-created.js +312 -0
- package/dist/lib/triggers/new-bitlink-created.js.map +1 -0
- package/package.json +46 -0
- package/src/index.ts +38 -0
- package/src/lib/actions/archive-bitlink.ts +59 -0
- package/src/lib/actions/create-bitlink.ts +158 -0
- package/src/lib/actions/create-qr-code.ts +398 -0
- package/src/lib/actions/get-bitlink-details.ts +54 -0
- package/src/lib/actions/update-bitlink.ts +153 -0
- package/src/lib/common/auth.ts +37 -0
- package/src/lib/common/client.ts +122 -0
- package/src/lib/common/props.ts +123 -0
- package/src/lib/triggers/new-bitlink-created.ts +361 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
httpClient,
|
|
3
|
+
HttpMethod,
|
|
4
|
+
HttpRequest,
|
|
5
|
+
HttpMessageBody,
|
|
6
|
+
QueryParams,
|
|
7
|
+
} from '@agnocon/pieces-common';
|
|
8
|
+
|
|
9
|
+
export type BitlyAuthProps = {
|
|
10
|
+
accessToken: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type BitlyApiCallParams = {
|
|
14
|
+
method: HttpMethod;
|
|
15
|
+
resourceUri: string;
|
|
16
|
+
query?: Record<string, string | number | string[] | undefined>;
|
|
17
|
+
body?: any;
|
|
18
|
+
auth: BitlyAuthProps;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export async function bitlyApiCall<T extends HttpMessageBody>({
|
|
22
|
+
method,
|
|
23
|
+
resourceUri,
|
|
24
|
+
query,
|
|
25
|
+
body,
|
|
26
|
+
auth,
|
|
27
|
+
}: BitlyApiCallParams): Promise<T> {
|
|
28
|
+
const { accessToken } = auth;
|
|
29
|
+
|
|
30
|
+
if (!accessToken) {
|
|
31
|
+
throw new Error('Bitly Access Token is required for authentication');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const queryParams: QueryParams = {};
|
|
35
|
+
|
|
36
|
+
if (query) {
|
|
37
|
+
for (const [key, value] of Object.entries(query)) {
|
|
38
|
+
if (value !== null && value !== undefined) {
|
|
39
|
+
queryParams[key] = String(value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const baseUrl = 'https://api-ssl.bitly.com/v4';
|
|
45
|
+
|
|
46
|
+
const request: HttpRequest = {
|
|
47
|
+
method,
|
|
48
|
+
url: `${baseUrl}${resourceUri}`,
|
|
49
|
+
headers: {
|
|
50
|
+
Authorization: `Bearer ${accessToken}`,
|
|
51
|
+
'Content-Type': 'application/json',
|
|
52
|
+
},
|
|
53
|
+
queryParams,
|
|
54
|
+
body,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const response = await httpClient.sendRequest<T>(request);
|
|
59
|
+
return response.body;
|
|
60
|
+
} catch (error: any) {
|
|
61
|
+
const statusCode = error.response?.status;
|
|
62
|
+
const errorData = error.response?.data;
|
|
63
|
+
const errorMessage = errorData?.description || errorData?.message || 'Unknown error occurred';
|
|
64
|
+
|
|
65
|
+
switch (statusCode) {
|
|
66
|
+
case 400:
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Bad Request: ${errorMessage}. Please check your input parameters.`
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
case 401:
|
|
72
|
+
throw new Error(
|
|
73
|
+
'Authentication Failed: Invalid Access Token. Please verify your Bitly credentials in the connection settings.'
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
case 402:
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Payment Required: ${errorMessage}. Your account has been suspended or you have reached a usage limit.`
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
case 403:
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Access Forbidden: ${errorMessage}. You do not have permission to access this resource.`
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
case 404:
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Resource Not Found: ${errorMessage}. The requested resource does not exist.`
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
case 417:
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Expectation Failed: ${errorMessage}. You must agree to the latest terms of service.`
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
case 422:
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Unprocessable Entity: ${errorMessage}. The request was well-formed but was unable to be followed due to semantic errors.`
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
case 429:
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Rate Limit Exceeded: ${errorMessage}. Too many requests. Please wait before trying again.`
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
case 500:
|
|
107
|
+
throw new Error(
|
|
108
|
+
'Internal Server Error: Bitly is experiencing technical difficulties. Please try again later.'
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
case 503:
|
|
112
|
+
throw new Error(
|
|
113
|
+
'Service Unavailable: Bitly service is temporarily unavailable. Please try again in a few minutes.'
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
default:
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Bitly API Error (${statusCode || 'Unknown'}): ${errorMessage}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { HttpMethod } from '@agnocon/pieces-common';
|
|
3
|
+
import { bitlyApiCall } from './client';
|
|
4
|
+
import { BitlyAuthProps } from './client';
|
|
5
|
+
import { bitlyAuth } from './auth';
|
|
6
|
+
|
|
7
|
+
interface BitlyGroup {
|
|
8
|
+
guid: string;
|
|
9
|
+
name: string;
|
|
10
|
+
bsds: Array<{ domain: string }>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Bitlink {
|
|
14
|
+
id: string;
|
|
15
|
+
title: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const getBitlyGroups = async (auth: BitlyAuthProps): Promise<BitlyGroup[]> => {
|
|
19
|
+
const response = await bitlyApiCall<{ groups: BitlyGroup[] }>({
|
|
20
|
+
auth,
|
|
21
|
+
method: HttpMethod.GET,
|
|
22
|
+
resourceUri: '/groups',
|
|
23
|
+
});
|
|
24
|
+
return response.groups || [];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const groupGuid = Property.Dropdown({
|
|
28
|
+
displayName: 'Group',
|
|
29
|
+
description: 'The group where the item will be managed.',
|
|
30
|
+
required: true,
|
|
31
|
+
refreshers: [],
|
|
32
|
+
auth:bitlyAuth ,
|
|
33
|
+
options: async ({ auth }) => {
|
|
34
|
+
if (!auth) {
|
|
35
|
+
return { disabled: true, options: [], placeholder: 'Please connect your Bitly account first.' };
|
|
36
|
+
}
|
|
37
|
+
const { accessToken } = auth.props;
|
|
38
|
+
if (!accessToken) {
|
|
39
|
+
return { disabled: true, options: [], placeholder: 'Please connect your Bitly account first.' };
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const groups = await getBitlyGroups({ accessToken });
|
|
43
|
+
if (groups.length === 0) {
|
|
44
|
+
return { disabled: true, options: [], placeholder: 'No groups found in your account.' };
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
disabled: false,
|
|
48
|
+
options: groups.map((group) => ({
|
|
49
|
+
label: group.name,
|
|
50
|
+
value: group.guid,
|
|
51
|
+
})),
|
|
52
|
+
};
|
|
53
|
+
} catch (e) {
|
|
54
|
+
return { disabled: true, options: [], placeholder: `Error fetching groups: ${(e as Error).message}` };
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const domain = Property.Dropdown({
|
|
60
|
+
auth: bitlyAuth,
|
|
61
|
+
displayName: 'Domain',
|
|
62
|
+
description: 'Domain to use for the Bitlink.',
|
|
63
|
+
required: false,
|
|
64
|
+
refreshers: ['group_guid'],
|
|
65
|
+
options: async ({ auth, group_guid }) => {
|
|
66
|
+
if (!auth) {
|
|
67
|
+
return { disabled: true, options: [], placeholder: 'Please connect your Bitly account first.' };
|
|
68
|
+
}
|
|
69
|
+
const { accessToken } = auth.props;
|
|
70
|
+
if (!accessToken || !group_guid) {
|
|
71
|
+
return { disabled: true, options: [], placeholder: 'Please select a group first.' };
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const groups = await getBitlyGroups({ accessToken });
|
|
75
|
+
const selectedGroup = groups.find(g => g.guid === group_guid);
|
|
76
|
+
const customDomains = selectedGroup?.bsds?.map(bsd => bsd.domain) || [];
|
|
77
|
+
const allDomains = ['bit.ly', ...customDomains];
|
|
78
|
+
return {
|
|
79
|
+
disabled: false,
|
|
80
|
+
options: allDomains.map(d => ({
|
|
81
|
+
label: d,
|
|
82
|
+
value: d,
|
|
83
|
+
})),
|
|
84
|
+
};
|
|
85
|
+
} catch (e) {
|
|
86
|
+
return { disabled: true, options: [], placeholder: `Error fetching domains: ${(e as Error).message}` };
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export const bitlinkDropdown = Property.Dropdown({
|
|
92
|
+
displayName: 'Bitlink',
|
|
93
|
+
description: 'Select the Bitlink to modify.',
|
|
94
|
+
required: true,
|
|
95
|
+
refreshers: ['group_guid'],
|
|
96
|
+
auth: bitlyAuth,
|
|
97
|
+
options: async ({ auth, group_guid }) => {
|
|
98
|
+
|
|
99
|
+
if (!auth) {
|
|
100
|
+
return { disabled: true, options: [], placeholder: 'Please connect your Bitly account first.' };
|
|
101
|
+
}
|
|
102
|
+
const { accessToken } = auth.props;
|
|
103
|
+
if (!accessToken) return { disabled: true, options: [], placeholder: 'Please connect your Bitly account first.' };
|
|
104
|
+
if (!group_guid) return { disabled: true, options: [], placeholder: 'Please select a group first.' };
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const response = await bitlyApiCall<{ links: Bitlink[] }>({
|
|
108
|
+
auth: { accessToken },
|
|
109
|
+
method: HttpMethod.GET,
|
|
110
|
+
resourceUri: `/groups/${group_guid as string}/bitlinks`,
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
disabled: false,
|
|
114
|
+
options: response.links.map(link => ({
|
|
115
|
+
label: `${link.title || 'No Title'} (${link.id})`,
|
|
116
|
+
value: link.id
|
|
117
|
+
}))
|
|
118
|
+
};
|
|
119
|
+
} catch (e) {
|
|
120
|
+
return { disabled: true, options: [], placeholder: `Error fetching Bitlinks: ${(e as Error).message}` };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { createTrigger, TriggerStrategy, Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { HttpMethod } from '@agnocon/pieces-common';
|
|
3
|
+
import { bitlyAuth } from '../common/auth';
|
|
4
|
+
import { bitlyApiCall } from '../common/client';
|
|
5
|
+
import { groupGuid } from '../common/props';
|
|
6
|
+
|
|
7
|
+
const LAST_BITLINK_IDS_KEY = 'bitly-last-bitlink-ids';
|
|
8
|
+
|
|
9
|
+
export const newBitlinkCreatedTrigger = createTrigger({
|
|
10
|
+
auth: bitlyAuth,
|
|
11
|
+
name: 'new_bitlink_created',
|
|
12
|
+
displayName: 'New Bitlink Created',
|
|
13
|
+
description: 'Fires when a new Bitlink is created.',
|
|
14
|
+
aiMetadata: {
|
|
15
|
+
description: 'Fires when a new Bitlink appears in the selected group, detected by polling. Represents a newly created short link, optionally narrowed to those whose title or tags match a filter and optionally including archived links.',
|
|
16
|
+
},
|
|
17
|
+
type: TriggerStrategy.POLLING,
|
|
18
|
+
props: {
|
|
19
|
+
pollingInterval: Property.StaticDropdown({
|
|
20
|
+
displayName: 'Polling Interval',
|
|
21
|
+
description: 'How frequently to check for new Bitlinks.',
|
|
22
|
+
required: false,
|
|
23
|
+
defaultValue: '5',
|
|
24
|
+
options: {
|
|
25
|
+
disabled: false,
|
|
26
|
+
options: [
|
|
27
|
+
{ label: 'Every 1 minute', value: '1' },
|
|
28
|
+
{ label: 'Every 5 minutes', value: '5' },
|
|
29
|
+
{ label: 'Every 15 minutes', value: '15' },
|
|
30
|
+
{ label: 'Every 30 minutes', value: '30' },
|
|
31
|
+
{ label: 'Every hour', value: '60' },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
|
|
36
|
+
group_guid: groupGuid,
|
|
37
|
+
|
|
38
|
+
titleFilter: Property.ShortText({
|
|
39
|
+
displayName: 'Title Filter (Optional)',
|
|
40
|
+
description: 'Only trigger for Bitlinks containing this text in their title.',
|
|
41
|
+
required: false,
|
|
42
|
+
}),
|
|
43
|
+
|
|
44
|
+
tagFilter: Property.ShortText({
|
|
45
|
+
displayName: 'Tag Filter (Optional)',
|
|
46
|
+
description: 'Only trigger for Bitlinks containing this tag.',
|
|
47
|
+
required: false,
|
|
48
|
+
}),
|
|
49
|
+
|
|
50
|
+
includeArchived: Property.Checkbox({
|
|
51
|
+
displayName: 'Include Archived Bitlinks',
|
|
52
|
+
description: 'Include archived Bitlinks in monitoring.',
|
|
53
|
+
required: false,
|
|
54
|
+
defaultValue: false,
|
|
55
|
+
}),
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
async onEnable(context) {
|
|
59
|
+
const { group_guid } = context.propsValue;
|
|
60
|
+
const { accessToken } = context.auth.props;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const response = await bitlyApiCall<{ links: BitlyLink[] }>({
|
|
64
|
+
auth: { accessToken },
|
|
65
|
+
method: HttpMethod.GET,
|
|
66
|
+
resourceUri: `/groups/${group_guid}/bitlinks`,
|
|
67
|
+
query: {
|
|
68
|
+
size: 50,
|
|
69
|
+
archived: context.propsValue.includeArchived ? 'both' : 'off',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const linkIds = response.links.map((link) => link.id);
|
|
74
|
+
await context.store.put<string[]>(LAST_BITLINK_IDS_KEY, linkIds);
|
|
75
|
+
|
|
76
|
+
console.log(`Bitly New Bitlink trigger initialized with ${linkIds.length} existing links`);
|
|
77
|
+
} catch (error: any) {
|
|
78
|
+
if (error.response?.status === 401) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
'Authentication failed: Please check your access token. Make sure your token has permission to access Bitlinks.'
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (error.response?.status === 403) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
'Access denied: You do not have permission to list Bitlinks. Please check your Bitly account permissions.'
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Failed to initialize Bitlink monitoring: ${error.message || 'Unknown error occurred'}. Please check your Bitly connection.`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
async onDisable() {
|
|
97
|
+
console.log('Bitly New Bitlink trigger disabled and cleaned up');
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
async run(context) {
|
|
101
|
+
const { group_guid, titleFilter, tagFilter, includeArchived } = context.propsValue;
|
|
102
|
+
const { accessToken } = context.auth.props;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const previousLinkIds = await context.store.get<string[]>(LAST_BITLINK_IDS_KEY) || [];
|
|
106
|
+
|
|
107
|
+
const response = await bitlyApiCall<{ links: BitlyLink[] }>({
|
|
108
|
+
auth: { accessToken },
|
|
109
|
+
method: HttpMethod.GET,
|
|
110
|
+
resourceUri: `/groups/${group_guid}/bitlinks`,
|
|
111
|
+
query: {
|
|
112
|
+
size: 50,
|
|
113
|
+
archived: includeArchived ? 'both' : 'off',
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const allLinks = response.links || [];
|
|
118
|
+
const currentLinkIds = allLinks.map((l) => l.id);
|
|
119
|
+
|
|
120
|
+
await context.store.put<string[]>(LAST_BITLINK_IDS_KEY, currentLinkIds);
|
|
121
|
+
|
|
122
|
+
let newLinks = allLinks.filter((link) => !previousLinkIds.includes(link.id));
|
|
123
|
+
|
|
124
|
+
if (titleFilter && titleFilter.trim()) {
|
|
125
|
+
const filterText = titleFilter.trim().toLowerCase();
|
|
126
|
+
newLinks = newLinks.filter((link) =>
|
|
127
|
+
link.title && link.title.toLowerCase().includes(filterText)
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (tagFilter && tagFilter.trim()) {
|
|
132
|
+
const filterTag = tagFilter.trim().toLowerCase();
|
|
133
|
+
newLinks = newLinks.filter((link) =>
|
|
134
|
+
link.tags && Array.isArray(link.tags) &&
|
|
135
|
+
link.tags.some(tag => tag.toLowerCase().includes(filterTag))
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const processedLinks = newLinks.map((link) => ({
|
|
140
|
+
id: link.id,
|
|
141
|
+
link: link.link,
|
|
142
|
+
longUrl: link.long_url,
|
|
143
|
+
title: link.title,
|
|
144
|
+
tags: link.tags || [],
|
|
145
|
+
|
|
146
|
+
isArchived: link.archived,
|
|
147
|
+
|
|
148
|
+
createdAt: link.created_at,
|
|
149
|
+
modifiedAt: link.modified_at,
|
|
150
|
+
|
|
151
|
+
customBitlinks: link.custom_bitlinks || [],
|
|
152
|
+
|
|
153
|
+
references: {
|
|
154
|
+
group: link.references?.group,
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
rawLinkData: link,
|
|
158
|
+
|
|
159
|
+
triggerInfo: {
|
|
160
|
+
detectedAt: new Date().toISOString(),
|
|
161
|
+
source: 'bitly',
|
|
162
|
+
type: 'new_bitlink',
|
|
163
|
+
},
|
|
164
|
+
}));
|
|
165
|
+
|
|
166
|
+
return processedLinks;
|
|
167
|
+
} catch (error: any) {
|
|
168
|
+
if (error.response?.status === 401) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
'Authentication failed: Your access token may have expired. Please check your Bitly authentication.'
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (error.response?.status === 429) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
'Rate limit exceeded: Bitly API rate limit reached. Consider increasing your polling interval.'
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (error.response?.status === 403) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
'Access denied: You do not have permission to list Bitlinks. Please check your account permissions.'
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Failed to check for new Bitlinks: ${error.message || 'Unknown error occurred'}. The trigger will retry on the next polling interval.`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
async test(context) {
|
|
193
|
+
const { group_guid, includeArchived } = context.propsValue;
|
|
194
|
+
const { accessToken } = context.auth.props;
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const response = await bitlyApiCall<{ links: BitlyLink[] }>({
|
|
198
|
+
auth: { accessToken },
|
|
199
|
+
method: HttpMethod.GET,
|
|
200
|
+
resourceUri: `/groups/${group_guid}/bitlinks`,
|
|
201
|
+
query: {
|
|
202
|
+
size: 1,
|
|
203
|
+
archived: includeArchived ? 'both' : 'off',
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const links = response.links || [];
|
|
208
|
+
|
|
209
|
+
if (links.length > 0) {
|
|
210
|
+
const testLink = links[0];
|
|
211
|
+
return [
|
|
212
|
+
{
|
|
213
|
+
id: testLink.id,
|
|
214
|
+
link: testLink.link,
|
|
215
|
+
longUrl: testLink.long_url,
|
|
216
|
+
title: testLink.title,
|
|
217
|
+
tags: testLink.tags || [],
|
|
218
|
+
isArchived: testLink.archived,
|
|
219
|
+
createdAt: testLink.created_at,
|
|
220
|
+
modifiedAt: testLink.modified_at,
|
|
221
|
+
customBitlinks: testLink.custom_bitlinks || [],
|
|
222
|
+
references: {
|
|
223
|
+
group: testLink.references?.group,
|
|
224
|
+
},
|
|
225
|
+
rawLinkData: testLink,
|
|
226
|
+
triggerInfo: {
|
|
227
|
+
detectedAt: new Date().toISOString(),
|
|
228
|
+
source: 'bitly',
|
|
229
|
+
type: 'new_bitlink',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
];
|
|
233
|
+
} else {
|
|
234
|
+
return [
|
|
235
|
+
{
|
|
236
|
+
id: 'bit.ly/test123',
|
|
237
|
+
link: 'https://bit.ly/test123',
|
|
238
|
+
longUrl: 'https://example.com/very-long-url',
|
|
239
|
+
title: 'Sample Bitlink',
|
|
240
|
+
tags: ['sample', 'test'],
|
|
241
|
+
isArchived: false,
|
|
242
|
+
createdAt: '2025-01-15T10:00:00+0000',
|
|
243
|
+
modifiedAt: '2025-01-15T10:00:00+0000',
|
|
244
|
+
customBitlinks: [],
|
|
245
|
+
references: {
|
|
246
|
+
group: 'Ba1bc23dE4F',
|
|
247
|
+
},
|
|
248
|
+
rawLinkData: {
|
|
249
|
+
id: 'bit.ly/test123',
|
|
250
|
+
link: 'https://bit.ly/test123',
|
|
251
|
+
long_url: 'https://example.com/very-long-url',
|
|
252
|
+
title: 'Sample Bitlink',
|
|
253
|
+
tags: ['sample', 'test'],
|
|
254
|
+
archived: false,
|
|
255
|
+
created_at: '2025-01-15T10:00:00+0000',
|
|
256
|
+
modified_at: '2025-01-15T10:00:00+0000',
|
|
257
|
+
custom_bitlinks: [],
|
|
258
|
+
references: {
|
|
259
|
+
group: 'Ba1bc23dE4F',
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
triggerInfo: {
|
|
263
|
+
detectedAt: new Date().toISOString(),
|
|
264
|
+
source: 'bitly',
|
|
265
|
+
type: 'new_bitlink',
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
} catch (error: any) {
|
|
271
|
+
return [
|
|
272
|
+
{
|
|
273
|
+
id: 'bit.ly/test123',
|
|
274
|
+
link: 'https://bit.ly/test123',
|
|
275
|
+
longUrl: 'https://example.com/test-url',
|
|
276
|
+
title: 'Test Bitlink',
|
|
277
|
+
tags: ['test'],
|
|
278
|
+
isArchived: false,
|
|
279
|
+
createdAt: '2025-01-15T10:00:00+0000',
|
|
280
|
+
modifiedAt: '2025-01-15T10:00:00+0000',
|
|
281
|
+
customBitlinks: [],
|
|
282
|
+
references: {
|
|
283
|
+
group: 'Ba1bc23dE4F',
|
|
284
|
+
},
|
|
285
|
+
rawLinkData: {
|
|
286
|
+
id: 'bit.ly/test123',
|
|
287
|
+
link: 'https://bit.ly/test123',
|
|
288
|
+
long_url: 'https://example.com/test-url',
|
|
289
|
+
title: 'Test Bitlink',
|
|
290
|
+
tags: ['test'],
|
|
291
|
+
archived: false,
|
|
292
|
+
created_at: '2025-01-15T10:00:00+0000',
|
|
293
|
+
modified_at: '2025-01-15T10:00:00+0000',
|
|
294
|
+
custom_bitlinks: [],
|
|
295
|
+
references: {
|
|
296
|
+
group: 'Ba1bc23dE4F',
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
triggerInfo: {
|
|
300
|
+
detectedAt: new Date().toISOString(),
|
|
301
|
+
source: 'bitly',
|
|
302
|
+
type: 'new_bitlink',
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
sampleData: {
|
|
310
|
+
id: 'bit.ly/3XYZ123',
|
|
311
|
+
link: 'https://bit.ly/3XYZ123',
|
|
312
|
+
longUrl: 'https://example.com/marketing-campaign-landing-page',
|
|
313
|
+
title: 'Marketing Campaign Landing Page',
|
|
314
|
+
tags: ['marketing', 'campaign', '2025'],
|
|
315
|
+
isArchived: false,
|
|
316
|
+
createdAt: '2025-01-15T09:30:00+0000',
|
|
317
|
+
modifiedAt: '2025-01-15T09:30:00+0000',
|
|
318
|
+
customBitlinks: [],
|
|
319
|
+
references: {
|
|
320
|
+
group: 'Ba1bc23dE4F',
|
|
321
|
+
},
|
|
322
|
+
rawLinkData: {
|
|
323
|
+
id: 'bit.ly/3XYZ123',
|
|
324
|
+
link: 'https://bit.ly/3XYZ123',
|
|
325
|
+
long_url: 'https://example.com/marketing-campaign-landing-page',
|
|
326
|
+
title: 'Marketing Campaign Landing Page',
|
|
327
|
+
tags: ['marketing', 'campaign', '2025'],
|
|
328
|
+
archived: false,
|
|
329
|
+
created_at: '2025-01-15T09:30:00+0000',
|
|
330
|
+
modified_at: '2025-01-15T09:30:00+0000',
|
|
331
|
+
custom_bitlinks: [],
|
|
332
|
+
references: {
|
|
333
|
+
group: 'Ba1bc23dE4F',
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
triggerInfo: {
|
|
337
|
+
detectedAt: '2025-01-15T09:30:00.000Z',
|
|
338
|
+
source: 'bitly',
|
|
339
|
+
type: 'new_bitlink',
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Interface for Bitly link data structure
|
|
346
|
+
*/
|
|
347
|
+
interface BitlyLink {
|
|
348
|
+
id: string;
|
|
349
|
+
link: string;
|
|
350
|
+
long_url: string;
|
|
351
|
+
title: string;
|
|
352
|
+
tags: string[];
|
|
353
|
+
archived: boolean;
|
|
354
|
+
created_at: string;
|
|
355
|
+
modified_at: string;
|
|
356
|
+
custom_bitlinks: string[];
|
|
357
|
+
references: {
|
|
358
|
+
group: string;
|
|
359
|
+
};
|
|
360
|
+
[key: string]: any;
|
|
361
|
+
}
|