-
Notifications
You must be signed in to change notification settings - Fork 0
/
notes.ts
388 lines (364 loc) · 12.6 KB
/
notes.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
import type { AxiosInstance } from 'axios'
import type {
DateTime,
Replace,
RequireAtLeastOne,
RequireOnlyOne,
} from './types.ts'
import { defaultTransformers } from './axios_default_transformers.ts'
import type { PagedRequest } from './paged_request.ts'
import type { PagedResponse } from './paged_response.ts'
import { notesUrl } from './urls.ts'
import { createSearchIteratorFn } from './create_search_iterator_fn.ts'
export enum NoteType {
PLAIN_TEXT = 0,
HTML = 2,
/**
* Can only be created by the Notetaker AI tool from Affinity.
*/
AI_SUMMARY = 3,
/**
* @deprecated
*/
EMAIL = 1,
}
/**
* A note object contains content, which is a string containing the note body. In addition, a note can be associated with multiple people, organizations, or opportunities. Each person, organization, or opportunity will display linked notes on their profiles.
*/
export type NoteRaw = {
/**
* The unique identifier of the note object.
*/
id: number
/**
* The unique identifier of the person object who created the note.
*/
creator_id: number
/**
* An array containing the unique identifiers for all the persons relevant to the note. This is the union of {@link Note.associated_person_ids} and {@link Note.interaction_person_ids}.
*/
person_ids: number[]
/**
* An array containing the unique identifiers for the persons directly associated with the note.
*/
associated_person_ids: number[]
/**
* An array containing the unique identifiers for the persons on the interaction the note is attached to, if any. This will be an empty array if there is no such interaction or there aren’t any attendees.
*/
interaction_person_ids: number[]
/**
* The unique identifier of the interaction the note is attached to, if any.
*/
interaction_id: number | null
/**
* The type of the interaction the note is attached to, if any.
*/
interaction_type: number | null
/**
* True if the note is attached to a meeting or a call.
*/
is_meeting: boolean
/**
* An array containing the unique identifiers for the persons who are @ mentioned in the note. If there are no mentioned persons, this will be an empty array.
*/
mentioned_person_ids: number[]
/**
* An array of unique identifiers of organization objects that are associated with the note.
*/
organization_ids: number[]
/**
* An array of unique identifiers of opportunity objects that are associated with the note.
*/
opportunity_ids: number[]
/**
* The unique identifier of the note that this note is a reply to. If this field is null, the note is not a reply. Note replies will never have values for opportunity_ids, person_ids, and organization_ids. Only the parent note is associated with an entity. You can fetch the parent note resource to identify the root entity.
*/
parent_id: number | null
/**
* The string containing the content of the note.
*/
content: string
/**
* The type of the note.
*/
type: NoteType
/**
* The string representing the time when the note was created.
*/
created_at: DateTime
/**
* The string representing the last time the note was updated.
*/
updated_at: DateTime | null
}
export type CreateNoteRequest =
& {
/**
* The string containing the content of the new note. See [formatting options](https://api-docs.affinity.co/#formatting-content-as-html) for HTML support.
*/
content: string
/**
* The type of the new note. Defaults to 0. The types 0 and 2 represent plain text and HTML notes, respectively. If submitting as HTML, see the [formatting options](https://api-docs.affinity.co/#formatting-content-as-html).
*/
type?: NoteType
/**
* The ID of a Person resource who should be recorded as the author of the note. Must be a person who can access Affinity. If not provided the creator defaults to the owner of the API key.
*/
creator_id?: number
/**
* The creation time to be recorded for the note. If not provided, defaults to the current time. Does not support times in the future.
*/
created_at?: Date
}
& (
| {
/**
* The unique identifier of the note to which the newly created note should reply.
*/
parent_id: number
}
| RequireAtLeastOne<{
/**
* An array of unique identifiers of person objects that are associated with the new note.
*/
person_ids: number[]
/**
* An array of unique identifiers of organization objects that are associated with the new note.
*/
organization_ids: number[]
/**
* An array of unique identifiers of opportunity objects that are associated with the new note.
*/
opportunity_ids: number[]
}>
)
export type CreateNoteRequestRaw = Replace<CreateNoteRequest, {
created_at?: DateTime
}>
export type Note = Replace<NoteRaw, {
created_at: Date
updated_at: Date | null
}>
export type AllNotesRequest =
| PagedRequest
| (
& PagedRequest
& RequireOnlyOne<{
/**
* A unique identifier that represents a Person that was tagged in the retrieved notes.
*/
person_id: number
/**
* A unique identifier that represents an Organization that was tagged in the retrieved notes.
*/
organization_id: number
/**
* A unique identifier that represents an Opportunity that was tagged in the retrieved notes.
*/
opportunity_id: number
/**
* A unique identifier that represents an Affinity user whose created notes should be retrieved.
*/
creator_id: number
}>
)
export type PagedNotesResponseRaw =
& {
notes: NoteRaw[]
}
& PagedResponse
export type PagedNotesResponse = Replace<
{ notes: Note[] },
PagedNotesResponseRaw
>
export type NoteReference = {
/** The unique ID of the note */
note_id: number
}
export type GetNoteRequest = NoteReference
export type SingleNoteResponseRaw = NoteRaw
export type SingleNoteResponse = Note
export type UpdateNoteRequest =
& NoteReference
& Pick<CreateNoteRequest, 'content'>
/**
* Entity files are files uploaded to a relevant entity.
* Possible files, for example, would be a pitch deck for an opportunity or a physical mail correspondence for a person.
*/
export class Notes {
/** @hidden */
constructor(private readonly axios: AxiosInstance) {
}
private static transformNote(note: NoteRaw): Note {
return {
...note,
created_at: new Date(note.created_at),
updated_at: note.updated_at ? new Date(note.updated_at) : null,
}
}
/**
* Fetches a note with a specified `note_id`.
*
* @returns The Note object corresponding to the `note_id`.
*
* @example
* ```typescript
* const note = await affinity.notes.get({
* note_id: 12345
* })
* console.log(note)
* ```
*/
async get(
params: GetNoteRequest,
): Promise<SingleNoteResponse> {
const { note_id, ...rest } = params
const response = await this.axios.get<SingleNoteResponse>(
notesUrl(note_id),
{
params: rest,
transformResponse: [
...defaultTransformers(),
Notes.transformNote,
],
},
)
return response.data
}
/**
* Returns all notes attached to a person, organization or opportunity.
*/
async all(params?: AllNotesRequest): Promise<PagedNotesResponse> {
const response = await this.axios.get<PagedNotesResponse>(
notesUrl(),
{
params,
transformResponse: [
...defaultTransformers(),
(json: PagedNotesResponseRaw) => {
return {
...json,
notes: json.notes.map(Notes.transformNote),
}
},
],
},
)
return response.data
}
/**
* Returns an async iterator that yields all notes matching the given request
* Each yielded array contains up to the number specified in {@link PagedRequest.page_size} of notes.
* Use this method if you want to process the notes in a streaming fashion.
*
* *Please note:* the yielded notes array may be empty on the last page.
*
* @example
* ```typescript
* let page = 0
* for await (const entries of affinity.notes.pagedIterator({
* person_id: 123,
* page_size: 10
* })) {
* console.log(`Page ${++page} of entries:`, entries)
* }
* ```
*/
pagedIterator = createSearchIteratorFn(
this.all.bind(this),
'notes',
)
/**
* Creates a new note with the supplied parameters.
*
* Set the `type` parameter to 2 to create an HTML note.
* See [here](https://support.affinity.co/hc/en-us/articles/360016292631-Rich-text-formatting-for-notes-within-Affinity) for more information on the sorts of rich text formatting we support in notes.
* Please note that `<a>` tags aren't currently clickable inside the Affinity web app - though full links are.
*
* It is possible to create a **reply** to an existing note by setting `parent_id`.
* The parent note should not have a `parent_id` itself.
* It is possible for a single parent note to have multiple reply notes - They just get displayed in order of creation. `opportunity_ids`, `person_ids`, and `organization_ids` will be ignored when a `parent_id` is provided.
*
* @example
* ```typescript
* const newNote = await affinity.notes.create({
* person_ids: [
* 38706,
* 624289
* ],
* organization_ids: [
* 120611418
* ],
* opportunity_ids: [
* 167
* ],
* content: "Had a lunch meeting with Jane and John today. They want to invest in Acme Corp."
* })
* console.log(newNote)
* ```
*/
async create(
data: CreateNoteRequest,
): Promise<SingleNoteResponse> {
const { created_at, ...rest } = data
const request: CreateNoteRequestRaw = created_at
? {
...rest,
created_at: created_at.toISOString() as DateTime,
}
: rest
const response = await this.axios.post<SingleNoteResponseRaw>(
notesUrl(),
request,
)
return Notes.transformNote(response.data)
}
/**
* Updates an existing person with `note_id` with the supplied parameters.
*
* *Caveats:*
* - You cannot update the content of a note that has mentions.
* - You also cannot update the content of a note associated with an email.
* - You cannot update the type of a note.
*
* *Note from 2024-08-13*: Updating an HTML note with changed HTML content seems to result in the note being displayed as plain text in the Affinity web app. Bug is reported.
*
* @example
* ```typescript
* const updatedNote = await affinity.notes.update({
* note_id: 12345,
* content: "Dinner wasn't great, but the conversation was excellent.",
* })
* console.log(updatedNote)
* ```
*/
async update(
data: UpdateNoteRequest,
): Promise<SingleNoteResponse> {
const { note_id, ...rest } = data
const response = await this.axios.put<SingleNoteResponseRaw>(
notesUrl(note_id),
rest,
)
return Notes.transformNote(response.data)
}
/**
* Deletes a note with a specified `note_id`.
* @returns true if the deletion was successful
*
* @example
* ```typescript
* const success = await affinity.notes.delete({
* note_id: 12345
* })
* console.log(success ? 'Note deleted': 'Note not deleted')
* ```
*/
async delete(request: NoteReference): Promise<boolean> {
const { note_id } = request
const response = await this.axios.delete<{ success: boolean }>(
notesUrl(note_id),
)
return response.data.success === true
}
}