IccCodeConnectBookClient Implementation Plan
Issue: #339 - ICC Code Connect API Client
Goal
Create IccCodeConnectBookClient in the codeconnect package to recursively fetch ICC Code Connect books using the existing retriever classes, persisting files in api/api.iccsafe.org.
Proposed Changes
Phase 1: Content Persister
[NEW] IccCodeConnectContentPersister.java
Persist individual content items:
public void fetchAndSaveContent(String contentId, String outputPath)
Phase 2: Section Fetcher
[NEW] IccCodeConnectSectionFetcher.java
Recursively fetch section and its content items:
public SectionList fetchSection(String bookId, String chapterId, String sectionId)
public void fetchSectionWithContent(String bookId, String chapterId, String sectionId)
Phase 3: Chapter Fetcher
[NEW] IccCodeConnectChapterFetcher.java
Fetch chapter metadata and all its sections:
public ChapterList fetchChapter(String bookId, String chapterId)
public void fetchChapterWithSections(String bookId, String chapterId)
Phase 4: Book Client
[NEW] IccCodeConnectBookClient.java
Full book fetch - Builder pattern like IccBookClient:
public class IccCodeConnectBookClient {
private final String bookId;
private final FileSystemHandler fsHandler;
private final IccCodeConnectClient client;
public void fetch() // Full book fetch
public void fetchBookInfo()
public void fetchChaptersIndex()
public void fetchAllChapters() // With rate limiting
}
Folder Structure
api/api.iccsafe.org/
├── v1/
│ ├── books.json # All books
│ ├── book/{bookId}/
│ │ ├── chapters.json # Chapter list
│ │ └── chapter/{chapterId}/
│ │ ├── sections.json # Section list
│ │ └── list/{sectionId}.json
│ └── content/{contentId}.json
Verification Plan
- Unit test each component independently
- Integration test: fetch a single content item
- Integration test: fetch a single section with content
- Integration test: fetch a single chapter with sections
- Full book fetch with rate limiting (manual verification)
API Nuances and Gotchas
Empty Section IDs (Definitions) vs Regular Sections
The API sections list contains mixed types of items:
- Regular Sections: Have
dtype="section"and a validid. These are traversable and have content. - Definitions/Markers: Have
dtype="definition"(or potentially other types) and an emptyid. These are typically inline text markers or metadata without their own content endpoint.
Trying to fetch content for items with empty IDs results in a 404.
Resolution: The recursive fetcher MUST skip items where id is empty.
Example Payload Comparison:
{
"sections": [
{
"id": "IBC2018P6_Ch04_Sec402.8.2.1",
"dtype": "section", // VALID: Has ID and dtype="section"
"title": "Occupant formula"
},
{
"dtype": "definition", // INVALID: No ID, dtype="definition"
"childrenCount": { "subsections": 0 }
}
]
}
In the above example, the first item is processed. The second item (definition) is ignored.