@kubun/connector-google-contacts 0.17.2 → 0.18.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/lib/actions.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ActionHandler } from '@kubun/connector';
1
+ import { type ActionHandler } from '@kubun/connector';
2
2
  export type GoogleContactsActionHandlerConfig = {
3
3
  credential: {
4
4
  accessToken: string;
package/lib/actions.js CHANGED
@@ -1,16 +1,18 @@
1
+ import { carryField, refuseCutLists } from '@kubun/connector';
1
2
  import { createContact, updateContact } from './api.js';
2
3
  // The People API writes a contact's organization name and job title as one
3
4
  // `organizations` entry, so a write that changes only one of them carries the
4
5
  // other from the target document — sending one alone would clear the other.
5
- function withOrganizationPair(params, existing) {
6
+ // A carried value trimmed at sync is refused (`carryField`), never sent.
7
+ function withOrganizationPair(params, existing, context) {
6
8
  const changesOne = 'organization' in params !== 'jobTitle' in params;
7
9
  if (!changesOne || existing == null) return params;
8
10
  return 'organization' in params ? {
9
11
  ...params,
10
- jobTitle: existing.jobTitle
12
+ jobTitle: carryField(existing, 'jobTitle', context)
11
13
  } : {
12
14
  ...params,
13
- organization: existing.organization
15
+ organization: carryField(existing, 'organization', context)
14
16
  };
15
17
  }
16
18
  export function createGoogleContactsActionHandler(config) {
@@ -29,10 +31,11 @@ export function createGoogleContactsActionHandler(config) {
29
31
  };
30
32
  },
31
33
  async update (_model, sourceID, params, existing, context) {
34
+ refuseCutLists(params, context);
32
35
  const record = await updateContact({
33
36
  accessToken: config.credential.accessToken,
34
37
  sourceID,
35
- params: withOrganizationPair(params, existing),
38
+ params: withOrganizationPair(params, existing, context),
36
39
  etag: context?.providerETag,
37
40
  fetch: config.fetch
38
41
  });
package/lib/api.d.ts CHANGED
@@ -1,26 +1,60 @@
1
1
  import { type EntityRecord } from '@kubun/connector';
2
2
  import { type GoogleAPIClient } from '@kubun/provider-google';
3
+ export type GooglePersonName = {
4
+ displayName?: string;
5
+ givenName?: string;
6
+ familyName?: string;
7
+ unstructuredName?: string;
8
+ };
9
+ export type GooglePersonLabeledValue = {
10
+ value?: string;
11
+ type?: string;
12
+ metadata?: {
13
+ primary?: boolean;
14
+ };
15
+ };
16
+ export type GooglePersonAddress = {
17
+ streetAddress?: string;
18
+ city?: string;
19
+ region?: string;
20
+ postalCode?: string;
21
+ country?: string;
22
+ type?: string;
23
+ metadata?: {
24
+ primary?: boolean;
25
+ };
26
+ };
27
+ export type GooglePersonOrganization = {
28
+ name?: string;
29
+ title?: string;
30
+ };
31
+ export type GooglePersonBirthday = {
32
+ date?: {
33
+ year?: number;
34
+ month?: number;
35
+ day?: number;
36
+ };
37
+ };
38
+ export type GooglePersonPhoto = {
39
+ url?: string;
40
+ default?: boolean;
41
+ };
3
42
  export type GooglePersonResource = {
4
43
  resourceName: string;
5
44
  etag?: string;
6
- names?: Array<{
7
- displayName?: string;
8
- givenName?: string;
9
- familyName?: string;
10
- }>;
11
- emailAddresses?: Array<{
45
+ names?: Array<GooglePersonName>;
46
+ nicknames?: Array<{
12
47
  value?: string;
13
48
  }>;
14
- phoneNumbers?: Array<{
49
+ emailAddresses?: Array<GooglePersonLabeledValue>;
50
+ phoneNumbers?: Array<GooglePersonLabeledValue>;
51
+ addresses?: Array<GooglePersonAddress>;
52
+ organizations?: Array<GooglePersonOrganization>;
53
+ birthdays?: Array<GooglePersonBirthday>;
54
+ biographies?: Array<{
15
55
  value?: string;
16
56
  }>;
17
- organizations?: Array<{
18
- name?: string;
19
- title?: string;
20
- }>;
21
- photos?: Array<{
22
- url?: string;
23
- }>;
57
+ photos?: Array<GooglePersonPhoto>;
24
58
  metadata?: {
25
59
  deleted?: boolean;
26
60
  };
@@ -32,11 +66,12 @@ export type GoogleConnectionsResponse = {
32
66
  nextSyncToken?: string;
33
67
  totalPeople?: number;
34
68
  };
35
- export declare function buildListURL(params: {
69
+ export type BuildListURLParams = {
36
70
  pageToken?: string;
37
71
  syncToken?: string;
38
72
  pageSize?: number;
39
- }): string;
73
+ };
74
+ export declare function buildListURL(params: BuildListURLParams): string;
40
75
  export declare function mapPersonToEntity(person: GooglePersonResource): EntityRecord;
41
76
  export declare function buildPersonFromParams(params: Record<string, unknown>): Partial<GooglePersonResource>;
42
77
  export declare function getPerson(config: {
package/lib/api.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { classifyHTTPStatus, connectorHTTPError } from '@kubun/connector';
2
2
  import { createGoogleAPIClient } from '@kubun/provider-google';
3
3
  import { ContactEtagRequired, GoogleContactsError } from './errors.js';
4
- const PERSON_FIELDS = 'names,emailAddresses,phoneNumbers,organizations,photos';
4
+ const PERSON_FIELDS = 'names,nicknames,emailAddresses,phoneNumbers,addresses,organizations,birthdays,biographies,photos';
5
5
  const CONTACTS_BASE_URL = 'https://people.googleapis.com/v1';
6
6
  // A Google People API resource name is `people/{id}`. A sourceID reaches here
7
7
  // from caller-controlled GraphQL input and is interpolated straight into the
@@ -46,43 +46,164 @@ export function buildListURL(params) {
46
46
  }
47
47
  return url.toString();
48
48
  }
49
+ // An empty provider string is no value: the model's text fields hold at
50
+ // least one character.
51
+ function text(value) {
52
+ return value == null || value === '' ? undefined : value;
53
+ }
54
+ function nonEmpty(items) {
55
+ return items == null || items.length === 0 ? undefined : items;
56
+ }
57
+ // Emails and phone numbers share the same labeled-value shape at Google
58
+ // (value, a free-text type, and a primary bit) and in the model (LabeledValue).
59
+ function mapLabeledValue(entry) {
60
+ const mapped = {
61
+ value: entry.value
62
+ };
63
+ const label = text(entry.type);
64
+ if (label != null) mapped.label = label;
65
+ if (entry.metadata?.primary === true) mapped.primary = true;
66
+ return mapped;
67
+ }
68
+ function mapAddress(entry) {
69
+ const mapped = {};
70
+ const street = text(entry.streetAddress);
71
+ if (street != null) mapped.street = street;
72
+ const locality = text(entry.city);
73
+ if (locality != null) mapped.locality = locality;
74
+ const region = text(entry.region);
75
+ if (region != null) mapped.region = region;
76
+ const postalCode = text(entry.postalCode);
77
+ if (postalCode != null) mapped.postalCode = postalCode;
78
+ const country = text(entry.country);
79
+ if (country != null) mapped.country = country;
80
+ const label = text(entry.type);
81
+ if (label != null) mapped.label = label;
82
+ if (entry.metadata?.primary === true) mapped.primary = true;
83
+ return mapped;
84
+ }
85
+ function pad2(value) {
86
+ return String(value).padStart(2, '0');
87
+ }
88
+ // Google keeps a birthday with no year as `{ month, day }` alone. The
89
+ // model's birthday is a single LocalDate, which cannot name a year-less day
90
+ // without inventing one, so a year-less birthday is left unmapped rather
91
+ // than guessing a year.
92
+ function mapBirthday(person) {
93
+ const date = person.birthdays?.[0]?.date;
94
+ if (date?.year == null || date.month == null || date.day == null) return undefined;
95
+ return `${date.year}-${pad2(date.month)}-${pad2(date.day)}`;
96
+ }
97
+ function mapPrimaryPhotoURL(person) {
98
+ const photos = person.photos ?? [];
99
+ return (photos.find((photo)=>photo.default === true) ?? photos[0])?.url;
100
+ }
49
101
  export function mapPersonToEntity(person) {
50
102
  return {
51
103
  sourceService: 'google-contacts',
52
104
  sourceID: person.resourceName,
53
105
  displayName: person.names?.[0]?.displayName ?? '',
54
- email: person.emailAddresses?.[0]?.value ?? undefined,
55
- phoneNumber: person.phoneNumbers?.[0]?.value ?? undefined,
106
+ givenName: text(person.names?.[0]?.givenName),
107
+ familyName: text(person.names?.[0]?.familyName),
108
+ nickname: text(person.nicknames?.[0]?.value),
109
+ emails: nonEmpty(person.emailAddresses)?.map(mapLabeledValue),
110
+ phones: nonEmpty(person.phoneNumbers)?.map(mapLabeledValue),
111
+ addresses: nonEmpty(person.addresses)?.map(mapAddress),
56
112
  organization: person.organizations?.[0]?.name ?? undefined,
57
113
  jobTitle: person.organizations?.[0]?.title ?? undefined,
58
- photoURL: person.photos?.[0]?.url ?? undefined,
114
+ birthday: mapBirthday(person),
115
+ // Photos are read-only on write (the People API has no way to set a
116
+ // photo via updateContact), and the model keeps only the one the
117
+ // provider marks default (or else the first) — never the whole list.
118
+ photoURL: mapPrimaryPhotoURL(person),
119
+ note: text(person.biographies?.[0]?.value),
59
120
  sourceURL: `https://contacts.google.com/person/${person.resourceName.replace('people/', '')}`,
60
121
  etag: person.etag
61
122
  };
62
123
  }
124
+ function buildLabeledValues(values) {
125
+ return values.map((value)=>{
126
+ const entry = value ?? {};
127
+ const built = {
128
+ value: entry.value
129
+ };
130
+ if (typeof entry.label === 'string') built.type = entry.label;
131
+ if (entry.primary === true) built.metadata = {
132
+ primary: true
133
+ };
134
+ return built;
135
+ });
136
+ }
137
+ function buildAddresses(values) {
138
+ return values.map((value)=>{
139
+ const entry = value ?? {};
140
+ const built = {};
141
+ if (typeof entry.street === 'string') built.streetAddress = entry.street;
142
+ if (typeof entry.locality === 'string') built.city = entry.locality;
143
+ if (typeof entry.region === 'string') built.region = entry.region;
144
+ if (typeof entry.postalCode === 'string') built.postalCode = entry.postalCode;
145
+ if (typeof entry.country === 'string') built.country = entry.country;
146
+ if (typeof entry.label === 'string') built.type = entry.label;
147
+ if (entry.primary === true) built.metadata = {
148
+ primary: true
149
+ };
150
+ return built;
151
+ });
152
+ }
153
+ const LOCAL_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
154
+ function buildBirthday(value) {
155
+ const match = LOCAL_DATE.exec(value);
156
+ if (match == null) {
157
+ throw new GoogleContactsError(`a Google Contacts birthday must be a YYYY-MM-DD date, got ${JSON.stringify(value)}`, {
158
+ kind: 'rejected'
159
+ });
160
+ }
161
+ return {
162
+ date: {
163
+ year: Number(match[1] ?? ''),
164
+ month: Number(match[2] ?? ''),
165
+ day: Number(match[3] ?? '')
166
+ }
167
+ };
168
+ }
63
169
  export function buildPersonFromParams(params) {
64
170
  const person = {};
65
- if (params.displayName != null) {
171
+ if (params.givenName != null || params.familyName != null) {
66
172
  person.names = [
67
173
  {
68
- givenName: params.displayName
174
+ ...params.givenName != null ? {
175
+ givenName: params.givenName
176
+ } : {},
177
+ ...params.familyName != null ? {
178
+ familyName: params.familyName
179
+ } : {}
69
180
  }
70
181
  ];
71
- }
72
- if (params.email != null) {
73
- person.emailAddresses = [
182
+ } else if (params.displayName != null) {
183
+ // With no structured given/family name, take the caller's display name
184
+ // as an unstructured name rather than guessing a given/family split.
185
+ person.names = [
74
186
  {
75
- value: params.email
187
+ unstructuredName: params.displayName
76
188
  }
77
189
  ];
78
190
  }
79
- if (params.phoneNumber != null) {
80
- person.phoneNumbers = [
191
+ if (params.nickname != null) {
192
+ person.nicknames = [
81
193
  {
82
- value: params.phoneNumber
194
+ value: params.nickname
83
195
  }
84
196
  ];
85
197
  }
198
+ if (Array.isArray(params.emails)) {
199
+ person.emailAddresses = buildLabeledValues(params.emails);
200
+ }
201
+ if (Array.isArray(params.phones)) {
202
+ person.phoneNumbers = buildLabeledValues(params.phones);
203
+ }
204
+ if (Array.isArray(params.addresses)) {
205
+ person.addresses = buildAddresses(params.addresses);
206
+ }
86
207
  if (params.organization != null || params.jobTitle != null) {
87
208
  person.organizations = [
88
209
  {
@@ -95,6 +216,18 @@ export function buildPersonFromParams(params) {
95
216
  }
96
217
  ];
97
218
  }
219
+ if (params.birthday != null) {
220
+ person.birthdays = [
221
+ buildBirthday(params.birthday)
222
+ ];
223
+ }
224
+ if (params.note != null) {
225
+ person.biographies = [
226
+ {
227
+ value: params.note
228
+ }
229
+ ];
230
+ }
98
231
  return person;
99
232
  }
100
233
  // Raw people.get, shared by getPerson/getPersonWithETag (fetchEntity/
@@ -143,9 +276,13 @@ export async function updateContact(options) {
143
276
  person.etag = options.etag;
144
277
  const updatePersonFields = [];
145
278
  if (person.names != null) updatePersonFields.push('names');
279
+ if (person.nicknames != null) updatePersonFields.push('nicknames');
146
280
  if (person.emailAddresses != null) updatePersonFields.push('emailAddresses');
147
281
  if (person.phoneNumbers != null) updatePersonFields.push('phoneNumbers');
282
+ if (person.addresses != null) updatePersonFields.push('addresses');
148
283
  if (person.organizations != null) updatePersonFields.push('organizations');
284
+ if (person.birthdays != null) updatePersonFields.push('birthdays');
285
+ if (person.biographies != null) updatePersonFields.push('biographies');
149
286
  const url = `${CONTACTS_BASE_URL}/${options.sourceID}:updateContact?updatePersonFields=${updatePersonFields.join(',')}&personFields=${PERSON_FIELDS}`;
150
287
  const client = createGoogleAPIClient({
151
288
  api: 'Google People API',
package/lib/connector.js CHANGED
@@ -1,7 +1,30 @@
1
+ import { contactsModelDefinitions } from '@kubun/models-contacts';
1
2
  import { createGoogleContactsActionHandler } from './actions.js';
2
3
  import { canonicalizeContactIntent } from './intent.js';
3
4
  import { googleContactsClusters } from './model.js';
4
5
  import { createGoogleContactsProvider } from './provider.js';
6
+ const contactKindProperties = contactsModelDefinitions.find((definition)=>definition.name === 'ContactKind')?.schema?.properties ?? {};
7
+ function contactKindProperty(name) {
8
+ const property = contactKindProperties[name];
9
+ if (property == null) throw new Error(`ContactKind declares no "${name}" field`);
10
+ return property;
11
+ }
12
+ // Every ContactKind field a write can set, in the ContactKind shape.
13
+ // `photoURL` is read-only (the People API has no way to set a photo via
14
+ // updateContact), so it is left out.
15
+ const contactInputProperties = {
16
+ displayName: contactKindProperty('displayName'),
17
+ givenName: contactKindProperty('givenName'),
18
+ familyName: contactKindProperty('familyName'),
19
+ nickname: contactKindProperty('nickname'),
20
+ emails: contactKindProperty('emails'),
21
+ phones: contactKindProperty('phones'),
22
+ addresses: contactKindProperty('addresses'),
23
+ organization: contactKindProperty('organization'),
24
+ jobTitle: contactKindProperty('jobTitle'),
25
+ birthday: contactKindProperty('birthday'),
26
+ note: contactKindProperty('note')
27
+ };
5
28
  export function createGoogleContactsConnector(options) {
6
29
  return {
7
30
  name: 'google-contacts',
@@ -27,23 +50,7 @@ export function createGoogleContactsConnector(options) {
27
50
  requiresCredential: true,
28
51
  input: {
29
52
  type: 'object',
30
- properties: {
31
- displayName: {
32
- type: 'string'
33
- },
34
- email: {
35
- type: 'string'
36
- },
37
- phoneNumber: {
38
- type: 'string'
39
- },
40
- organization: {
41
- type: 'string'
42
- },
43
- jobTitle: {
44
- type: 'string'
45
- }
46
- },
53
+ properties: contactInputProperties,
47
54
  required: [
48
55
  'displayName'
49
56
  ]
@@ -67,21 +74,7 @@ export function createGoogleContactsConnector(options) {
67
74
  sourceID: {
68
75
  type: 'string'
69
76
  },
70
- displayName: {
71
- type: 'string'
72
- },
73
- email: {
74
- type: 'string'
75
- },
76
- phoneNumber: {
77
- type: 'string'
78
- },
79
- organization: {
80
- type: 'string'
81
- },
82
- jobTitle: {
83
- type: 'string'
84
- }
77
+ ...contactInputProperties
85
78
  },
86
79
  required: [
87
80
  'sourceID'
package/lib/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { createGoogleContactsActionHandler, type GoogleContactsActionHandlerConfig, } from './actions.js';
2
- export { buildListURL, buildPersonFromParams, type CreateContactParams, createContact, type GoogleConnectionsResponse, type GooglePersonResource, mapPersonToEntity, type UpdateContactParams, updateContact, } from './api.js';
2
+ export { type BuildListURLParams, buildListURL, buildPersonFromParams, type CreateContactParams, createContact, type GoogleConnectionsResponse, type GooglePersonResource, mapPersonToEntity, type UpdateContactParams, updateContact, } from './api.js';
3
3
  export { type CreateGoogleContactsConnectorOptions, createGoogleContactsConnector, } from './connector.js';
4
4
  export { ContactEtagRequired, GoogleContactsError, type GoogleContactsErrorCode, type GoogleContactsErrorOptions, } from './errors.js';
5
5
  export { canonicalizeContactIntent } from './intent.js';
package/lib/model.d.ts CHANGED
@@ -1,39 +1,21 @@
1
1
  export declare const googleContactDefinition: {
2
2
  readonly name: "GoogleContact";
3
3
  readonly behavior: "default";
4
- readonly interfaces: [string, string];
4
+ readonly interfaces: [string, string, string];
5
5
  readonly schema: {
6
6
  readonly type: "object";
7
7
  readonly properties: {
8
- readonly displayName: {
9
- readonly type: "string";
10
- };
11
8
  readonly sourceService: {
12
9
  readonly $ref: "#/definitions/SourceService";
13
10
  };
14
11
  readonly sourceID: {
15
12
  readonly $ref: "#/definitions/SourceID";
16
13
  };
17
- readonly lastSyncedAt: {
18
- readonly $ref: "#/definitions/DateTime";
19
- };
20
14
  readonly sourceURL: {
21
15
  readonly $ref: "#/definitions/SourceURL";
22
16
  };
23
- readonly email: {
24
- readonly type: "string";
25
- };
26
- readonly phoneNumber: {
27
- readonly type: "string";
28
- };
29
- readonly organization: {
30
- readonly type: "string";
31
- };
32
- readonly jobTitle: {
33
- readonly type: "string";
34
- };
35
- readonly photoURL: {
36
- readonly $ref: "#/definitions/PhotoURL";
17
+ readonly lastSyncedAt: {
18
+ readonly $ref: "#/definitions/DateTime";
37
19
  };
38
20
  readonly etag: {
39
21
  readonly type: "string";
@@ -42,11 +24,6 @@ export declare const googleContactDefinition: {
42
24
  readonly required: ["sourceService", "sourceID", "lastSyncedAt"];
43
25
  readonly additionalProperties: false;
44
26
  readonly definitions: {
45
- readonly PhotoURL: {
46
- readonly type: "string";
47
- readonly format: "uri";
48
- readonly title: "URL";
49
- };
50
27
  readonly SourceService: {
51
28
  readonly type: "string";
52
29
  readonly title: "SourceService";
@@ -69,7 +46,7 @@ export declare const googleContactDefinition: {
69
46
  };
70
47
  readonly fieldsMeta: {};
71
48
  readonly providerMergePolicy: {
72
- readonly group: ["displayName", "sourceService", "sourceID", "sourceURL", "email", "phoneNumber", "organization", "jobTitle", "photoURL", "etag"];
49
+ readonly group: [...string[], "sourceService", "sourceID", "sourceURL", "etag"];
73
50
  };
74
51
  };
75
52
  export declare const googleContactsClusters: import("@kubun/protocol").ClustersRecord;
package/lib/model.js CHANGED
@@ -1,46 +1,35 @@
1
1
  import { connectorClusters, connectorModelIDs, externalEntityDefinition } from '@kubun/connector';
2
- import { modelIDs, modelsClusters } from '@kubun/models';
2
+ import { contactsClusters, contactsModelDefinitions, contactsModelIDs } from '@kubun/models-contacts';
3
+ import { coreClusters, coreModelIDs } from '@kubun/models-core';
3
4
  import { GraphBuilder } from '@kubun/protocol';
4
- import { urlSchema } from '@kubun/scalars';
5
+ // The shared contact fields (displayName, givenName/familyName, nickname,
6
+ // emails, phones, addresses, organization, jobTitle, birthday, photoURL,
7
+ // note) come from ContactKind, and local tags from Taggable; only what is
8
+ // Google's own is declared here.
9
+ const contactKindProviderFields = contactsModelDefinitions.find((definition)=>definition.name === 'ContactKind')?.providerMergePolicy?.group;
10
+ if (contactKindProviderFields == null) throw new Error('ContactKind provider fields are missing');
5
11
  export const googleContactDefinition = {
6
12
  name: 'GoogleContact',
7
13
  behavior: 'default',
8
14
  interfaces: [
9
15
  connectorModelIDs.ExternalEntity,
10
- modelIDs.ContactKind
16
+ contactsModelIDs.ContactKind,
17
+ coreModelIDs.Taggable
11
18
  ],
12
19
  schema: {
13
20
  type: 'object',
14
21
  properties: {
15
- displayName: {
16
- type: 'string'
17
- },
18
22
  sourceService: {
19
23
  $ref: '#/definitions/SourceService'
20
24
  },
21
25
  sourceID: {
22
26
  $ref: '#/definitions/SourceID'
23
27
  },
24
- lastSyncedAt: {
25
- $ref: '#/definitions/DateTime'
26
- },
27
28
  sourceURL: {
28
29
  $ref: '#/definitions/SourceURL'
29
30
  },
30
- email: {
31
- type: 'string'
32
- },
33
- phoneNumber: {
34
- type: 'string'
35
- },
36
- organization: {
37
- type: 'string'
38
- },
39
- jobTitle: {
40
- type: 'string'
41
- },
42
- photoURL: {
43
- $ref: '#/definitions/PhotoURL'
31
+ lastSyncedAt: {
32
+ $ref: '#/definitions/DateTime'
44
33
  },
45
34
  etag: {
46
35
  type: 'string'
@@ -53,24 +42,19 @@ export const googleContactDefinition = {
53
42
  ],
54
43
  additionalProperties: false,
55
44
  definitions: {
56
- ...externalEntityDefinition.schema.definitions,
57
- PhotoURL: urlSchema
45
+ ...externalEntityDefinition.schema.definitions
58
46
  }
59
47
  },
60
48
  fieldsMeta: {},
61
- // lastSyncedAt is a device-local sync-bookkeeping annotation, not fetched
62
- // provider content, so it stays outside the group.
49
+ // lastSyncedAt is a device-local sync-bookkeeping annotation and tags a
50
+ // local user annotation, neither fetched provider content, so both stay
51
+ // outside the group.
63
52
  providerMergePolicy: {
64
53
  group: [
65
- 'displayName',
54
+ ...contactKindProviderFields,
66
55
  'sourceService',
67
56
  'sourceID',
68
57
  'sourceURL',
69
- 'email',
70
- 'phoneNumber',
71
- 'organization',
72
- 'jobTitle',
73
- 'photoURL',
74
58
  // The provider revision of this state: an update is written against it,
75
59
  // so it must travel with the fields it versions.
76
60
  'etag'
@@ -80,7 +64,8 @@ export const googleContactDefinition = {
80
64
  const builder = new GraphBuilder({
81
65
  externalClusters: {
82
66
  ...connectorClusters,
83
- ...modelsClusters
67
+ ...coreClusters,
68
+ ...contactsClusters
84
69
  }
85
70
  });
86
71
  builder.add(googleContactDefinition);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/connector-google-contacts",
3
- "version": "0.17.2",
3
+ "version": "0.18.0",
4
4
  "keywords": [],
5
5
  "license": "see LICENSE.md",
6
6
  "sideEffects": false,
@@ -15,13 +15,13 @@
15
15
  "LICENSE.md"
16
16
  ],
17
17
  "dependencies": {
18
- "@kubun/connector": "^0.17.3",
19
- "@kubun/credential-types": "^0.17.2",
20
- "@kubun/hlc": "^0.17.0",
21
- "@kubun/models": "^0.17.0",
22
- "@kubun/protocol": "^0.17.2",
23
- "@kubun/provider-google": "^0.17.1",
24
- "@kubun/scalars": "^0.17.0"
18
+ "@kubun/connector": "^0.18.0",
19
+ "@kubun/credential-types": "^0.18.0",
20
+ "@kubun/hlc": "^0.18.0",
21
+ "@kubun/models-contacts": "^0.18.0",
22
+ "@kubun/models-core": "^0.18.0",
23
+ "@kubun/protocol": "^0.18.0",
24
+ "@kubun/provider-google": "^0.18.0"
25
25
  },
26
26
  "publishConfig": {
27
27
  "access": "public"