Skip to content

Commit 2a50e2c

Browse files
committed
fix(knack): fix knack actions
1 parent 48e0b5a commit 2a50e2c

13 files changed

Lines changed: 228 additions & 794 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"extends": ["../../../../.eslintrc.json"],
3+
"ignorePatterns": ["!**/*"],
4+
"overrides": [
5+
{
6+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7+
"rules": {}
8+
},
9+
{
10+
"files": ["*.ts", "*.tsx"],
11+
"rules": {}
12+
},
13+
{
14+
"files": ["*.js", "*.jsx"],
15+
"rules": {}
16+
}
17+
]
18+
}

packages/pieces/community/knack/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import { createRecordAction } from './lib/actions/create-record';
55
import { deleteRecordAction } from './lib/actions/delete-record';
66
import { findRecordAction } from './lib/actions/find-record';
77
import { updateRecordAction } from './lib/actions/update-record';
8+
import { PieceCategory } from '@activepieces/shared';
89

910
export const knack = createPiece({
1011
displayName: 'Knack',
1112
auth: knackAuth,
1213
minimumSupportedRelease: '0.20.0',
1314
logoUrl: 'https://cdn.activepieces.com/pieces/knack.png',
15+
categories:[PieceCategory.CONTENT_AND_FILES],
1416
authors: ['aryel780'],
1517
actions: [
1618
createRecordAction,

packages/pieces/community/knack/src/lib/actions/create-record.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,62 @@
11
import { HttpMethod } from '@activepieces/pieces-common';
2-
import { createAction, Property } from '@activepieces/pieces-framework';
3-
import { knackApiCall, KnackAuthProps } from '../common/client';
2+
import { createAction } from '@activepieces/pieces-framework';
3+
import { knackApiCall } from '../common/client';
44
import { knackAuth } from '../common/auth';
5-
import { objectDropdown } from '../common/props';
5+
import {
6+
KnackGetObjectResponse,
7+
knackTransformFields,
8+
objectDropdown,
9+
recordFields,
10+
} from '../common/props';
611

712
export const createRecordAction = createAction({
813
auth: knackAuth,
914
name: 'create_record',
1015
displayName: 'Create Record',
11-
description: 'Insert a new record into a specified object/table.',
16+
description: 'Creates a new record into a specified object/table.',
1217
props: {
1318
object: objectDropdown,
14-
recordData: Property.Json({
15-
displayName: 'Record Data',
16-
description: 'The data for the new record in JSON format. Use field IDs as keys (e.g., {"field_1": "Value A", "field_2": 123}).',
17-
required: true,
18-
}),
19+
recordFields: recordFields,
1920
},
2021
async run({ propsValue, auth }) {
21-
const { object: objectKey, recordData } = propsValue;
22+
const { object: objectKey, recordFields: recordData } = propsValue;
2223

2324
try {
24-
const response = await knackApiCall({
25+
const response = await knackApiCall<Record<string, any>>({
2526
method: HttpMethod.POST,
2627
auth: auth,
2728
resourceUri: `/objects/${objectKey}/records`,
2829
body: recordData,
2930
});
30-
return response;
31+
32+
const objectDetails = await knackApiCall<KnackGetObjectResponse>({
33+
method: HttpMethod.GET,
34+
auth,
35+
resourceUri: `/objects/${objectKey}`,
36+
});
37+
38+
const transformedRecord = knackTransformFields(objectDetails, response);
39+
40+
return transformedRecord;
3141
} catch (error: any) {
3242
if (error.message.includes('409')) {
3343
throw new Error(
3444
'Conflict: The record could not be created due to a conflict, such as a duplicate unique value.'
3545
);
3646
}
37-
47+
3848
if (error.message.includes('400')) {
3949
throw new Error(
4050
'Bad Request: Invalid request parameters. Please check your Record Data JSON and field values.'
4151
);
4252
}
43-
53+
4454
if (error.message.includes('401') || error.message.includes('403')) {
4555
throw new Error(
4656
'Authentication Failed: Please check your API Key, Application ID, and user permissions.'
4757
);
4858
}
49-
59+
5060
if (error.message.includes('429')) {
5161
throw new Error(
5262
'Rate Limit Exceeded: Too many requests. Please wait a moment before trying again.'

packages/pieces/community/knack/src/lib/actions/delete-record.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@ import { HttpMethod } from '@activepieces/pieces-common';
22
import { createAction, Property } from '@activepieces/pieces-framework';
33
import { knackApiCall, KnackAuthProps } from '../common/client';
44
import { knackAuth } from '../common/auth';
5-
import { objectDropdown, recordIdDropdown } from '../common/props';
5+
import { objectDropdown } from '../common/props';
66

77
export const deleteRecordAction = createAction({
88
auth: knackAuth,
99
name: 'delete_record',
1010
displayName: 'Delete Record',
11-
description: 'Permanently delete a record from a table.',
11+
description: 'Deletes an existing record from a table.',
1212
props: {
1313
object: objectDropdown,
14-
recordId: recordIdDropdown,
14+
recordId: Property.ShortText({
15+
displayName: 'Record ID',
16+
required: true,
17+
description: 'The ID of the record to delete.',
18+
}),
1519
},
1620
async run({ propsValue, auth }) {
1721
const { object: objectKey, recordId } = propsValue;

packages/pieces/community/knack/src/lib/actions/find-record.ts

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,62 @@
11
import { HttpMethod } from '@activepieces/pieces-common';
22
import { createAction, Property } from '@activepieces/pieces-framework';
3-
import { knackApiCall, KnackAuthProps } from '../common/client';
3+
import { knackApiCall } from '../common/client';
44
import { knackAuth } from '../common/auth';
5-
import { objectDropdown } from '../common/props';
5+
import {
6+
fieldIdDropdown,
7+
KnackGetObjectResponse,
8+
knackTransformFields,
9+
objectDropdown,
10+
} from '../common/props';
611

712
export const findRecordAction = createAction({
813
auth: knackAuth,
914
name: 'find_record',
1015
displayName: 'Find Record',
11-
description:
12-
'Search for a single record using field filters (e.g., email, ID).',
16+
description: 'Finds a single record using field value.',
1317
props: {
1418
object: objectDropdown,
15-
filters: Property.Json({
16-
displayName: 'Filter Rules',
17-
description: 'The Knack filter rules to find the record.',
19+
fieldId: fieldIdDropdown,
20+
fieldValue: Property.ShortText({
21+
displayName: 'Field Value',
1822
required: true,
19-
defaultValue: {
20-
match: 'and',
21-
rules: [
22-
{
23-
field: 'field_1',
24-
operator: 'is',
25-
value: 'example@email.com',
26-
},
27-
],
28-
},
23+
description: 'The value to search for in the specified field.',
2924
}),
3025
},
3126
async run({ propsValue, auth }) {
32-
const { object: objectKey, filters } = propsValue;
27+
const { object: objectKey, fieldId, fieldValue } = propsValue;
3328

3429
try {
35-
const response = await knackApiCall<{ records: unknown[] }>({
30+
const response = await knackApiCall<{ records: Record<string, any>[] }>({
3631
method: HttpMethod.GET,
3732
auth: auth,
3833
resourceUri: `/objects/${objectKey}/records`,
3934
query: {
40-
filters: JSON.stringify(filters),
35+
filters: JSON.stringify([
36+
{
37+
field: fieldId,
38+
operator: 'is',
39+
value: fieldValue,
40+
},
41+
]),
4142
rows_per_page: '1',
4243
},
4344
});
4445

4546
if (response.records && response.records.length > 0) {
47+
const objectDetails = await knackApiCall<KnackGetObjectResponse>({
48+
method: HttpMethod.GET,
49+
auth,
50+
resourceUri: `/objects/${objectKey}`,
51+
});
52+
53+
const transformedRecord = knackTransformFields(
54+
objectDetails,
55+
response.records[0]
56+
);
4657
return {
4758
found: true,
48-
record: response.records[0],
59+
record: transformedRecord,
4960
};
5061
}
5162

packages/pieces/community/knack/src/lib/actions/update-record.ts

Lines changed: 21 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -3,122 +3,45 @@ import { createAction, Property } from '@activepieces/pieces-framework';
33
import { knackApiCall } from '../common/client';
44
import { knackAuth } from '../common/auth';
55
import {
6+
KnackGetObjectResponse,
7+
knackTransformFields,
68
objectDropdown,
7-
recordIdDropdown,
8-
dynamicRecordFields,
9+
recordFields,
910
} from '../common/props';
1011

1112
export const updateRecordAction = createAction({
1213
auth: knackAuth,
1314
name: 'update_record',
1415
displayName: 'Update Record',
15-
description:
16-
'Update fields of an existing record with an intuitive form interface.',
16+
description: 'Updates an existing record.',
1717
props: {
1818
object: objectDropdown,
19-
recordId: recordIdDropdown,
20-
recordFields: dynamicRecordFields,
21-
advancedMode: Property.Checkbox({
22-
displayName: 'Advanced Mode',
23-
description: 'Use raw JSON input instead of the form interface',
24-
required: false,
25-
defaultValue: false,
26-
}),
27-
recordData: Property.Json({
28-
displayName: 'Data to Update (JSON)',
29-
description:
30-
'The data to update in JSON format. Only the fields you include will be changed (e.g., {"field_1": "New Value"}). This field is only used when Advanced Mode is enabled.',
31-
required: false,
19+
recordId: Property.ShortText({
20+
displayName: 'Record ID',
21+
required: true,
22+
description: 'The ID of the record to update.',
3223
}),
24+
recordFields: recordFields,
3325
},
3426
async run({ propsValue, auth }) {
35-
const {
36-
object: objectKey,
37-
recordId,
38-
recordFields,
39-
advancedMode,
40-
recordData,
41-
} = propsValue;
42-
43-
let updateData: any;
44-
45-
if (advancedMode && recordData) {
46-
updateData = recordData;
47-
} else if (recordFields) {
48-
updateData = {};
49-
50-
for (const [fieldKey, fieldValue] of Object.entries(recordFields)) {
51-
if (
52-
fieldValue === undefined ||
53-
fieldValue === null ||
54-
fieldValue === '' ||
55-
(typeof fieldValue === 'string' && fieldValue.trim() === '')
56-
) {
57-
continue;
58-
}
59-
60-
if (
61-
fieldKey.includes('_street') ||
62-
fieldKey.includes('_city') ||
63-
fieldKey.includes('_state') ||
64-
fieldKey.includes('_zip')
65-
) {
66-
const baseFieldKey = fieldKey.replace(
67-
/_street|_city|_state|_zip/,
68-
''
69-
);
70-
if (!updateData[baseFieldKey]) {
71-
updateData[baseFieldKey] = {};
72-
}
73-
74-
if (fieldKey.endsWith('_street')) {
75-
updateData[baseFieldKey].street = fieldValue;
76-
} else if (fieldKey.endsWith('_city')) {
77-
updateData[baseFieldKey].city = fieldValue;
78-
} else if (fieldKey.endsWith('_state')) {
79-
updateData[baseFieldKey].state = fieldValue;
80-
} else if (fieldKey.endsWith('_zip')) {
81-
updateData[baseFieldKey].zip = fieldValue;
82-
}
83-
} else {
84-
if (typeof fieldValue === 'string') {
85-
if (fieldValue.startsWith('{') && fieldValue.endsWith('}')) {
86-
try {
87-
const parsed = JSON.parse(fieldValue);
88-
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
89-
continue;
90-
}
91-
} catch (e) {
92-
}
93-
}
94-
if (fieldValue === '[object Object]') {
95-
continue;
96-
}
97-
}
98-
99-
updateData[fieldKey] = fieldValue;
100-
}
101-
}
102-
103-
if (Object.keys(updateData).length === 0) {
104-
throw new Error(
105-
'No fields to update. Please fill in at least one field with a value.'
106-
);
107-
}
108-
} else {
109-
throw new Error(
110-
'No update data provided. Either enable Advanced Mode and provide JSON data, or fill out the form fields.'
111-
);
112-
}
27+
const { object: objectKey, recordId, recordFields } = propsValue;
11328

11429
try {
115-
const response = await knackApiCall({
30+
const response = await knackApiCall<Record<string, any>>({
11631
method: HttpMethod.PUT,
11732
auth: auth,
11833
resourceUri: `/objects/${objectKey}/records/${recordId}`,
119-
body: updateData,
34+
body: recordFields,
12035
});
121-
return response;
36+
const objectDetails = await knackApiCall<KnackGetObjectResponse>({
37+
method: HttpMethod.GET,
38+
auth,
39+
resourceUri: `/objects/${objectKey}`,
40+
});
41+
42+
const transformedRecord = knackTransformFields(objectDetails, response);
43+
44+
return transformedRecord;
12245
} catch (error: any) {
12346
if (error.message.includes('404')) {
12447
throw new Error(

packages/pieces/community/knack/src/lib/common/auth.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { knackApiCall } from './client';
33
import { HttpMethod } from '@activepieces/pieces-common';
44

55
export const knackAuth = PieceAuth.CustomAuth({
6-
description: 'Enter your Knack API Key and Application ID',
76
props: {
87
apiKey: PieceAuth.SecretText({
98
displayName: 'API Key',

packages/pieces/community/knack/src/lib/common/client.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,17 @@ export async function knackApiCall<T extends HttpMessageBody>({
102102
);
103103

104104
default:
105-
const errorMessage = errorData?.message ||
105+
{
106+
const errorMessage = errorData?.message ||
106107
error.message ||
107108
'Unknown error occurred';
108109

109110
throw new Error(
110111
`Knack API Error (${statusCode || 'Unknown'}): ${errorMessage}`
111112
);
113+
}
114+
115+
112116
}
113117
}
114118
}

0 commit comments

Comments
 (0)