chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:34:48 +08:00
commit 77bb5bf71f
3762 changed files with 353249 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
import { listObjects, getObject, listAttributes } from './objects'
import { listRecords, getRecord, createRecord, updateRecord } from './record'
import * as bp from '.botpress'
export default {
listRecords,
getRecord,
createRecord,
updateRecord,
listObjects,
getObject,
listAttributes,
} as const satisfies bp.IntegrationProps['actions']
+53
View File
@@ -0,0 +1,53 @@
//
import { RuntimeError } from '@botpress/client'
import { AttioApiClient } from '../attio-api'
import * as bp from '.botpress'
export const listObjects: bp.IntegrationProps['actions']['listObjects'] = async (props) => {
const { ctx } = props
const accessToken = ctx.configuration.accessToken
const attioApiClient = new AttioApiClient(accessToken)
try {
const data = await attioApiClient.listObjects()
return { data: data.data }
} catch (thrown) {
const error = thrown instanceof Error ? thrown : new Error(String(thrown))
throw new RuntimeError(error.message)
}
}
export const getObject: bp.IntegrationProps['actions']['getObject'] = async (props) => {
const { ctx, input } = props
const accessToken = ctx.configuration.accessToken
const attioApiClient = new AttioApiClient(accessToken)
const { object } = input
try {
const data = await attioApiClient.getObject(object)
return { data: data.data }
} catch (thrown) {
const error = thrown instanceof Error ? thrown : new Error(String(thrown))
throw new RuntimeError(error.message)
}
}
export const listAttributes: bp.IntegrationProps['actions']['listAttributes'] = async (props) => {
const { ctx, input } = props
const accessToken = ctx.configuration.accessToken
const attioApiClient = new AttioApiClient(accessToken)
const { object } = input
try {
const data = await attioApiClient.listAttributes(object)
return { data: data.data }
} catch (thrown) {
const error = thrown instanceof Error ? thrown : new Error(String(thrown))
throw new RuntimeError(error.message)
}
}
+185
View File
@@ -0,0 +1,185 @@
//
import { RuntimeError } from '@botpress/client'
import { AttioApiClient, Attribute } from '../attio-api'
import * as bp from '.botpress'
type AttioRecordIdentifier = {
workspace_id: string
object_id: string
record_id: string
}
type AttioRecord = {
id: AttioRecordIdentifier
created_at: string
web_url: string
values: Record<string, unknown>
}
function _buildAttributeMaps(attributes: Attribute[]) {
const idToSlug: Record<string, string> = {}
const slugToOptionLabelById: Record<string, Record<string, string>> = {}
for (const attr of attributes) {
// Handle new id structure (object with attribute_id) or fallback to old string id
const id = typeof attr.id === 'object' ? attr.id?.attribute_id : String(attr.id ?? '')
const slug = String(attr.slug ?? attr.api_slug ?? id)
if (id) idToSlug[id] = slug
if (Array.isArray(attr.options) && attr.options.length > 0) {
const map: Record<string, string> = {}
for (const opt of attr.options) {
const oid = String(opt.id ?? '')
if (!oid) continue
const label = String(opt.label ?? opt.name ?? opt.value ?? opt.title ?? opt.slug ?? oid)
map[oid] = label
}
slugToOptionLabelById[slug] = map
}
}
return { idToSlug, slugToOptionLabelById }
}
function _mapValueUsingOptions(value: unknown, optionMap: Record<string, string>) {
const mapOne = (v: unknown) => {
if (v && typeof v === 'object' && !Array.isArray(v)) {
const candidateId = (v as { id?: string }).id
if (candidateId && optionMap[candidateId]) return optionMap[candidateId]
return v
}
if (typeof v === 'string' && optionMap[v]) return optionMap[v]
return v
}
if (Array.isArray(value)) return value.map((v) => mapOne(v))
return mapOne(value)
}
function _humanizeRecordValues(
record: AttioRecord,
idToSlug: Record<string, string>,
slugToOptionLabelById: Record<string, Record<string, string>>
) {
const src: Record<string, unknown> = record?.values ?? {}
const dst: Record<string, unknown> = {}
for (const key of Object.keys(src)) {
const slug = idToSlug[key] ?? key
const optionMap = slugToOptionLabelById[slug]
const raw = src[key]
const value = optionMap ? _mapValueUsingOptions(raw, optionMap) : raw
dst[slug] = value
}
return { ...record, values: dst }
}
function _mapValues(values: { attribute: string; value: string }[]) {
return values.reduce((acc: Record<string, any>, curr) => {
acc[curr.attribute] = curr.value
return acc
}, {})
}
export const listRecords: bp.IntegrationProps['actions']['listRecords'] = async (props) => {
const { ctx, input } = props
const accessToken = ctx.configuration.accessToken
const attioApiClient = new AttioApiClient(accessToken)
const { object, filter, sorts, limit, offset } = input
const filterMap = filter ? _mapValues(filter) : undefined
try {
const data = await attioApiClient.listRecords(object, { filter: filterMap, sorts, limit, offset })
const attributes = await attioApiClient.listAttributes(object)
const { idToSlug, slugToOptionLabelById } = _buildAttributeMaps(attributes.data)
const humanized = data.data.map((rec) => _humanizeRecordValues(rec, idToSlug, slugToOptionLabelById))
return { data: humanized }
} catch (thrown) {
const error = thrown instanceof Error ? thrown : new Error(String(thrown))
throw new RuntimeError(error.message)
}
}
export const getRecord: bp.IntegrationProps['actions']['getRecord'] = async (props) => {
const { ctx, input } = props
const accessToken = ctx.configuration.accessToken
const attioApiClient = new AttioApiClient(accessToken)
const { object, id } = input
try {
const data = await attioApiClient.getRecord(object, id.record_id)
const attributes = await attioApiClient.listAttributes(object)
const { idToSlug, slugToOptionLabelById } = _buildAttributeMaps(attributes.data)
const humanized = _humanizeRecordValues(data.data, idToSlug, slugToOptionLabelById)
return { data: humanized }
} catch (thrown) {
const error = thrown instanceof Error ? thrown : new Error(String(thrown))
throw new RuntimeError(error.message)
}
}
export const createRecord: bp.IntegrationProps['actions']['createRecord'] = async (props) => {
const { ctx, input } = props
const accessToken = ctx.configuration.accessToken
const attioApiClient = new AttioApiClient(accessToken)
const { object, data } = input
const { values } = data
try {
const valuesMap = _mapValues(values)
const data = await attioApiClient.createRecord(object, { data: { values: valuesMap } })
return { data: data.data }
} catch (thrown) {
const error = thrown instanceof Error ? thrown : new Error(String(thrown))
throw new RuntimeError(error.message)
}
}
export const updateRecord: bp.IntegrationProps['actions']['updateRecord'] = async (props) => {
const { ctx, input } = props
const accessToken = ctx.configuration.accessToken
const attioApiClient = new AttioApiClient(accessToken)
const { object, id, data } = input
const { values } = data
try {
const valuesMap = _mapValues(values)
const data = await attioApiClient.updateRecord(object, id.record_id, { data: { values: valuesMap } })
return { data: data.data }
} catch (thrown) {
const error = thrown instanceof Error ? thrown : new Error(String(thrown))
throw new RuntimeError(error.message)
}
}
export const listAttributes: bp.IntegrationProps['actions']['listAttributes'] = async (props) => {
const { ctx, input } = props
const accessToken = ctx.configuration.accessToken
const attioApiClient = new AttioApiClient(accessToken)
const { object } = input
try {
const data = await attioApiClient.listAttributes(object)
return { data: data.data }
} catch (thrown) {
const error = thrown instanceof Error ? thrown : new Error(String(thrown))
throw new RuntimeError(error.message)
}
}