このコミットが含まれているのは:
2026-07-15 19:57:09 +09:00
コミット 6e5aa1e30f
16個のファイルの変更299行の追加345行の削除
+18 -77
ファイルの表示
@@ -7,18 +7,14 @@ const hasSkipReason = (row: PostImportRow): boolean =>
const hasValidationErrors = (row: PostImportRow): boolean =>
Object.keys (row.validationErrors ?? { }).length > 0
const rowChanged = (
previous: PostImportRow,
next: PostImportRow,
): boolean =>
previous.url !== next.url
|| JSON.stringify (previous.attributes) !== JSON.stringify (next.attributes)
|| JSON.stringify (previous.provenance) !== JSON.stringify (next.provenance)
|| JSON.stringify (previous.tagSources ?? { }) !== JSON.stringify (next.tagSources ?? { })
const buildResetSnapshot = (row: PostImportRow) => ({
url: row.url,
attributes: row.attributes })
attributes: { ...row.attributes },
provenance: { ...row.provenance },
tagSources: {
automatic: row.tagSources?.automatic ?? '',
manual: row.tagSources?.manual ?? '' },
metadataUrl: row.metadataUrl })
export const processableImportRows = (rows: PostImportRow[]): PostImportRow[] =>
@@ -43,23 +39,14 @@ export const reviewSummaryCounts = (rows: PostImportRow[]) => ({
submittable: rows.filter (row =>
creatableImportRows ([row]).length > 0
&& !(hasValidationErrors (row))).length,
invalid: rows.filter (row => hasValidationErrors (row)).length,
skipPlanned: rows.filter (row =>
hasSkipReason (row)
&& !(hasValidationErrors (row))
&& row.importStatus !== 'created').length,
created: rows.filter (row => row.importStatus === 'created').length,
failed: rows.filter (row => row.importStatus === 'failed').length })
processableImportRows ([row]).length > 0
&& hasSkipReason (row)).length })
export const resultSummaryCounts = (rows: PostImportRow[]) =>
rows.reduce (
(counts, row) => {
if (hasValidationErrors (row))
{
++counts.invalid
return counts
}
if (row.importStatus === 'created')
{
++counts.created
@@ -74,7 +61,7 @@ export const resultSummaryCounts = (rows: PostImportRow[]) =>
++counts.failed
return counts
},
{ created: 0, skipped: 0, failed: 0, invalid: 0 })
{ created: 0, skipped: 0, failed: 0 })
export const mergeValidatedImportRows = (
@@ -83,7 +70,11 @@ export const mergeValidatedImportRows = (
): PostImportRow[] => {
const validatedMap = new Map (validated.map (row => [row.sourceRow, row]))
return current.map (previous => {
if (previous.importStatus === 'created')
if (
previous.importStatus === 'created'
|| previous.importStatus === 'skipped'
|| previous.importStatus === 'failed'
)
return previous
const row = validatedMap.get (previous.sourceRow)
@@ -124,59 +115,6 @@ export const mergeValidatedImportRows = (
}
export const mergePreviewImportRows = (
current: PostImportRow[],
preview: PostImportRow[],
): PostImportRow[] => {
const currentMap = new Map (current.map (row => [row.sourceRow, row]))
return preview.map (row => {
const previous = currentMap.get (row.sourceRow)
if (previous == null)
return row
if (previous.importStatus === 'created')
return previous
const mergedAttributes = { ...row.attributes }
const mergedProvenance = { ...row.provenance }
for (const [field, origin] of Object.entries (previous.provenance))
{
if (origin !== 'manual' || field === 'url')
continue
if (field in previous.attributes)
mergedAttributes[field] = previous.attributes[field]
mergedProvenance[field] = 'manual'
}
const mergedTagSources =
previous.provenance.tags === 'manual'
? {
automatic: row.tagSources?.automatic ?? '',
manual: previous.tagSources?.manual ?? String (previous.attributes.tags ?? '') }
: row.tagSources
const nextRow = {
...row,
attributes: mergedAttributes,
provenance: mergedProvenance,
tagSources: mergedTagSources,
skipReason: row.skipReason,
existingPostId: row.existingPostId,
resetSnapshot: row.resetSnapshot,
createdPostId: previous.createdPostId,
importStatus: previous.importStatus,
importErrors: previous.importErrors }
if (rowChanged (previous, nextRow))
{
nextRow.importStatus = 'pending'
nextRow.importErrors = undefined
}
return nextRow
})
}
export const mergeImportResults = (
rows: PostImportRow[],
results: PostImportResultRow[],
@@ -193,13 +131,15 @@ export const mergeImportResults = (
return {
...row,
importStatus: 'created',
createdPostId: result.post?.id,
skipReason: undefined,
createdPostId: result.post.id,
existingPostId: undefined,
importErrors: result.errors }
case 'skipped':
return {
...row,
importStatus: 'skipped',
skipReason: 'existing',
createdPostId: undefined,
existingPostId: result.existingPostId,
importErrors: result.errors }
@@ -207,6 +147,7 @@ export const mergeImportResults = (
return {
...row,
importStatus: 'failed',
skipReason: undefined,
createdPostId: undefined,
existingPostId: undefined,
importErrors: result.errors }
+18 -1
ファイルの表示
@@ -150,10 +150,27 @@ const sanitiseResetSnapshot = (value: unknown): PostImportResetSnapshot | null =
if (!(Object.values (value.attributes).every (entry =>
typeof entry === 'string' || typeof entry === 'number')))
return null
if (!(isPlainObject (value.provenance)))
return null
if (!(hasOnlyKeys (value.provenance, PROVENANCE_KEYS)))
return null
if (!(Object.values (value.provenance).every (origin => isValidOrigin (origin))))
return null
if (!(isPlainObject (value.tagSources)))
return null
if (!(hasOnlyKeys (value.tagSources, TAG_SOURCE_KEYS)))
return null
if (!(Object.values (value.tagSources).every (_1 => typeof _1 === 'string')))
return null
if (value.metadataUrl != null && typeof value.metadataUrl !== 'string')
return null
return {
url: value.url,
attributes: value.attributes as Record<string, string | number> }
attributes: value.attributes as Record<string, string | number>,
provenance: value.provenance as Record<string, PostImportOrigin>,
tagSources: value.tagSources as Record<PostImportOrigin, string>,
metadataUrl: value.metadataUrl as string | undefined }
}
+20 -8
ファイルの表示
@@ -10,8 +10,11 @@ export type PostImportResultStatus = 'created' | 'skipped' | 'failed'
export type PostImportAttributeValue = string | number
export type PostImportResetSnapshot = {
url: string
attributes: Record<string, PostImportAttributeValue> }
url: string
attributes: Record<string, PostImportAttributeValue>
provenance: Record<string, PostImportOrigin>
tagSources: Record<PostImportOrigin, string>
metadataUrl?: string }
export type PostImportRow = {
sourceRow: number
@@ -31,12 +34,21 @@ export type PostImportRow = {
createdPostId?: number
importStatus?: PostImportStatus }
export type PostImportResultRow = {
sourceRow: number
status: PostImportResultStatus
post?: { id: number }
existingPostId?: number
errors?: Record<string, string[]> }
export type PostImportResultRow =
| {
sourceRow: number
status: 'created'
post: { id: number }
errors?: Record<string, string[]> }
| {
sourceRow: number
status: 'skipped'
existingPostId: number
errors?: Record<string, string[]> }
| {
sourceRow: number
status: 'failed'
errors?: Record<string, string[]> }
export type PostImportSession = {
version: number