Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: persons endpoint #18

Merged
merged 14 commits into from
Jul 26, 2024
3 changes: 3 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
reviews:
auto_review:
drafts: true
1 change: 1 addition & 0 deletions .yamlfmt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
gitignore_excludes: true
eof_newline: true
exclude:
- .yamllint
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ examples, etc.
- ✅ [Fields](src/v1/fields.ts)
- ✅ [Field Values](src/v1/field_values.ts)
- ✅ [Field Value Changes](src/v1/field_value_changes.ts)
- [Persons](src/v1/persons.ts)
- [Persons](src/v1/persons.ts)
- ✅ [Organizations](src/v1/organizations.ts)
- ❌ [Opportunities](src/v1/opportunities.ts)
- ❌ Interactions
Expand Down
21 changes: 11 additions & 10 deletions src/v1/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AxiosInstance } from 'axios'
import { enumFromValue } from './enum_from_value.ts'
import { defaultTransformers } from './axios_default_transformers.ts'
import { whoAmIUrl } from './urls.ts'
import type { DateTime, Replace } from './types.ts'

/**
* TODO(@joscha): Enum is most likely incomplete
Expand Down Expand Up @@ -36,17 +37,13 @@ export type User = {
email: string
}

type WhoAmIResponseRaw = {
tenant: Tenant
user: User
grant: {
type: string
scope: string
createdAt: string
}
type GrantRaw = {
type: string
scope: string
createdAt: DateTime
}

export type WhoAmIResponse = {
type WhoAmIResponseRaw = {
/**
* Information about the Affinity instance the user belongs to.
*/
Expand All @@ -58,12 +55,16 @@ export type WhoAmIResponse = {
/**
* Data about the type of authentication and metadata about the API key.
*/
grant: GrantRaw
}

export type WhoAmIResponse = Replace<WhoAmIResponseRaw, {
grant: {
type: GrantType
scope: Scope
createdAt: Date
}
}
}>

/**
* @module
Expand Down
39 changes: 39 additions & 0 deletions src/v1/create_search_iterator_fn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { PagedResponse } from './paged_response.ts'
import type { PagedRequest } from './paged_request.ts'

export const createSearchIteratorFn = <
FN extends (r: PAGED_REQUEST) => Promise<PAGED_RESPONSE>,
PAGED_REQUEST extends PagedRequest,
PAGED_RESPONSE extends
& PagedResponse
& Record<PAYLOAD_KEY, SINGLE_RESPONSE[]>,
PAYLOAD_KEY extends string =
& keyof Omit<PAGED_RESPONSE, keyof PagedResponse>
& string,
SINGLE_RESPONSE = object,
>(searchFn: FN, key: PAYLOAD_KEY) => {
async function* searchIterator(
params: Omit<PAGED_REQUEST, 'page_token'>,
): AsyncGenerator<PAGED_RESPONSE[]> {
let page_token: string | undefined = undefined
while (true) {
const response: PAGED_RESPONSE = await searchFn(
// TODO(@joscha): remove cast
(page_token
? { ...params, page_token }
: params) as PAGED_REQUEST,
)

// TODO(@joscha): remove cast
yield response[key] as unknown as PAGED_RESPONSE[]

if (response.next_page_token === null) {
// no more pages to fetch
return
} else {
page_token = response.next_page_token
}
}
}
Comment on lines +15 to +37
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure proper error handling for the asynchronous generator.

The function does not handle potential errors from the searchFn call. Consider adding try-catch blocks to handle errors gracefully.

+    try {
        const response: PAGED_RESPONSE = await searchFn(
            // TODO(@joscha): remove cast
            (page_token
                ? { ...params, page_token }
                : params) as PAGED_REQUEST,
        )

        // TODO(@joscha): remove cast
        yield response[key] as unknown as PAGED_RESPONSE[]

        if (response.next_page_token === null) {
            // no more pages to fetch
            return
        } else {
            page_token = response.next_page_token
        }
+    } catch (error) {
+        console.error("Error fetching page:", error);
+        throw error;
+    }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function* searchIterator(
params: Omit<PAGED_REQUEST, 'page_token'>,
): AsyncGenerator<PAGED_RESPONSE[]> {
let page_token: string | undefined = undefined
while (true) {
const response: PAGED_RESPONSE = await searchFn(
// TODO(@joscha): remove cast
(page_token
? { ...params, page_token }
: params) as PAGED_REQUEST,
)
// TODO(@joscha): remove cast
yield response[key] as unknown as PAGED_RESPONSE[]
if (response.next_page_token === null) {
// no more pages to fetch
return
} else {
page_token = response.next_page_token
}
}
}
async function* searchIterator(
params: Omit<PAGED_REQUEST, 'page_token'>,
): AsyncGenerator<PAGED_RESPONSE[]> {
let page_token: string | undefined = undefined
while (true) {
+ try {
const response: PAGED_RESPONSE = await searchFn(
// TODO(@joscha): remove cast
(page_token
? { ...params, page_token }
: params) as PAGED_REQUEST,
)
// TODO(@joscha): remove cast
yield response[key] as unknown as PAGED_RESPONSE[]
if (response.next_page_token === null) {
// no more pages to fetch
return
} else {
page_token = response.next_page_token
}
+ } catch (error) {
+ console.error("Error fetching page:", error);
+ throw error;
+ }
}
}

return searchIterator
}
Comment on lines +4 to +39
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider removing TODO comments or addressing them.

There are TODO comments indicating casts that need to be removed. Ensure these are addressed before finalizing the code.

-    // TODO(@joscha): remove cast
-    (page_token
-        ? { ...params, page_token }
-        : params) as PAGED_REQUEST,
+    const requestParams: PAGED_REQUEST = page_token
+        ? { ...params, page_token }
+        : params;
+    const response: PAGED_RESPONSE = await searchFn(requestParams);
-    // TODO(@joscha): remove cast
-    yield response[key] as unknown as PAGED_RESPONSE[]
+    const responsePayload: PAGED_RESPONSE[] = response[key];
+    yield responsePayload;

27 changes: 7 additions & 20 deletions src/v1/field_value_changes.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
import type { AxiosInstance } from 'axios'

import type { Person } from './list_entries.ts'
import type { DateTime } from './types.ts'
import { fieldValueChangesUrl } from './urls.ts'
import { defaultTransformers } from './axios_default_transformers.ts'
import type { Field } from './lists.ts'
import type { Value, ValueRaw } from './field_values.ts'

/**
* Via https://stackoverflow.com/questions/40510611
*/
export type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
& Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?:
& Required<Pick<T, K>>
& Partial<Record<Exclude<Keys, K>, never>>
}[Keys]
import type { Person } from './list_entries.ts'
import type { Field } from './lists.ts'
import type { DateTime, Replace, RequireOnlyOne } from './types.ts'
joscha marked this conversation as resolved.
Show resolved Hide resolved
import { fieldValueChangesUrl } from './urls.ts'

/**
* Enum for Action Type.
Expand Down Expand Up @@ -90,11 +79,9 @@ export type FieldValueChangeRaw = {

export type FieldValueChangeResponseRaw = FieldValueChangeRaw[]

export type FieldValueChange =
& Omit<FieldValueChangeRaw, 'changed_at'>
& {
changed_at: Date
}
export type FieldValueChange = Replace<FieldValueChangeRaw, {
changed_at: Date
}>

export type FieldValueChangeResponse = FieldValueChange[]

Expand Down
7 changes: 4 additions & 3 deletions src/v1/field_values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { fieldValuesUrl } from './urls.ts'
import { defaultTransformers } from './axios_default_transformers.ts'
import { FieldValueType } from './lists.ts'
import { Field } from './lists.ts'
import type { DateTime } from './types.ts'
import type { DateTime, Replace } from './types.ts'
import { FieldBase } from './fields.ts'
export type { DateTime } from './types.ts'

Expand Down Expand Up @@ -155,13 +155,14 @@ export type FieldValueRaw =

export type FieldValueResponseRaw = FieldValueRaw[]

export type FieldValue =
& Omit<FieldValueRaw, 'value' | 'updated_at' | 'created_at' | 'value_type'>
export type FieldValue = Replace<
FieldValueRaw,
& {
updated_at: Date | null
created_at: Date
}
& ValueTypeMixin<FieldValueValues>
>

export type FieldValueResponse = FieldValue[]

Expand Down
4 changes: 4 additions & 0 deletions src/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Fields } from './fields.ts'
import { FieldValues } from './field_values.ts'
import { FieldValueChanges } from './field_value_changes.ts'
import { Organizations } from './organizations.ts'
import { Persons } from './persons.ts'
export type * as ListEntries from './list_entries.ts'
export type * as Lists from './lists.ts'
export type * as Fields from './fields.ts'
Expand Down Expand Up @@ -51,6 +52,7 @@ export class Affinity {
this.fieldValues = new FieldValues(this.axios)
this.fieldValueChanges = new FieldValueChanges(this.axios)
this.organizations = new Organizations(this.axios)
this.persons = new Persons(this.axios)
}

public readonly auth: Auth
Expand All @@ -66,4 +68,6 @@ export class Affinity {
public readonly fieldValueChanges: FieldValueChanges

public readonly organizations: Organizations

public readonly persons: Persons
}
42 changes: 14 additions & 28 deletions src/v1/list_entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import type { AxiosInstance } from 'axios'
import type { EntityType, GetQuery } from './lists.ts'
import { listEntriesUrl } from './urls.ts'
import { defaultTransformers } from './axios_default_transformers.ts'
import type { DateTime } from './types.ts'
import type { DateTime, Replace } from './types.ts'
import { PersonType } from './persons.ts'
import { Organization } from './organizations.ts'
import { transformListEntryReference } from './transform_list_entry_reference.ts'

export type Person = {
id: number
Expand Down Expand Up @@ -52,7 +53,6 @@ export type ListEntryReferenceRaw = {
}

export type ListEntryResponseRaw =
& ListEntryReferenceRaw
& {
/**
* The type of the entity corresponding to the list entry.
Expand All @@ -63,25 +63,24 @@ export type ListEntryResponseRaw =
*/
entity: Entity
}
& ListEntryReferenceRaw

export type PagedListEntryResponseRaw = {
list_entries: ListEntryResponseRaw[]
/**
* The absence of a `next_page_token` indicates that all the records have been fetched, though its presence does not necessarily indicate that there are more resources to be fetched.
* The next page may be empty (but then `next_page_token` would be `null` to confirm that there are no more resources).
* The absence of a {@link PagedResponse.next_page_token} indicates that all the records have been fetched, though its presence does not necessarily indicate that there are more resources to be fetched.
* The next page may be empty (but then {@link PagedResponse.next_page_token} would be `null` to confirm that there are no more resources).
*/
next_page_token: string | null
}

export type ListEntryResponse = Omit<ListEntryResponseRaw, 'created_at'> & {
export type ListEntryResponse = Replace<ListEntryResponseRaw, {
created_at: Date
}
}>

export type PagedListEntryResponse =
& Omit<PagedListEntryResponseRaw, 'list_entries'>
& {
list_entries: ListEntryResponse[]
}
export type PagedListEntryResponse = Replace<PagedListEntryResponseRaw, {
list_entries: ListEntryResponse[]
}>

/**
* Paging parameters for retrieving list entries.
Expand Down Expand Up @@ -144,15 +143,6 @@ export class ListEntries {
constructor(private readonly axios: AxiosInstance) {
}

private static transformEntry = (
entry: ListEntryResponseRaw,
): ListEntryResponse => {
return {
...entry,
created_at: new Date(entry.created_at),
}
}

/**
* Fetches all list entries in the list with the supplied list id.
*
Expand Down Expand Up @@ -215,11 +205,11 @@ export class ListEntries {
return {
...json,
list_entries: json.list_entries.map(
ListEntries.transformEntry,
transformListEntryReference,
),
}
} else {
return json.map(ListEntries.transformEntry)
return json.map(transformListEntryReference)
}
},
],
Expand Down Expand Up @@ -248,9 +238,7 @@ export class ListEntries {
{
transformResponse: [
...defaultTransformers(),
(json: ListEntryResponseRaw) => {
return ListEntries.transformEntry(json)
},
transformListEntryReference,
],
},
)
Expand Down Expand Up @@ -347,9 +335,7 @@ export class ListEntries {
{
transformResponse: [
...defaultTransformers(),
(json: ListEntryResponseRaw) => {
return ListEntries.transformEntry(json)
},
transformListEntryReference,
],
},
)
Expand Down
Loading