Skip to content

Commit dc485fe

Browse files
committed
wip: cell level mappings
1 parent 84b0941 commit dc485fe

25 files changed

Lines changed: 3031 additions & 479 deletions
Lines changed: 142 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,157 @@
1-
import { GoogleDocsReviewFixture } from './types';
1+
import {
2+
type ReviewAsset,
3+
type ReviewEntryBlockGraph,
4+
type ReviewNormalizedDocument,
5+
type GoogleDocsReviewData,
6+
} from './types';
27

3-
const fixtureModules = import.meta.glob('./fixture.json', { eager: true }) as Record<
8+
const FIXTURE_LOG_PREFIX = '[google-docs][fixture]';
9+
10+
const fixtureModules = import.meta.glob('./*.json', { eager: true }) as Record<
411
string,
512
{ default: unknown }
613
>;
714

8-
const hasRequiredShape = (value: unknown): value is GoogleDocsReviewFixture => {
9-
if (!value || typeof value !== 'object') {
10-
return false;
11-
}
15+
function isRecord(value: unknown): value is Record<string, unknown> {
16+
return typeof value === 'object' && value !== null && !Array.isArray(value);
17+
}
18+
19+
function isNormalizedDocumentLike(value: unknown): value is ReviewNormalizedDocument {
20+
return isRecord(value) && Array.isArray(value.contentBlocks) && Array.isArray(value.tables);
21+
}
22+
23+
function isEntryBlockGraphLike(value: unknown): value is ReviewEntryBlockGraph {
24+
return isRecord(value) && Array.isArray(value.entries) && Array.isArray(value.excludedSourceRefs);
25+
}
1226

13-
const candidate = value as Record<string, unknown>;
27+
function hasReviewFixtureShape(value: unknown): value is GoogleDocsReviewData {
1428
return (
15-
Array.isArray(candidate.entries) &&
16-
Array.isArray(candidate.assets) &&
17-
!!candidate.normalizedDocument &&
18-
typeof candidate.normalizedDocument === 'object' &&
19-
!!candidate.mappingPlan &&
20-
typeof candidate.mappingPlan === 'object'
29+
isRecord(value) &&
30+
Array.isArray(value.entries) &&
31+
Array.isArray(value.assets) &&
32+
isNormalizedDocumentLike(value.originalNormalizedDocument) &&
33+
isNormalizedDocumentLike(value.editableNormalizedDocument) &&
34+
isEntryBlockGraphLike(value.entryBlockGraph)
2135
);
22-
};
36+
}
37+
38+
function hasNormalizedDocumentPayloadShape(value: unknown): value is {
39+
normalizedDocument: ReviewNormalizedDocument;
40+
entryBlockGraph: ReviewEntryBlockGraph;
41+
contentTypes?: Array<Record<string, unknown>>;
42+
assets?: ReviewAsset[];
43+
cmaAssets?: ReviewAsset[];
44+
referenceGraph?: GoogleDocsReviewData['referenceGraph'];
45+
} {
46+
return (
47+
isRecord(value) &&
48+
isNormalizedDocumentLike(value.normalizedDocument) &&
49+
isEntryBlockGraphLike(value.entryBlockGraph)
50+
);
51+
}
52+
53+
function buildEntriesFromEntryBlockGraph(
54+
entryBlockGraph: ReviewEntryBlockGraph,
55+
contentTypes: Array<Record<string, unknown>> = []
56+
): GoogleDocsReviewData['entries'] {
57+
const contentTypeNameById = new Map<string, string>();
58+
59+
contentTypes.forEach((contentType) => {
60+
const sys = isRecord(contentType.sys) ? contentType.sys : null;
61+
const contentTypeId = typeof sys?.id === 'string' ? sys.id : null;
62+
const contentTypeName = typeof contentType.name === 'string' ? contentType.name : null;
63+
64+
if (contentTypeId && contentTypeName) {
65+
contentTypeNameById.set(contentTypeId, contentTypeName);
66+
}
67+
});
68+
69+
return entryBlockGraph.entries.map((entry, index) => ({
70+
tempId: entry.tempId,
71+
contentTypeId: entry.contentTypeId,
72+
fields: {
73+
title: {
74+
'en-US':
75+
contentTypeNameById.get(entry.contentTypeId) ??
76+
entry.tempId ??
77+
`${entry.contentTypeId}-${index + 1}`,
78+
},
79+
},
80+
}));
81+
}
82+
83+
export function coerceGoogleDocsReviewData(value: unknown): GoogleDocsReviewData | null {
84+
if (hasReviewFixtureShape(value)) {
85+
return value;
86+
}
87+
88+
if (hasNormalizedDocumentPayloadShape(value)) {
89+
const assets = Array.isArray(value.assets)
90+
? value.assets
91+
: Array.isArray(value.cmaAssets)
92+
? value.cmaAssets
93+
: [];
94+
95+
return {
96+
entries: buildEntriesFromEntryBlockGraph(value.entryBlockGraph, value.contentTypes ?? []),
97+
assets,
98+
referenceGraph: isRecord(value.referenceGraph) ? value.referenceGraph : undefined,
99+
originalNormalizedDocument: value.normalizedDocument,
100+
editableNormalizedDocument: structuredClone(value.normalizedDocument),
101+
entryBlockGraph: value.entryBlockGraph,
102+
};
103+
}
104+
105+
return null;
106+
}
107+
108+
export const loadGoogleDocsReviewData = (): GoogleDocsReviewData | null => {
109+
const availableFixtureFiles = Object.keys(fixtureModules)
110+
.map((path) => path.replace('./', ''))
111+
.sort();
112+
113+
console.info(FIXTURE_LOG_PREFIX, 'Attempting to load review fixture.', {
114+
requestedFile: 'fixture.json',
115+
availableFixtureFiles,
116+
});
23117

24-
export const loadGoogleDocsReviewFixture = (): GoogleDocsReviewFixture | null => {
25118
const module = fixtureModules['./fixture.json'];
26119
if (!module) {
120+
console.warn(
121+
FIXTURE_LOG_PREFIX,
122+
'Unable to find fixture.json. Add src/fixtures/googleDocsReview/fixture.json or rename the fixture you want to load.'
123+
);
124+
return null;
125+
}
126+
127+
const fixture = coerceGoogleDocsReviewData(module.default);
128+
if (!fixture) {
129+
console.warn(
130+
FIXTURE_LOG_PREFIX,
131+
'fixture.json was found but does not match a supported Google Docs review fixture shape.',
132+
{
133+
topLevelKeys:
134+
module.default && typeof module.default === 'object'
135+
? Object.keys(module.default as Record<string, unknown>).sort()
136+
: [],
137+
}
138+
);
27139
return null;
28140
}
29141

30-
return hasRequiredShape(module.default) ? module.default : null;
142+
const variant = hasReviewFixtureShape(module.default)
143+
? 'review-fixture'
144+
: hasNormalizedDocumentPayloadShape(module.default)
145+
? 'normalized-document-payload'
146+
: 'unknown';
147+
148+
console.info(FIXTURE_LOG_PREFIX, 'Review fixture loaded successfully.', {
149+
variant,
150+
entries: fixture.entries.length,
151+
assets: fixture.assets.length,
152+
contentBlocks: fixture.originalNormalizedDocument.contentBlocks.length,
153+
tables: fixture.originalNormalizedDocument.tables.length,
154+
});
155+
156+
return fixture;
31157
};
Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
export { loadGoogleDocsReviewFixture } from './fixture-loader';
1+
export { loadGoogleDocsReviewData } from './fixture-loader';
22
export type {
3-
GoogleDocsReviewFixture,
4-
FixtureContentBlock,
5-
FixtureTable,
6-
FixtureUsageItem,
3+
GoogleDocsReviewData,
4+
ReviewContentBlock,
5+
ReviewTextRun,
6+
ReviewTable,
7+
ReviewUsageItem,
8+
ReviewFieldMapping,
9+
ReviewSourceRef,
10+
ReviewTablePart,
11+
ReviewEntryBlockGraph,
712
} from './types';

0 commit comments

Comments
 (0)