Skip to content

Commit

Permalink
feat: notes; other endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
joscha committed Aug 13, 2024
1 parent 5eeb07f commit 61c2267
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 8 deletions.
198 changes: 193 additions & 5 deletions src/v1/notes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { AxiosInstance } from 'axios'
import { DateTime, Replace, RequireOnlyOne } from './types.ts'
import type { AxiosInstance } from 'axios'
import type {
DateTime,
Replace,
RequireAtLeastOne,
RequireOnlyOne,
} from './types.ts'
import { defaultTransformers } from './axios_default_transformers.ts'
import { PagedRequest } from './paged_request.ts'
import { PagedResponse } from './paged_response.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'

Expand Down Expand Up @@ -89,7 +94,56 @@ export type NoteRaw = {
updated_at: DateTime | null
}

type Note = Replace<NoteRaw, {
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
}>
Expand Down Expand Up @@ -126,6 +180,19 @@ type PagedNotesResponseRaw =

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.
Expand All @@ -143,6 +210,36 @@ export class Notes {
}
}

/**
* 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.
*/
Expand Down Expand Up @@ -187,4 +284,95 @@ export class Notes {
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.
*
* @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
}
}
6 changes: 3 additions & 3 deletions src/v1/persons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export class Persons {
}

/**
* Fetches an person with a specified `person_id`.
* Fetches a person with a specified `person_id`.
*
* @returns The person object corresponding to the `person_id`.
*
Expand Down Expand Up @@ -357,7 +357,7 @@ export class Persons {
/**
* Updates an existing person with `person_id` with the supplied parameters.
*
* @param data - Object containing the data for updating an person
* @param data - Object containing the data for updating a person
* @returns The person resource that was just updated.
*
* @example
Expand All @@ -382,7 +382,7 @@ export class Persons {
}

/**
* Deletes an person with a specified `person_id`.
* Deletes a person with a specified `person_id`.
* @returns true if the deletion was successful
*
* @example
Expand Down
6 changes: 6 additions & 0 deletions src/v1/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ export type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
& Required<Pick<T, K>>
& Partial<Record<Exclude<Keys, K>, never>>
}[Keys]

export type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
& Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
}[Keys]

0 comments on commit 61c2267

Please sign in to comment.