Open3
ValibotでNotionのレスポンスをデシリアライズするユーティリティ
import * as v from "valibot"
const NumberSchema = v.pipe(
v.object({
number: v.nullable(v.number()),
}),
v.transform((v) => v.number),
)
const UrlSchema = v.pipe(
v.object({
url: v.nullable(v.string()),
}),
v.transform((v) => v.url),
)
const SelectSchema = v.pipe(
v.object({
select: v.nullable(
v.object({
name: v.string(),
}),
),
}),
v.transform((v) => v.select?.name),
)
const MultiSelectSchema = v.pipe(
v.object({
multi_select: v.array(
v.object({
name: v.string(),
}),
),
}),
v.transform((v) => v.multi_select.map((v) => v.name)),
)
const StatusSchema = v.pipe(
v.object({
status: v.nullable(
v.object({
name: v.string(),
}),
),
}),
v.transform((v) => v.status?.name),
)
const StartDateSchema = v.pipe(
v.object({
date: v.nullable(
v.object({
start: v.pipe(
v.string(),
v.transform((v) => new Date(v)),
),
}),
),
}),
v.transform((v) => v.date?.start),
)
const DateSchema = v.pipe(
v.object({
date: v.nullable(
v.object({
start: v.pipe(
v.string(),
v.transform((v) => new Date(v)),
),
end: v.nullable(
v.pipe(
v.string(),
v.transform((v) => new Date(v)),
),
),
}),
),
}),
v.transform((v) =>
v.date
? {
start: v.date.start,
end: v.date.end,
}
: null,
),
)
const EmailSchema = v.pipe(
v.object({
email: v.nullable(v.string()),
}),
v.transform((v) => v.email),
)
const PhoneNumberSchema = v.pipe(
v.object({
phone_number: v.nullable(v.string()),
}),
v.transform((v) => v.phone_number),
)
const CheckboxSchema = v.pipe(
v.object({
checkbox: v.boolean(),
}),
v.transform((v) => v.checkbox),
)
const FilesSchema = v.pipe(
v.object({
files: v.array(
v.object({
file: v.object({
url: v.string(),
}),
}),
),
}),
v.transform((v) => v.files.map((v) => v.file.url)),
)
const CreatedByNameSchema = v.pipe(
v.object({
created_by: v.object({
name: v.nullish(v.string(), null),
}),
}),
v.transform((v) => v.created_by.name),
)
const CreatedByIdSchema = v.pipe(
v.object({
created_by: v.object({
id: v.string(),
}),
}),
v.transform((v) => v.created_by.id),
)
const CreatedTimeSchema = v.pipe(
v.object({
created_time: v.pipe(
v.string(),
v.transform((v) => new Date(v)),
),
}),
v.transform((v) => v.created_time),
)
const LastEditedByNameSchema = v.pipe(
v.object({
last_edited_by: v.object({
name: v.nullish(v.string(), null),
}),
}),
v.transform((v) => v.last_edited_by.name),
)
const LastEditedByIdSchema = v.pipe(
v.object({
last_edited_by: v.object({
id: v.string(),
}),
}),
v.transform((v) => v.last_edited_by.id),
)
const LastEditedTimeSchema = v.pipe(
v.object({
last_edited_time: v.pipe(
v.string(),
v.transform((v) => new Date(v)),
),
}),
v.transform((v) => v.last_edited_time),
)
const FormulaSchema = v.pipe(
v.object({
formula: v.variant("type", [
v.object({
type: v.literal("string"),
string: v.string(),
}),
v.object({
type: v.literal("date"),
date: v.nullable(
v.object({
start: v.pipe(
v.string(),
v.transform((v) => new Date(v)),
),
end: v.nullable(
v.pipe(
v.string(),
v.transform((v) => new Date(v)),
),
),
}),
),
}),
v.object({
type: v.literal("number"),
number: v.number(),
}),
v.object({
type: v.literal("boolean"),
boolean: v.nullable(v.boolean(), false),
}),
]),
}),
v.transform((v) => {
switch (v.formula.type) {
case "string":
return v.formula.string
case "date":
return v.formula.date
case "number":
return v.formula.number
case "boolean":
return v.formula.boolean
}
}),
)
const UniqueIdSchema = v.pipe(
v.object({
unique_id: v.object({
prefix: v.nullish(v.string(), null),
number: v.nullish(v.number(), null),
}),
}),
v.transform((v) => v.unique_id),
)
const RichTextArraySchema = v.pipe(
v.array(
v.object({
plain_text: v.string(),
}),
),
v.transform((v) => v.map((v) => v.plain_text).join("")),
)
const TitleSchema = v.pipe(
v.object({
title: RichTextArraySchema,
}),
v.transform((v) => v.title),
)
const RichTextSchema = v.pipe(
v.object({
rich_text: RichTextArraySchema,
}),
v.transform((v) => v.rich_text),
)
const PeopleSchema = v.pipe(
v.object({
people: v.array(
v.object({
id: v.string(),
name: v.nullish(v.string(), null),
}),
),
}),
v.transform((v) => v.people),
)
const RelationSchema = v.pipe(
v.object({
relation: v.array(
v.pipe(
v.object({ id: v.string() }),
v.transform((v) => v.id),
),
),
}),
v.transform((v) => v.relation),
)
export function queryDatabaseResponseSchema<T extends v.GenericSchema>(
propertiesSchema: T,
) {
return v.pipe(
v.object({
results: v.array(
v.pipe(
v.object({
id: v.string(),
object: v.literal("page"),
properties: propertiesSchema,
}),
),
),
}),
v.transform((v) => v.results),
)
}
const PropertiesSchema = v.pipe(
v.object({
プロジェクト名: TitleSchema,
NotionタスクデータベースID: RichTextSchema,
DiscordチャンネルID: RichTextSchema,
}),
v.transform((v) => ({
name: v.プロジェクト名,
notionTaskDatabaseId: v.NotionタスクデータベースID,
discordChannelId: v.DiscordチャンネルID,
})),
)
const QueryDatabaseResponseSchema = v.pipe(
v.object({
results: v.array(
v.pipe(
v.object({
id: v.string(),
object: v.literal("page"),
properties: PropertiesSchema,
}),
v.transform((v) => ({
id: v.id,
...v.properties,
})),
),
),
}),
v.transform((v) => v.results),
)
const QueryDatabaseResponseSchema = queryDatabaseResponseSchema(
PropertiesSchema
)
const result = notion.db.query(...)
const parsed = v.parse(QueryDatabaseResponseSchema, result)