@zapier/google-contacts-connector 0.0.0 → 0.1.0
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 +93 -0
- package/NOTICE +8 -0
- package/README.md +106 -2
- package/SKILL.md +135 -0
- package/cli.js +71 -0
- package/cli.ts +5 -0
- package/connections.ts +8 -0
- package/dist/cli.js +4 -0
- package/dist/index.js +1134 -0
- package/index.ts +64 -0
- package/package.json +59 -4
- package/preflight.sh +157 -0
- package/references/google-contacts-api-gotchas.md +181 -0
- package/scripts/.gitkeep +0 -0
- package/scripts/copyOtherContact.ts +57 -0
- package/scripts/createContact.ts +140 -0
- package/scripts/createContactGroup.ts +51 -0
- package/scripts/deleteContact.ts +52 -0
- package/scripts/deleteContactGroup.ts +61 -0
- package/scripts/deleteContactPhoto.ts +56 -0
- package/scripts/getContact.ts +57 -0
- package/scripts/getContactGroup.ts +71 -0
- package/scripts/listContactGroups.ts +81 -0
- package/scripts/listContacts.ts +110 -0
- package/scripts/listOtherContacts.ts +80 -0
- package/scripts/modifyContactGroupMembers.ts +82 -0
- package/scripts/searchContacts.ts +72 -0
- package/scripts/searchOtherContacts.ts +71 -0
- package/scripts/updateContact.ts +166 -0
- package/scripts/updateContactGroup.ts +67 -0
- package/scripts/updateContactPhoto.ts +64 -0
- package/tsup.config.ts +63 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import {
|
|
7
|
+
AddressInput,
|
|
8
|
+
BiographyInput,
|
|
9
|
+
BirthdayInput,
|
|
10
|
+
ContactEventInput,
|
|
11
|
+
DEFAULT_PERSON_FIELDS,
|
|
12
|
+
EmailAddressInput,
|
|
13
|
+
MembershipInput,
|
|
14
|
+
NameInput,
|
|
15
|
+
OrganizationInput,
|
|
16
|
+
PersonSchema,
|
|
17
|
+
PhoneNumberInput,
|
|
18
|
+
RelationInput,
|
|
19
|
+
throwForGoogleContacts,
|
|
20
|
+
UrlInput,
|
|
21
|
+
UserDefinedInput,
|
|
22
|
+
} from "../lib/google-contacts.ts";
|
|
23
|
+
|
|
24
|
+
const inputSchema = z
|
|
25
|
+
.object({
|
|
26
|
+
names: z
|
|
27
|
+
.array(NameInput)
|
|
28
|
+
.describe(
|
|
29
|
+
"Name parts. Set unstructuredName (the full formatted name) or it is derived from the parts.",
|
|
30
|
+
)
|
|
31
|
+
.optional(),
|
|
32
|
+
emailAddresses: z
|
|
33
|
+
.array(EmailAddressInput)
|
|
34
|
+
.describe("Email addresses.")
|
|
35
|
+
.optional(),
|
|
36
|
+
phoneNumbers: z
|
|
37
|
+
.array(PhoneNumberInput)
|
|
38
|
+
.describe("Phone numbers.")
|
|
39
|
+
.optional(),
|
|
40
|
+
addresses: z.array(AddressInput).describe("Postal addresses.").optional(),
|
|
41
|
+
organizations: z
|
|
42
|
+
.array(OrganizationInput)
|
|
43
|
+
.describe("Companies / job titles.")
|
|
44
|
+
.optional(),
|
|
45
|
+
biographies: z
|
|
46
|
+
.array(BiographyInput)
|
|
47
|
+
.describe("Notes about the contact.")
|
|
48
|
+
.optional(),
|
|
49
|
+
birthdays: z
|
|
50
|
+
.array(BirthdayInput)
|
|
51
|
+
.describe(
|
|
52
|
+
"Birthdays. Use a structured date (year optional for year-less birthdays).",
|
|
53
|
+
)
|
|
54
|
+
.optional(),
|
|
55
|
+
urls: z.array(UrlInput).describe("Websites or profile links.").optional(),
|
|
56
|
+
events: z
|
|
57
|
+
.array(ContactEventInput)
|
|
58
|
+
.describe("Custom dates such as anniversaries.")
|
|
59
|
+
.optional(),
|
|
60
|
+
relations: z
|
|
61
|
+
.array(RelationInput)
|
|
62
|
+
.describe("Related people (e.g. spouse, manager).")
|
|
63
|
+
.optional(),
|
|
64
|
+
userDefined: z
|
|
65
|
+
.array(UserDefinedInput)
|
|
66
|
+
.describe("Custom key/value fields.")
|
|
67
|
+
.optional(),
|
|
68
|
+
memberships: z
|
|
69
|
+
.array(MembershipInput)
|
|
70
|
+
.describe(
|
|
71
|
+
"Contact groups to add this new contact to at creation. For an EXISTING contact use modifyContactGroupMembers instead. Resolve ids via listContactGroups.",
|
|
72
|
+
)
|
|
73
|
+
.optional(),
|
|
74
|
+
personFields: z
|
|
75
|
+
.string()
|
|
76
|
+
.describe(
|
|
77
|
+
"Comma-separated list of contact fields to return (e.g. names,emailAddresses,phoneNumbers). Defaults to a comprehensive set; narrow it to reduce payload. Include metadata for the etag.",
|
|
78
|
+
)
|
|
79
|
+
.default(DEFAULT_PERSON_FIELDS),
|
|
80
|
+
})
|
|
81
|
+
.strict()
|
|
82
|
+
.refine(
|
|
83
|
+
(i) =>
|
|
84
|
+
(i.names?.length ?? 0) > 0 ||
|
|
85
|
+
(i.emailAddresses?.length ?? 0) > 0 ||
|
|
86
|
+
(i.phoneNumbers?.length ?? 0) > 0,
|
|
87
|
+
{
|
|
88
|
+
message:
|
|
89
|
+
"Provide at least one of names, emailAddresses, or phoneNumbers — a contact with no name and no contact method is not useful.",
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const definition = defineTool({
|
|
94
|
+
name: "createContact",
|
|
95
|
+
title: "Create Contact",
|
|
96
|
+
description:
|
|
97
|
+
"Create a new contact from structured name, email, phone, address, and organization fields. Provide at least a name or one contact method.",
|
|
98
|
+
inputSchema,
|
|
99
|
+
outputSchema: PersonSchema,
|
|
100
|
+
annotations: {
|
|
101
|
+
readOnlyHint: false,
|
|
102
|
+
destructiveHint: false,
|
|
103
|
+
idempotentHint: false,
|
|
104
|
+
openWorldHint: true,
|
|
105
|
+
},
|
|
106
|
+
connection: "google-contacts",
|
|
107
|
+
run: async (input, ctx) => {
|
|
108
|
+
const { personFields, memberships, ...sections } = input;
|
|
109
|
+
// Only attach sections that carry values — the API rejects empty optional
|
|
110
|
+
// sections ([] / [{}]) with INVALID_ARGUMENT.
|
|
111
|
+
const body: Record<string, unknown> = {};
|
|
112
|
+
for (const [key, value] of Object.entries(sections)) {
|
|
113
|
+
if (value !== undefined) body[key] = value;
|
|
114
|
+
}
|
|
115
|
+
// memberships is agent-friendly ([{contactGroupResourceName}]); the wire shape
|
|
116
|
+
// nests it under contactGroupMembership.
|
|
117
|
+
if (memberships !== undefined) {
|
|
118
|
+
body.memberships = memberships.map((m) => ({
|
|
119
|
+
contactGroupMembership: {
|
|
120
|
+
contactGroupResourceName: m.contactGroupResourceName,
|
|
121
|
+
},
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
const url = new URL(
|
|
125
|
+
"https://people.googleapis.com/v1/people:createContact",
|
|
126
|
+
);
|
|
127
|
+
url.searchParams.set("personFields", personFields);
|
|
128
|
+
const res = await ctx.fetch(url.toString(), {
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: { "Content-Type": "application/json" },
|
|
131
|
+
body: JSON.stringify(body),
|
|
132
|
+
});
|
|
133
|
+
await throwForGoogleContacts(res, "createContact");
|
|
134
|
+
return res.json();
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
export default definition;
|
|
139
|
+
|
|
140
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import {
|
|
7
|
+
ContactGroupSchema,
|
|
8
|
+
throwForGoogleContacts,
|
|
9
|
+
} from "../lib/google-contacts.ts";
|
|
10
|
+
|
|
11
|
+
const inputSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
name: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe(
|
|
16
|
+
"The contact group (label) name. A duplicate name returns an already-exists error.",
|
|
17
|
+
),
|
|
18
|
+
})
|
|
19
|
+
.strict();
|
|
20
|
+
|
|
21
|
+
const definition = defineTool({
|
|
22
|
+
name: "createContactGroup",
|
|
23
|
+
title: "Create Contact Group",
|
|
24
|
+
description:
|
|
25
|
+
"Create a new user contact group (label). Returns the resourceName used to add members via modifyContactGroupMembers.",
|
|
26
|
+
inputSchema,
|
|
27
|
+
outputSchema: ContactGroupSchema,
|
|
28
|
+
annotations: {
|
|
29
|
+
readOnlyHint: false,
|
|
30
|
+
destructiveHint: false,
|
|
31
|
+
idempotentHint: false,
|
|
32
|
+
openWorldHint: true,
|
|
33
|
+
},
|
|
34
|
+
connection: "google-contacts",
|
|
35
|
+
run: async (input, ctx) => {
|
|
36
|
+
const res = await ctx.fetch(
|
|
37
|
+
"https://people.googleapis.com/v1/contactGroups",
|
|
38
|
+
{
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: { "Content-Type": "application/json" },
|
|
41
|
+
body: JSON.stringify({ contactGroup: { name: input.name } }),
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
await throwForGoogleContacts(res, "createContactGroup");
|
|
45
|
+
return res.json();
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export default definition;
|
|
50
|
+
|
|
51
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import { throwForGoogleContacts } from "../lib/google-contacts.ts";
|
|
7
|
+
|
|
8
|
+
const inputSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
resourceName: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe(
|
|
13
|
+
"Contact resource name, e.g. people/c12345 (from listContacts or searchContacts). Pass it whole, including the people/ prefix.",
|
|
14
|
+
),
|
|
15
|
+
})
|
|
16
|
+
.strict();
|
|
17
|
+
|
|
18
|
+
const definition = defineTool({
|
|
19
|
+
name: "deleteContact",
|
|
20
|
+
title: "Delete Contact",
|
|
21
|
+
description:
|
|
22
|
+
"Delete a contact from the user's account. A 404 means nothing was deleted — the resourceName wasn't found (already deleted, or a wrong/typo'd id); it is NOT a success. Re-resolve the contact via searchContacts/listContacts before retrying.",
|
|
23
|
+
inputSchema,
|
|
24
|
+
outputSchema: z.object({
|
|
25
|
+
success: z
|
|
26
|
+
.boolean()
|
|
27
|
+
.describe(
|
|
28
|
+
"True when the contact was deleted (the API returns an empty body).",
|
|
29
|
+
),
|
|
30
|
+
}),
|
|
31
|
+
annotations: {
|
|
32
|
+
readOnlyHint: false,
|
|
33
|
+
destructiveHint: true,
|
|
34
|
+
// Not idempotent: a 404 (already-deleted or wrong id) throws rather than
|
|
35
|
+
// reporting success, so a blind retry isn't safe — the agent must re-resolve.
|
|
36
|
+
idempotentHint: false,
|
|
37
|
+
openWorldHint: true,
|
|
38
|
+
},
|
|
39
|
+
connection: "google-contacts",
|
|
40
|
+
run: async (input, ctx) => {
|
|
41
|
+
// resourceName (people/c…) is a Google resource path — the slash is significant
|
|
42
|
+
// and must NOT be percent-encoded.
|
|
43
|
+
const url = `https://people.googleapis.com/v1/${input.resourceName}:deleteContact`;
|
|
44
|
+
const res = await ctx.fetch(url, { method: "DELETE" });
|
|
45
|
+
await throwForGoogleContacts(res, "deleteContact");
|
|
46
|
+
return { success: true };
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export default definition;
|
|
51
|
+
|
|
52
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import { throwForGoogleContacts } from "../lib/google-contacts.ts";
|
|
7
|
+
|
|
8
|
+
const inputSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
resourceName: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe(
|
|
13
|
+
"Contact group resource name, e.g. contactGroups/1a2b3c (from listContactGroups). Pass it whole, including the contactGroups/ prefix.",
|
|
14
|
+
),
|
|
15
|
+
deleteContacts: z
|
|
16
|
+
.boolean()
|
|
17
|
+
.describe(
|
|
18
|
+
"When true, also delete the contacts that were in the group (not just the label). Defaults to false (label only).",
|
|
19
|
+
)
|
|
20
|
+
.default(false),
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
|
|
24
|
+
const definition = defineTool({
|
|
25
|
+
name: "deleteContactGroup",
|
|
26
|
+
title: "Delete Contact Group",
|
|
27
|
+
description:
|
|
28
|
+
"Delete a user contact group (label). System groups (myContacts, starred) cannot be deleted. Optionally delete the member contacts too. A 404 means nothing was deleted — the resourceName wasn't found (already deleted, or a wrong id); it is NOT a success. Re-resolve via listContactGroups before retrying.",
|
|
29
|
+
inputSchema,
|
|
30
|
+
outputSchema: z.object({
|
|
31
|
+
success: z
|
|
32
|
+
.boolean()
|
|
33
|
+
.describe(
|
|
34
|
+
"True when the group was deleted (the API returns an empty body).",
|
|
35
|
+
),
|
|
36
|
+
}),
|
|
37
|
+
annotations: {
|
|
38
|
+
readOnlyHint: false,
|
|
39
|
+
destructiveHint: true,
|
|
40
|
+
// Not idempotent: a 404 (already-deleted or wrong id) throws rather than
|
|
41
|
+
// reporting success, so a blind retry isn't safe — the agent must re-resolve.
|
|
42
|
+
idempotentHint: false,
|
|
43
|
+
openWorldHint: true,
|
|
44
|
+
},
|
|
45
|
+
connection: "google-contacts",
|
|
46
|
+
run: async (input, ctx) => {
|
|
47
|
+
// resourceName (contactGroups/…) is a Google resource path — the slash is
|
|
48
|
+
// significant and must NOT be percent-encoded.
|
|
49
|
+
const url = new URL(
|
|
50
|
+
`https://people.googleapis.com/v1/${input.resourceName}`,
|
|
51
|
+
);
|
|
52
|
+
url.searchParams.set("deleteContacts", String(input.deleteContacts));
|
|
53
|
+
const res = await ctx.fetch(url.toString(), { method: "DELETE" });
|
|
54
|
+
await throwForGoogleContacts(res, "deleteContactGroup");
|
|
55
|
+
return { success: true };
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export default definition;
|
|
60
|
+
|
|
61
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_PERSON_FIELDS,
|
|
8
|
+
PersonResponseSchema,
|
|
9
|
+
throwForGoogleContacts,
|
|
10
|
+
} from "../lib/google-contacts.ts";
|
|
11
|
+
|
|
12
|
+
const inputSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
resourceName: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe(
|
|
17
|
+
"Contact resource name, e.g. people/c12345 (from listContacts or searchContacts). Pass it whole, including the people/ prefix.",
|
|
18
|
+
),
|
|
19
|
+
personFields: z
|
|
20
|
+
.string()
|
|
21
|
+
.describe(
|
|
22
|
+
"Comma-separated list of contact fields to return (e.g. names,emailAddresses,phoneNumbers). Defaults to a comprehensive set; narrow it to reduce payload. Include metadata for the etag.",
|
|
23
|
+
)
|
|
24
|
+
.default(DEFAULT_PERSON_FIELDS),
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
|
|
28
|
+
const definition = defineTool({
|
|
29
|
+
name: "deleteContactPhoto",
|
|
30
|
+
title: "Delete Contact Photo",
|
|
31
|
+
description: "Remove a contact's photo, reverting to the default avatar.",
|
|
32
|
+
inputSchema,
|
|
33
|
+
outputSchema: PersonResponseSchema,
|
|
34
|
+
annotations: {
|
|
35
|
+
readOnlyHint: false,
|
|
36
|
+
destructiveHint: true,
|
|
37
|
+
idempotentHint: true,
|
|
38
|
+
openWorldHint: true,
|
|
39
|
+
},
|
|
40
|
+
connection: "google-contacts",
|
|
41
|
+
run: async (input, ctx) => {
|
|
42
|
+
// resourceName (people/c…) is a Google resource path — the slash is significant
|
|
43
|
+
// and must NOT be percent-encoded.
|
|
44
|
+
const url = new URL(
|
|
45
|
+
`https://people.googleapis.com/v1/${input.resourceName}:deleteContactPhoto`,
|
|
46
|
+
);
|
|
47
|
+
url.searchParams.set("personFields", input.personFields);
|
|
48
|
+
const res = await ctx.fetch(url.toString(), { method: "DELETE" });
|
|
49
|
+
await throwForGoogleContacts(res, "deleteContactPhoto");
|
|
50
|
+
return res.json();
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export default definition;
|
|
55
|
+
|
|
56
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_PERSON_FIELDS,
|
|
8
|
+
PersonSchema,
|
|
9
|
+
throwForGoogleContacts,
|
|
10
|
+
} from "../lib/google-contacts.ts";
|
|
11
|
+
|
|
12
|
+
const inputSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
resourceName: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe(
|
|
17
|
+
"Contact resource name, e.g. people/c12345 (from listContacts or searchContacts). Pass it whole, including the people/ prefix.",
|
|
18
|
+
),
|
|
19
|
+
personFields: z
|
|
20
|
+
.string()
|
|
21
|
+
.describe(
|
|
22
|
+
"Comma-separated list of contact fields to return (e.g. names,emailAddresses,phoneNumbers). Defaults to a comprehensive set; narrow it to reduce payload. Include metadata for the etag.",
|
|
23
|
+
)
|
|
24
|
+
.default(DEFAULT_PERSON_FIELDS),
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
|
|
28
|
+
const definition = defineTool({
|
|
29
|
+
name: "getContact",
|
|
30
|
+
title: "Get Contact",
|
|
31
|
+
description:
|
|
32
|
+
"Retrieve a single contact by resource name, with full field detail. Call before an additive update to read the existing arrays.",
|
|
33
|
+
inputSchema,
|
|
34
|
+
outputSchema: PersonSchema,
|
|
35
|
+
annotations: {
|
|
36
|
+
readOnlyHint: true,
|
|
37
|
+
destructiveHint: false,
|
|
38
|
+
idempotentHint: true,
|
|
39
|
+
openWorldHint: true,
|
|
40
|
+
},
|
|
41
|
+
connection: "google-contacts",
|
|
42
|
+
run: async (input, ctx) => {
|
|
43
|
+
// resourceName (people/c…) is a Google resource path — the slash is significant
|
|
44
|
+
// and must NOT be percent-encoded, so it is interpolated raw, not via encodeURIComponent.
|
|
45
|
+
const url = new URL(
|
|
46
|
+
`https://people.googleapis.com/v1/${input.resourceName}`,
|
|
47
|
+
);
|
|
48
|
+
url.searchParams.set("personFields", input.personFields);
|
|
49
|
+
const res = await ctx.fetch(url.toString(), { method: "GET" });
|
|
50
|
+
await throwForGoogleContacts(res, "getContact");
|
|
51
|
+
return res.json();
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export default definition;
|
|
56
|
+
|
|
57
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// A single GET /v1/contactGroups/{id}. Kept as its own tool because the bare
|
|
3
|
+
// "GET /{resourceName}" path+method is shared with getContact (people/* vs
|
|
4
|
+
// contactGroups/*), and the two resources need different field params and parsing.
|
|
5
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
import { connectionResolvers } from "../connections.ts";
|
|
9
|
+
import {
|
|
10
|
+
ContactGroupSchema,
|
|
11
|
+
DEFAULT_GROUP_FIELDS,
|
|
12
|
+
throwForGoogleContacts,
|
|
13
|
+
} from "../lib/google-contacts.ts";
|
|
14
|
+
|
|
15
|
+
const inputSchema = z
|
|
16
|
+
.object({
|
|
17
|
+
resourceName: z
|
|
18
|
+
.string()
|
|
19
|
+
.describe(
|
|
20
|
+
"Contact group resource name, e.g. contactGroups/1a2b3c (from listContactGroups). Pass it whole, including the contactGroups/ prefix.",
|
|
21
|
+
),
|
|
22
|
+
maxMembers: z
|
|
23
|
+
.number()
|
|
24
|
+
.int()
|
|
25
|
+
.gte(1)
|
|
26
|
+
.describe(
|
|
27
|
+
"Include up to this many member contact resource names (people/c…) in memberResourceNames. Omit to return group metadata only.",
|
|
28
|
+
)
|
|
29
|
+
.optional(),
|
|
30
|
+
groupFields: z
|
|
31
|
+
.string()
|
|
32
|
+
.describe(
|
|
33
|
+
"Comma-separated list of contact-group fields to return (e.g. name,groupType,memberCount).",
|
|
34
|
+
)
|
|
35
|
+
.default(DEFAULT_GROUP_FIELDS),
|
|
36
|
+
})
|
|
37
|
+
.strict();
|
|
38
|
+
|
|
39
|
+
const definition = defineTool({
|
|
40
|
+
name: "getContactGroup",
|
|
41
|
+
title: "Get Contact Group",
|
|
42
|
+
description:
|
|
43
|
+
"Get a single contact group (label) by resource name, optionally with its member contact resource names (set maxMembers).",
|
|
44
|
+
inputSchema,
|
|
45
|
+
outputSchema: ContactGroupSchema,
|
|
46
|
+
annotations: {
|
|
47
|
+
readOnlyHint: true,
|
|
48
|
+
destructiveHint: false,
|
|
49
|
+
idempotentHint: true,
|
|
50
|
+
openWorldHint: true,
|
|
51
|
+
},
|
|
52
|
+
connection: "google-contacts",
|
|
53
|
+
run: async (input, ctx) => {
|
|
54
|
+
// resourceName (contactGroups/…) is a Google resource path — the slash is
|
|
55
|
+
// significant and must NOT be percent-encoded.
|
|
56
|
+
const url = new URL(
|
|
57
|
+
`https://people.googleapis.com/v1/${input.resourceName}`,
|
|
58
|
+
);
|
|
59
|
+
url.searchParams.set("groupFields", input.groupFields);
|
|
60
|
+
if (input.maxMembers !== undefined) {
|
|
61
|
+
url.searchParams.set("maxMembers", String(input.maxMembers));
|
|
62
|
+
}
|
|
63
|
+
const res = await ctx.fetch(url.toString(), { method: "GET" });
|
|
64
|
+
await throwForGoogleContacts(res, "getContactGroup");
|
|
65
|
+
return res.json();
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export default definition;
|
|
70
|
+
|
|
71
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import {
|
|
7
|
+
ContactGroupSchema,
|
|
8
|
+
DEFAULT_GROUP_FIELDS,
|
|
9
|
+
throwForGoogleContacts,
|
|
10
|
+
} from "../lib/google-contacts.ts";
|
|
11
|
+
|
|
12
|
+
const inputSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
groupFields: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe(
|
|
17
|
+
"Comma-separated list of contact-group fields to return (e.g. name,groupType,memberCount).",
|
|
18
|
+
)
|
|
19
|
+
.default(DEFAULT_GROUP_FIELDS),
|
|
20
|
+
pageSize: z
|
|
21
|
+
.number()
|
|
22
|
+
.int()
|
|
23
|
+
.gte(1)
|
|
24
|
+
.lte(1000)
|
|
25
|
+
.describe(
|
|
26
|
+
"Max groups to return per page. Defaults to 20 when omitted; pass a value when you need a specific number of results.",
|
|
27
|
+
)
|
|
28
|
+
.optional(),
|
|
29
|
+
pageToken: z
|
|
30
|
+
.string()
|
|
31
|
+
.describe(
|
|
32
|
+
"Page cursor from a previous response's next_page_token. Omit for the first page.",
|
|
33
|
+
)
|
|
34
|
+
.optional(),
|
|
35
|
+
})
|
|
36
|
+
.strict();
|
|
37
|
+
|
|
38
|
+
const definition = defineTool({
|
|
39
|
+
name: "listContactGroups",
|
|
40
|
+
title: "List Contact Groups",
|
|
41
|
+
description:
|
|
42
|
+
"List the account's contact groups (labels), user and system, with id, name, type, and member count. The resolver for any contactGroupResourceName.",
|
|
43
|
+
inputSchema,
|
|
44
|
+
outputSchema: z.object({
|
|
45
|
+
contactGroups: z
|
|
46
|
+
.array(ContactGroupSchema)
|
|
47
|
+
.optional()
|
|
48
|
+
.describe("The page of contact groups."),
|
|
49
|
+
next_page_token: z
|
|
50
|
+
.string()
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("Cursor for the next page; absent when there are no more."),
|
|
53
|
+
}),
|
|
54
|
+
annotations: {
|
|
55
|
+
readOnlyHint: true,
|
|
56
|
+
destructiveHint: false,
|
|
57
|
+
idempotentHint: true,
|
|
58
|
+
openWorldHint: true,
|
|
59
|
+
},
|
|
60
|
+
connection: "google-contacts",
|
|
61
|
+
run: async (input, ctx) => {
|
|
62
|
+
const url = new URL("https://people.googleapis.com/v1/contactGroups");
|
|
63
|
+
url.searchParams.set("groupFields", input.groupFields);
|
|
64
|
+
url.searchParams.set("pageSize", String(input.pageSize ?? 20));
|
|
65
|
+
if (input.pageToken !== undefined) {
|
|
66
|
+
url.searchParams.set("pageToken", input.pageToken);
|
|
67
|
+
}
|
|
68
|
+
const res = await ctx.fetch(url.toString(), { method: "GET" });
|
|
69
|
+
await throwForGoogleContacts(res, "listContactGroups");
|
|
70
|
+
const payload = (await res.json()) as Record<string, unknown>;
|
|
71
|
+
if (payload && typeof payload === "object" && "nextPageToken" in payload) {
|
|
72
|
+
payload.next_page_token = payload.nextPageToken;
|
|
73
|
+
delete payload.nextPageToken;
|
|
74
|
+
}
|
|
75
|
+
return payload;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export default definition;
|
|
80
|
+
|
|
81
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_PERSON_FIELDS,
|
|
8
|
+
PersonSchema,
|
|
9
|
+
throwForGoogleContacts,
|
|
10
|
+
} from "../lib/google-contacts.ts";
|
|
11
|
+
|
|
12
|
+
const inputSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
personFields: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe(
|
|
17
|
+
"Comma-separated list of contact fields to return (e.g. names,emailAddresses,phoneNumbers). Defaults to a comprehensive set; narrow it to reduce payload. Include metadata for the etag.",
|
|
18
|
+
)
|
|
19
|
+
.default(DEFAULT_PERSON_FIELDS),
|
|
20
|
+
sortOrder: z
|
|
21
|
+
.enum([
|
|
22
|
+
"LAST_MODIFIED_ASCENDING",
|
|
23
|
+
"LAST_MODIFIED_DESCENDING",
|
|
24
|
+
"FIRST_NAME_ASCENDING",
|
|
25
|
+
"LAST_NAME_ASCENDING",
|
|
26
|
+
])
|
|
27
|
+
.describe("Order of the returned contacts.")
|
|
28
|
+
.optional(),
|
|
29
|
+
pageSize: z
|
|
30
|
+
.number()
|
|
31
|
+
.int()
|
|
32
|
+
.gte(1)
|
|
33
|
+
.lte(1000)
|
|
34
|
+
.describe(
|
|
35
|
+
"Max contacts to return per page. Defaults to 20 when omitted; pass a value when you need a specific number of results.",
|
|
36
|
+
)
|
|
37
|
+
.optional(),
|
|
38
|
+
pageToken: z
|
|
39
|
+
.string()
|
|
40
|
+
.describe(
|
|
41
|
+
"Page cursor from a previous response's next_page_token. Omit for the first page.",
|
|
42
|
+
)
|
|
43
|
+
.optional(),
|
|
44
|
+
})
|
|
45
|
+
.strict();
|
|
46
|
+
|
|
47
|
+
const definition = defineTool({
|
|
48
|
+
name: "listContacts",
|
|
49
|
+
title: "List Contacts",
|
|
50
|
+
description:
|
|
51
|
+
"List the user's contacts, paginated, with full field detail. The primary contact enumerator and resourceName resolver. Use this (not searchContacts) when freshness matters.",
|
|
52
|
+
inputSchema,
|
|
53
|
+
outputSchema: z.object({
|
|
54
|
+
contacts: z
|
|
55
|
+
.array(PersonSchema)
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("The page of contacts."),
|
|
58
|
+
next_page_token: z
|
|
59
|
+
.string()
|
|
60
|
+
.optional()
|
|
61
|
+
.describe("Cursor for the next page; absent when there are no more."),
|
|
62
|
+
total_people: z
|
|
63
|
+
.number()
|
|
64
|
+
.int()
|
|
65
|
+
.optional()
|
|
66
|
+
.describe("Total contacts on the account."),
|
|
67
|
+
}),
|
|
68
|
+
annotations: {
|
|
69
|
+
readOnlyHint: true,
|
|
70
|
+
destructiveHint: false,
|
|
71
|
+
idempotentHint: true,
|
|
72
|
+
openWorldHint: true,
|
|
73
|
+
},
|
|
74
|
+
connection: "google-contacts",
|
|
75
|
+
run: async (input, ctx) => {
|
|
76
|
+
const url = new URL(
|
|
77
|
+
"https://people.googleapis.com/v1/people/me/connections",
|
|
78
|
+
);
|
|
79
|
+
url.searchParams.set("personFields", input.personFields);
|
|
80
|
+
if (input.sortOrder !== undefined) {
|
|
81
|
+
url.searchParams.set("sortOrder", input.sortOrder);
|
|
82
|
+
}
|
|
83
|
+
url.searchParams.set("pageSize", String(input.pageSize ?? 20));
|
|
84
|
+
if (input.pageToken !== undefined) {
|
|
85
|
+
url.searchParams.set("pageToken", input.pageToken);
|
|
86
|
+
}
|
|
87
|
+
const res = await ctx.fetch(url.toString(), { method: "GET" });
|
|
88
|
+
await throwForGoogleContacts(res, "listContacts");
|
|
89
|
+
const payload = (await res.json()) as Record<string, unknown>;
|
|
90
|
+
if (payload && typeof payload === "object") {
|
|
91
|
+
if ("connections" in payload) {
|
|
92
|
+
payload.contacts = payload.connections;
|
|
93
|
+
delete payload.connections;
|
|
94
|
+
}
|
|
95
|
+
if ("nextPageToken" in payload) {
|
|
96
|
+
payload.next_page_token = payload.nextPageToken;
|
|
97
|
+
delete payload.nextPageToken;
|
|
98
|
+
}
|
|
99
|
+
if ("totalPeople" in payload) {
|
|
100
|
+
payload.total_people = payload.totalPeople;
|
|
101
|
+
delete payload.totalPeople;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return payload;
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
export default definition;
|
|
109
|
+
|
|
110
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|