Add a new data source
How to add new data the school serves publicly (course lists, menus, other public data). Sources are independent and share only the common layer (common: errors, HTTP transport, version). Authentication (auth) is one source just like public data, so you can build yours without touching the others.
Procedure
Section titled “Procedure”- Propose it in an issue. Use the
새 정보 출처 제안(new data source proposal) issue template. Describe where it is served, whether login is needed, the request form (JSON or HTML), the fields, and whether it contains personal data. Information that exists only as HTML is not used as a primary source. - Write the contract first. Put the address, request keys and fixed values under
publicDatainspec/contract.json(each language keeps its own constants and its contract test compares them), then add the method, request and field mapping tospec/PUBLIC_DATA.md, one line each, with the date checked and the account type you used. Add the same method to the agent-facing method listspec/api-index.jsontoo (parameters, return model, Java signatures).python3 scripts/check-api-index.pyfails if it drifts from the three implementations. - Create synthetic fixtures and expected results. Add them to
FIXTURESand the per-sourcemap_*()functions (merged bymap_all()) inscripts/generate-public-data-fixtures.pyand run it. Never copy a real response, and never put in values that identify a person, such as a staff name. - Implement Java, Python and Node in the same PR. Put the request building and mapping in one file per source (
<source>_source.py,<source>Source.ts,<source>Source.java) and add only a one-line public method toSejongPublicData. Reuse the shared file (fetcher) for cache and failure isolation, and follow the contract’s value rules (empty string when missing,extra). - Write only the tests you need. One test that the fixture result equals the expected result is enough. Do not add tests that call the school’s servers.
- Add a shape check. Add a row to
SOURCESinscripts/probe-public-data.py. It runs without an account. - Go through the feature exposure checklist.
Where the code goes
Section titled “Where the code goes”For a public-data source. One method maps to three files.
| Language | Method and request | Result model | Test |
|---|---|---|---|
| Python | python/src/sejongpeia/public_data/<source>_source.py (request, mapping), the public method line in client.py |
public_data/models.py |
python/tests/test_public_data.py |
| Node | node/src/publicData/<source>Source.ts (request, mapping), the public method line in client.ts, argument objects in queries.ts |
publicData/models.ts |
node/test/publicData.test.mjs |
| Java | publicdata/<source>Source.java (request, mapping), the public methods in SejongPublicData.java |
publicdata/model/ |
SejongPublicDataTest.java |
- Method names are
snake_casein Python andcamelCasein Node and Java. School fields you do not map go intoextra. - Login-required sources are not covered by this procedure. Sources cannot import each other, so how a login session would be shared needs a design; agree on the direction in an issue first.
- When you add or change a public method or result field, check that the “Public API” list in API stability and the integration examples are still correct.
Dependency rule
Section titled “Dependency rule”The rule has three layers. Common (common) imports no source, and each source (auth, public_data) imports only common and never another source. The top-level entry points (__init__.py, index.ts, Spring config) only assemble, so they may import every layer. Tests enforce it.
| Language | Rule test |
|---|---|
| Python | python/tests/test_dependency_rules.py |
| Node | node/test/dependencyRules.test.mjs |
| Java | DependencyRulesTest |
Inside public data, source files (*_source) never import each other or the entry point (client); they use only the shared files (fetcher, row_reader, models). If a rule fails, move what needs sharing into common.
Folder layout (same shape in all three languages)
| Layer | Python python/src/sejongpeia/ |
Node node/src/ |
Java java/src/main/java/kr/suhsaechan/sejongpeia/ |
|---|---|---|---|
| Common | common/ (errors, transport, version) |
common/ (errors, transport, version) |
common/ (exception, util, http, config) |
| Authentication | auth/ (client, parsers, portal_main_parser, models) |
auth/ (client, parsers, portalMainParser, models) |
auth/ (client, parser, model, service) |
| Public data | public_data/ (client, fetcher, row_reader, models, *_source) |
publicData/ (client, fetcher, rowReader, queries, models, *Source) |
publicdata/ (SejongPublicData, *Source, PublicDataFetcher, model) |
| Assembly | __init__.py |
index.ts |
config/ (Spring auto-configuration, TLS setup) |
What goes in spec/
Section titled “What goes in spec/”The contract between the school’s services and the three implementations lives in spec/. When the school changes something, change spec/ first, then all three implementations in the same PR.
| Kind | Location | Rule |
|---|---|---|
| Request and response contract, field meanings, limits | spec/<source>.md |
One document per source. Write only what was verified, and say what was not |
| Values shared by the implementations (URLs, error messages) | spec/contract.json |
The single source. Each language’s contract test fails if its constants drift |
| Where the school serves what, dead ends, what was not checked | spec/REFERENCE_DATA_SURVEY.md |
Read it before searching again. Update it when you look somewhere new |
| Sample responses for tests | spec/fixtures/ |
Synthetic only. Never real responses, personal data, session values or staff names |
| Shape drift detection | scripts/probe-*.py |
Runs without an account, one entry per source |
Rules that keep it safe
Section titled “Rules that keep it safe”- Return the school’s raw strings. No value lists or code tables in the library, no guessing a code from a name.
- A failure in one source never blocks another, authentication in particular. Return an empty value or the last cached value.
- Do not log or return internal server details (stack traces, session identifiers, IP addresses, password-state flags).
- Be gentle with public pages and endpoints: cache, space requests out, and bound the number of requests per refresh.
See also Failure isolation and cache and Common rules.
