@things-factory/integration-data-go-kr 10.0.0-beta.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -0
- package/ROADMAP.md +282 -0
- package/dist-server/engine/connector/data-go-kr-client.d.ts +27 -0
- package/dist-server/engine/connector/data-go-kr-client.js +138 -0
- package/dist-server/engine/connector/data-go-kr-client.js.map +1 -0
- package/dist-server/engine/connector/data-go-kr-connector.d.ts +29 -0
- package/dist-server/engine/connector/data-go-kr-connector.js +53 -0
- package/dist-server/engine/connector/data-go-kr-connector.js.map +1 -0
- package/dist-server/engine/connector/index.d.ts +1 -0
- package/dist-server/engine/connector/index.js +4 -0
- package/dist-server/engine/connector/index.js.map +1 -0
- package/dist-server/engine/index.d.ts +2 -0
- package/dist-server/engine/index.js +5 -0
- package/dist-server/engine/index.js.map +1 -0
- package/dist-server/engine/spec/building-ledger/hub.d.ts +12 -0
- package/dist-server/engine/spec/building-ledger/hub.js +138 -0
- package/dist-server/engine/spec/building-ledger/hub.js.map +1 -0
- package/dist-server/engine/spec/building-ledger/index.d.ts +7 -0
- package/dist-server/engine/spec/building-ledger/index.js +12 -0
- package/dist-server/engine/spec/building-ledger/index.js.map +1 -0
- package/dist-server/engine/spec/index.d.ts +17 -0
- package/dist-server/engine/spec/index.js +59 -0
- package/dist-server/engine/spec/index.js.map +1 -0
- package/dist-server/engine/spec/types.d.ts +79 -0
- package/dist-server/engine/spec/types.js +30 -0
- package/dist-server/engine/spec/types.js.map +1 -0
- package/dist-server/engine/spec/weather/asos.d.ts +2 -0
- package/dist-server/engine/spec/weather/asos.js +33 -0
- package/dist-server/engine/spec/weather/asos.js.map +1 -0
- package/dist-server/engine/spec/weather/aws.d.ts +2 -0
- package/dist-server/engine/spec/weather/aws.js +28 -0
- package/dist-server/engine/spec/weather/aws.js.map +1 -0
- package/dist-server/engine/spec/weather/index.d.ts +14 -0
- package/dist-server/engine/spec/weather/index.js +23 -0
- package/dist-server/engine/spec/weather/index.js.map +1 -0
- package/dist-server/engine/spec/weather/mid-term-forecast.d.ts +2 -0
- package/dist-server/engine/spec/weather/mid-term-forecast.js +56 -0
- package/dist-server/engine/spec/weather/mid-term-forecast.js.map +1 -0
- package/dist-server/engine/spec/weather/short-term-forecast.d.ts +2 -0
- package/dist-server/engine/spec/weather/short-term-forecast.js +71 -0
- package/dist-server/engine/spec/weather/short-term-forecast.js.map +1 -0
- package/dist-server/engine/spec/weather/warning.d.ts +2 -0
- package/dist-server/engine/spec/weather/warning.js +34 -0
- package/dist-server/engine/spec/weather/warning.js.map +1 -0
- package/dist-server/engine/task/building-ledger/general.d.ts +30 -0
- package/dist-server/engine/task/building-ledger/general.js +123 -0
- package/dist-server/engine/task/building-ledger/general.js.map +1 -0
- package/dist-server/engine/task/building-ledger/index.d.ts +13 -0
- package/dist-server/engine/task/building-ledger/index.js +16 -0
- package/dist-server/engine/task/building-ledger/index.js.map +1 -0
- package/dist-server/engine/task/building-ledger/summary.d.ts +95 -0
- package/dist-server/engine/task/building-ledger/summary.js +174 -0
- package/dist-server/engine/task/building-ledger/summary.js.map +1 -0
- package/dist-server/engine/task/call.d.ts +40 -0
- package/dist-server/engine/task/call.js +144 -0
- package/dist-server/engine/task/call.js.map +1 -0
- package/dist-server/engine/task/index.d.ts +20 -0
- package/dist-server/engine/task/index.js +23 -0
- package/dist-server/engine/task/index.js.map +1 -0
- package/dist-server/engine/task/weather/grid-converter.d.ts +24 -0
- package/dist-server/engine/task/weather/grid-converter.js +69 -0
- package/dist-server/engine/task/weather/grid-converter.js.map +1 -0
- package/dist-server/engine/task/weather/index.d.ts +13 -0
- package/dist-server/engine/task/weather/index.js +18 -0
- package/dist-server/engine/task/weather/index.js.map +1 -0
- package/dist-server/engine/task/weather/short-term-forecast.d.ts +38 -0
- package/dist-server/engine/task/weather/short-term-forecast.js +99 -0
- package/dist-server/engine/task/weather/short-term-forecast.js.map +1 -0
- package/dist-server/engine/types.d.ts +63 -0
- package/dist-server/engine/types.js +9 -0
- package/dist-server/engine/types.js.map +1 -0
- package/dist-server/index.d.ts +6 -0
- package/dist-server/index.js +13 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/helps/integration/connector/data-go-kr-connector.md +38 -0
- package/helps/integration/services/building-ledger.md +110 -0
- package/helps/integration/services/weather.md +82 -0
- package/helps/integration/task/building-ledger.md +135 -0
- package/helps/integration/task/data-go-kr-call.md +114 -0
- package/helps/integration/task/weather-short-term-forecast.md +96 -0
- package/package.json +28 -0
- package/server/engine/connector/data-go-kr-client.ts +157 -0
- package/server/engine/connector/data-go-kr-connector.ts +72 -0
- package/server/engine/connector/index.ts +1 -0
- package/server/engine/index.ts +2 -0
- package/server/engine/spec/building-ledger/hub.ts +143 -0
- package/server/engine/spec/building-ledger/index.ts +7 -0
- package/server/engine/spec/index.ts +62 -0
- package/server/engine/spec/types.ts +84 -0
- package/server/engine/spec/weather/asos.ts +32 -0
- package/server/engine/spec/weather/aws.ts +27 -0
- package/server/engine/spec/weather/index.ts +14 -0
- package/server/engine/spec/weather/mid-term-forecast.ts +56 -0
- package/server/engine/spec/weather/short-term-forecast.ts +73 -0
- package/server/engine/spec/weather/warning.ts +33 -0
- package/server/engine/task/building-ledger/general.ts +141 -0
- package/server/engine/task/building-ledger/index.ts +13 -0
- package/server/engine/task/building-ledger/summary.ts +277 -0
- package/server/engine/task/call.ts +177 -0
- package/server/engine/task/index.ts +20 -0
- package/server/engine/task/weather/grid-converter.ts +79 -0
- package/server/engine/task/weather/index.ts +14 -0
- package/server/engine/task/weather/short-term-forecast.ts +110 -0
- package/server/engine/types.ts +64 -0
- package/server/index.ts +7 -0
- package/things-factory.config.js +1 -0
- package/translations/en.json +18 -0
- package/translations/ja.json +18 -0
- package/translations/ko.json +18 -0
- package/translations/ms.json +18 -0
- package/translations/zh.json +18 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../../../server/engine/task/building-ledger/summary.ts"],"names":[],"mappings":"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","sourcesContent":["import { ConnectionManager, Context, TaskRegistry, evaluateTemplate } from '@things-factory/integration-base'\n\nimport { DataGoKrConnectionInstance } from '../../connector/data-go-kr-connector'\nimport { BldRgstHubService } from '../../spec/building-ledger'\nimport { BrTitleItem } from '../../types'\n\n/**\n * 건축물 4 핵심 KPI (연면적·용적률·지상층수·지하층수) 만 **단일 객체** 로 반환하는\n * 요약 태스크. PNU 한 번 입력으로 케이스 (집합·단동) 자동 분기 + 단일값 보장.\n *\n * 내부 흐름:\n * 1) `getBrRecapTitleInfo` (총괄표제부) 호출\n * - 응답 items 가 1건 이상이면 단지 단위 row 그대로 사용 (source='recap')\n * - 0건이면 일반건축물 가능성 → 2 단계로\n * 2) `getBrTitleInfo` (표제부) 호출\n * - items[0] 사용 (source='title')\n * - 단동이면 1건, 다동이면 첫 동 (일반건축물 분기라 보통 단동)\n * 3) 둘 다 0건이면 에러\n *\n * 출력 (`data`):\n * {\n * totArea: number | null, // 연면적 (㎡)\n * vlRat: number | null, // 용적률 (%)\n * grndFlrCnt: number | null, // 지상층수\n * ugrndFlrCnt:number | null, // 지하층수\n * bldNm: string | null,\n * useAprDay: string | null, // 사용승인일 YYYYMMDD\n * source: 'recap' | 'title',\n * raw: BrTitleItem // 원본 (집합·단동 케이스 모두의 단일 row)\n * }\n *\n * 시나리오 사용 예 (juso 결합):\n * [juso-resolve-address] → data.pnu\n * [data-go-kr-building-summary]\n * sigunguCd = ${data.pnu.sigunguCd}\n * bjdongCd = ${data.pnu.bjdongCd}\n * platGbCd = ${data.pnu.platGbCd}\n * bun = ${data.pnu.bun}\n * ji = ${data.pnu.ji}\n * 다음 step 에서 ${data.totArea}, ${data.vlRat} 등 직접 접근.\n */\n\nexport interface BuildingSummary {\n totArea: number | null\n /** 대지면적 (㎡) — 집합건축물 총괄표제부에 있음 */\n platArea: number | null\n /** 건축면적 (㎡) */\n archArea: number | null\n /** 건폐율 (%) */\n bcRat: number | null\n vlRat: number | null\n grndFlrCnt: number | null\n ugrndFlrCnt: number | null\n /** 주구조 코드 (예: '21'=철근콘크리트구조, '22'=철골철근콘크리트구조(SRC), '14'=프리캐스트콘크리트(PC) …) */\n strctCd: string | null\n /** 주구조 이름 (예: '철근콘크리트구조', '철골철근콘크리트구조') */\n strctCdNm: string | null\n /** 기타구조 자유텍스트 (도면상 상세 표기) */\n etcStrct: string | null\n /** 주용도 코드 (예: '02000'=공동주택, '03000'=제1종근린생활시설 …) */\n mainPurpsCd: string | null\n /** 주용도 이름 (예: '공동주택', '제1종근린생활시설') — DSSP SiteType 매핑 원천 */\n mainPurpsCdNm: string | null\n /** 기타용도 자유텍스트 (도면상 상세 표기) */\n etcPurps: string | null\n /** 세대수 — 집합건축물 */\n hhldCnt: number | null\n /** 가구수 */\n fmlyCnt: number | null\n /** 호수 (상가/근린 등) */\n hoCnt: number | null\n /** 주건축물수 (단지 동 수) — recap 에서 노출 */\n mainBldCnt: number | null\n /** 부속건축물수 */\n atchBldCnt: number | null\n /** 건물 높이 (m) */\n heit: number | null\n bldNm: string | null\n useAprDay: string | null\n /** 건축허가일 YYYYMMDD */\n pmsDay: string | null\n /** 착공일 YYYYMMDD */\n stcnsDay: string | null\n source: 'recap' | 'title'\n raw: BrTitleItem\n}\n\nasync function BuildingSummaryTask(step, context: Context): Promise<{ data: BuildingSummary }> {\n const { connection: connectionName, params } = step\n const { domain, user, data, variables, lng, logger } = context\n\n const instance: DataGoKrConnectionInstance = await ConnectionManager.getConnectionInstanceByName(\n domain,\n connectionName\n )\n if (!instance?.client) {\n throw new Error(`data-go-kr connection '${connectionName}' is not established.`)\n }\n\n const scope = { domain, user, lng, data, variables, console }\n const evalString = (value: string | undefined) => {\n if (value === undefined || value === null || value === '') return undefined\n try {\n return evaluateTemplate(String(value), scope)\n } catch (err) {\n throw new Error(`data-go-kr-building-summary: failed to evaluate parameter: ${(err as Error).message}`)\n }\n }\n\n const sigunguCd = evalString(params.sigunguCd)\n const bjdongCd = evalString(params.bjdongCd)\n const bun = evalString(params.bun)\n if (!sigunguCd) throw new Error('data-go-kr-building-summary: sigunguCd is required')\n if (!bjdongCd) throw new Error('data-go-kr-building-summary: bjdongCd is required')\n if (!bun) throw new Error('data-go-kr-building-summary: bun is required')\n\n const pnu = {\n sigunguCd,\n bjdongCd,\n platGbCd: evalString(params.platGbCd) || '0',\n bun: zpad(bun, 4),\n ji: zpad(evalString(params.ji) || '0', 4),\n numOfRows: 100,\n pageNo: 1\n }\n\n // 두 API 모두 호출 — recap 결과가 있어도 strctCdNm 등 동 단위 정보는\n // 표제부의 주건축물 row 에서 보완.\n const recapPath = `${BldRgstHubService.serviceId}/getBrRecapTitleInfo`\n const titlePath = `${BldRgstHubService.serviceId}/getBrTitleInfo`\n logger?.info?.(`[data-go-kr] fetch recap + title params=${JSON.stringify(pnu)}`)\n\n const [recapItems, titleItems] = await Promise.all([\n instance.client\n .getItems<BrTitleItem>(recapPath, pnu)\n .catch(err => {\n logger?.warn?.(`[data-go-kr] recap call failed: ${(err as Error).message}`)\n return [] as BrTitleItem[]\n }),\n instance.client\n .getItems<BrTitleItem>(titlePath, pnu)\n .catch(err => {\n logger?.warn?.(`[data-go-kr] title call failed: ${(err as Error).message}`)\n return [] as BrTitleItem[]\n })\n ])\n\n if (recapItems.length === 0 && titleItems.length === 0) {\n throw new Error(\n `data-go-kr-building-summary: PNU ${pnu.sigunguCd}-${pnu.bjdongCd}-${pnu.platGbCd}-${pnu.bun}-${pnu.ji} ` +\n `에 대해 총괄표제부·표제부 모두 0건. PNU 오류, 미준공 건물, 또는 미등록 필지 가능성.`\n )\n }\n\n // 집합건축물 = recap 있음 → 단지 단위 4 KPI 정확.\n // 일반건축물 = recap 없음 → title 첫 row 가 답.\n // 두 경우 모두 구조정보(strctCdNm 등) 는 title 의 **주건축물** row 에서 가져오는\n // 게 정확. recap 에는 단지 대표 구조가 노출 안 될 수 있음.\n const recap = recapItems[0]\n const mainTitle =\n titleItems.find(t => (t as any).mainAtchGbCd === '0') || // 주건축물 우선\n titleItems[0]\n const source: 'recap' | 'title' = recap ? 'recap' : 'title'\n logger?.info?.(\n `[data-go-kr] summary source=${source} recap=${recapItems.length} title=${titleItems.length} mainBldNm='${(mainTitle as any)?.bldNm || ''}'`\n )\n\n return { data: toSummary(recap, mainTitle, source) }\n}\n\n/**\n * 4 KPI + 구조 정보 + 메타를 단일 객체로 추출.\n *\n * - 4 KPI (totArea/vlRat/grndFlrCnt/ugrndFlrCnt) : 집합이면 recap 우선, 단동이면 title.\n * - 구조 정보 (strctCd/Nm/etcStrct) : 동 단위라 title 의 주건축물 row 에서 가져옴.\n * title 에 정보 없으면 recap 에서 fallback.\n */\nfunction toSummary(\n recap: BrTitleItem | undefined,\n mainTitle: BrTitleItem | undefined,\n source: 'recap' | 'title'\n): BuildingSummary {\n const num = (v: any): number | null => {\n if (v === undefined || v === null || v === '' || v === ' ') return null\n const n = Number(v)\n return Number.isFinite(n) ? n : null\n }\n const str = (v: any): string | null => {\n if (v === undefined || v === null) return null\n const s = String(v).trim()\n return s ? s : null\n }\n // KPI 베이스 — 집합이면 recap, 아니면 title\n const base = recap || mainTitle || ({} as BrTitleItem)\n // 구조 — title 의 주건축물 우선, 없으면 recap\n const structSrc = mainTitle || recap || ({} as BrTitleItem)\n\n // 단지 차원 수치 (대지면적/건폐율/세대수/동수 등) 는 recap 에 명시. 단동이면 title.\n return {\n totArea: num(base.totArea),\n platArea: num((base as any).platArea) || num((structSrc as any).platArea),\n archArea: num((base as any).archArea) || num((structSrc as any).archArea),\n bcRat: num((base as any).bcRat) || num((structSrc as any).bcRat),\n vlRat: num(base.vlRat),\n grndFlrCnt: num(base.grndFlrCnt),\n ugrndFlrCnt: num(base.ugrndFlrCnt),\n strctCd: str(structSrc.strctCd) || str(base.strctCd),\n strctCdNm: str(structSrc.strctCdNm) || str(base.strctCdNm),\n etcStrct: str((structSrc as any).etcStrct) || str((base as any).etcStrct),\n // 주용도도 구조와 같이 title 의 주건축물 row 가 신뢰할 만함 — 단지 단위 recap\n // 에 단일값으로 노출 안 되는 경우가 있음.\n mainPurpsCd: str((structSrc as any).mainPurpsCd) || str((base as any).mainPurpsCd),\n mainPurpsCdNm: str(structSrc.mainPurpsCdNm) || str(base.mainPurpsCdNm),\n etcPurps: str((structSrc as any).etcPurps) || str((base as any).etcPurps),\n hhldCnt: num((base as any).hhldCnt) || num((structSrc as any).hhldCnt),\n fmlyCnt: num((base as any).fmlyCnt) || num((structSrc as any).fmlyCnt),\n hoCnt: num((base as any).hoCnt) || num((structSrc as any).hoCnt),\n mainBldCnt: num((base as any).mainBldCnt) || num((structSrc as any).mainBldCnt),\n atchBldCnt: num((base as any).atchBldCnt) || num((structSrc as any).atchBldCnt),\n heit: num((structSrc as any).heit) || num((base as any).heit),\n bldNm: str(base.bldNm) || str(structSrc.bldNm),\n useAprDay: str(base.useAprDay) || str(structSrc.useAprDay),\n pmsDay: str((base as any).pmsDay) || str((structSrc as any).pmsDay),\n stcnsDay: str((base as any).stcnsDay) || str((structSrc as any).stcnsDay),\n source,\n raw: base\n }\n}\n\nfunction zpad(value: string, width: number): string {\n const s = String(value)\n return s.length >= width ? s : '0'.repeat(width - s.length) + s\n}\n\nBuildingSummaryTask.parameterSpec = [\n {\n type: 'string',\n name: 'sigunguCd',\n label: 'label.sigungu-cd',\n placeholder: '예) 11680 또는 ${data.pnu.sigunguCd}',\n description: '시군구코드 5자리. 이전 step 결과 액세서 사용 예: `${data.pnu.sigunguCd}`'\n },\n {\n type: 'string',\n name: 'bjdongCd',\n label: 'label.bjdong-cd',\n placeholder: '예) 10300 또는 ${data.pnu.bjdongCd}',\n description: '법정동코드 5자리. 이전 step 결과 액세서 사용 예: `${data.pnu.bjdongCd}`'\n },\n {\n type: 'string',\n name: 'platGbCd',\n label: 'label.plat-gb-cd',\n placeholder: '0=대지, 1=산, 2=블록 — 또는 ${data.pnu.platGbCd}',\n description: '대지구분 1자리. 정적값 0/1/2 또는 액세서 표현식.'\n },\n {\n type: 'string',\n name: 'bun',\n label: 'label.bun',\n placeholder: '예) 223 또는 ${data.pnu.bun}',\n description: '본번. 자동 4자리 zero-pad. 액세서 사용 예: `${data.pnu.bun}`'\n },\n {\n type: 'string',\n name: 'ji',\n label: 'label.ji',\n placeholder: '예) 0 또는 ${data.pnu.ji}',\n description: '부번. 없으면 0. 자동 4자리 zero-pad. 액세서 사용 예: `${data.pnu.ji}`'\n }\n]\n\nBuildingSummaryTask.help = 'integration/task/building-summary'\n\nTaskRegistry.registerTaskHandler('data-go-kr-building-summary', BuildingSummaryTask)\n\nexport default BuildingSummaryTask\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Context } from '@things-factory/integration-base';
|
|
2
|
+
import { DataGoKrResponse } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* 범용 공공데이터포털 호출 태스크.
|
|
5
|
+
*
|
|
6
|
+
* 하나의 태스크가 등록된 레지스트리의 **모든 오퍼레이션** 을 커버한다. TaskRegistry 가
|
|
7
|
+
* 수백 개 태스크로 폭발하는 것을 방지하는 핵심 설계.
|
|
8
|
+
*
|
|
9
|
+
* 입력:
|
|
10
|
+
* - `operationRef` : `"<serviceId>.<operationId>"` 형식 (예: `"1613000/BldRgstHubService.getBrTitleInfo"`)
|
|
11
|
+
* - `params` : 오퍼레이션 파라미터 객체. 스펙 등록된 오퍼레이션이면 자동 검증·기본값·zero-pad 적용
|
|
12
|
+
*
|
|
13
|
+
* 레지스트리에 없는 오퍼레이션도 호출 가능 (스펙 없음 → 검증 스킵). 새 data.go.kr 서비스가
|
|
14
|
+
* 등장해도 스펙 추가 전까지 범용 호출로 즉시 사용 가능하다는 장점.
|
|
15
|
+
*/
|
|
16
|
+
declare function DataGoKrCall(step: any, context: Context): Promise<{
|
|
17
|
+
data: DataGoKrResponse<any>;
|
|
18
|
+
}>;
|
|
19
|
+
declare namespace DataGoKrCall {
|
|
20
|
+
var parameterSpec: ({
|
|
21
|
+
type: string;
|
|
22
|
+
name: string;
|
|
23
|
+
label: string;
|
|
24
|
+
property: {
|
|
25
|
+
options: {
|
|
26
|
+
value: string;
|
|
27
|
+
display: string;
|
|
28
|
+
}[];
|
|
29
|
+
};
|
|
30
|
+
placeholder?: undefined;
|
|
31
|
+
} | {
|
|
32
|
+
type: string;
|
|
33
|
+
name: string;
|
|
34
|
+
label: string;
|
|
35
|
+
placeholder: string;
|
|
36
|
+
property?: undefined;
|
|
37
|
+
})[];
|
|
38
|
+
var help: string;
|
|
39
|
+
}
|
|
40
|
+
export default DataGoKrCall;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const integration_base_1 = require("@things-factory/integration-base");
|
|
4
|
+
const spec_1 = require("../spec");
|
|
5
|
+
/**
|
|
6
|
+
* 범용 공공데이터포털 호출 태스크.
|
|
7
|
+
*
|
|
8
|
+
* 하나의 태스크가 등록된 레지스트리의 **모든 오퍼레이션** 을 커버한다. TaskRegistry 가
|
|
9
|
+
* 수백 개 태스크로 폭발하는 것을 방지하는 핵심 설계.
|
|
10
|
+
*
|
|
11
|
+
* 입력:
|
|
12
|
+
* - `operationRef` : `"<serviceId>.<operationId>"` 형식 (예: `"1613000/BldRgstHubService.getBrTitleInfo"`)
|
|
13
|
+
* - `params` : 오퍼레이션 파라미터 객체. 스펙 등록된 오퍼레이션이면 자동 검증·기본값·zero-pad 적용
|
|
14
|
+
*
|
|
15
|
+
* 레지스트리에 없는 오퍼레이션도 호출 가능 (스펙 없음 → 검증 스킵). 새 data.go.kr 서비스가
|
|
16
|
+
* 등장해도 스펙 추가 전까지 범용 호출로 즉시 사용 가능하다는 장점.
|
|
17
|
+
*/
|
|
18
|
+
async function DataGoKrCall(step, context) {
|
|
19
|
+
const { connection: connectionName, params: stepParams } = step;
|
|
20
|
+
const { domain, user, data, variables, lng, logger } = context;
|
|
21
|
+
const instance = await integration_base_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
|
|
22
|
+
if (!instance?.client) {
|
|
23
|
+
throw new Error(`data-go-kr connection '${connectionName}' is not established.`);
|
|
24
|
+
}
|
|
25
|
+
const operationRef = stepParams?.operationRef;
|
|
26
|
+
if (!operationRef || typeof operationRef !== 'string') {
|
|
27
|
+
throw new Error('data-go-kr-call: operationRef is required (format: "<serviceId>.<operationId>")');
|
|
28
|
+
}
|
|
29
|
+
const resolved = spec_1.SpecRegistry.resolve(operationRef);
|
|
30
|
+
const rawParams = stepParams?.params || {};
|
|
31
|
+
const scope = { domain, user, lng, data, variables, console };
|
|
32
|
+
const evalValue = (value) => {
|
|
33
|
+
if (value === undefined || value === null)
|
|
34
|
+
return value;
|
|
35
|
+
if (typeof value !== 'string')
|
|
36
|
+
return value;
|
|
37
|
+
try {
|
|
38
|
+
return (0, integration_base_1.evaluateTemplate)(value, scope);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
throw new Error(`data-go-kr-call: failed to evaluate parameter: ${err.message}`);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const evaluatedParams = {};
|
|
45
|
+
for (const [k, v] of Object.entries(rawParams)) {
|
|
46
|
+
evaluatedParams[k] = evalValue(v);
|
|
47
|
+
}
|
|
48
|
+
const { service, operation } = resolved || { service: null, operation: null };
|
|
49
|
+
const finalParams = operation
|
|
50
|
+
? normalizeAndValidateParams(operation, evaluatedParams)
|
|
51
|
+
: evaluatedParams;
|
|
52
|
+
const servicePath = resolved
|
|
53
|
+
? `${service.serviceId}/${operation.operationId}`
|
|
54
|
+
: operationRef.replace('.', '/');
|
|
55
|
+
logger?.info?.(`[data-go-kr] call ${servicePath} params=${JSON.stringify(finalParams)}`);
|
|
56
|
+
const response = await instance.client.get(servicePath, finalParams);
|
|
57
|
+
return { data: response };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 스펙 기반 파라미터 정규화·검증.
|
|
61
|
+
*
|
|
62
|
+
* 1. required 필수값 체크
|
|
63
|
+
* 2. default 기본값 주입
|
|
64
|
+
* 3. pad zero-padding (예: bun=6 → "0006")
|
|
65
|
+
* 4. length 정확 길이 검증
|
|
66
|
+
* 5. enum 허용 값 검증
|
|
67
|
+
*/
|
|
68
|
+
function normalizeAndValidateParams(operation, input) {
|
|
69
|
+
const output = {};
|
|
70
|
+
const errors = [];
|
|
71
|
+
for (const p of operation.params) {
|
|
72
|
+
let value = input[p.name];
|
|
73
|
+
if (value === undefined || value === null || value === '') {
|
|
74
|
+
if (p.default !== undefined) {
|
|
75
|
+
value = p.default;
|
|
76
|
+
}
|
|
77
|
+
else if (p.required) {
|
|
78
|
+
errors.push(`'${p.name}' is required (${p.description})`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
value = String(value);
|
|
86
|
+
if (p.pad && value.length < p.pad) {
|
|
87
|
+
value = '0'.repeat(p.pad - value.length) + value;
|
|
88
|
+
}
|
|
89
|
+
if (p.length !== undefined && value.length !== p.length) {
|
|
90
|
+
errors.push(`'${p.name}' must be exactly ${p.length} characters (got "${value}", length ${value.length})`);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (p.enum && !p.enum.some(e => e.value === value)) {
|
|
94
|
+
errors.push(`'${p.name}' must be one of [${p.enum.map(e => e.value).join(', ')}] (got "${value}")`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
output[p.name] = value;
|
|
98
|
+
}
|
|
99
|
+
/* 스펙에 없는 파라미터도 pass-through (미등록 필드 허용) */
|
|
100
|
+
for (const [k, v] of Object.entries(input)) {
|
|
101
|
+
if (!(k in output) && !operation.params.find(p => p.name === k)) {
|
|
102
|
+
if (v !== undefined && v !== null && v !== '') {
|
|
103
|
+
output[k] = v;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (errors.length > 0) {
|
|
108
|
+
throw new Error(`data-go-kr-call parameter validation failed for ${operation.operationId}:\n - ${errors.join('\n - ')}`);
|
|
109
|
+
}
|
|
110
|
+
return output;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* SpecRegistry 에 등록된 모든 오퍼레이션을 (operationRef → display) 옵션으로 펼친다.
|
|
114
|
+
* 모듈 로딩 시 한 번 평가되며, 이후 register() 로 추가된 spec 은 반영되지 않음
|
|
115
|
+
* (드문 케이스라 수용 — 향후 동적 parameterSpec 도입 시 해결).
|
|
116
|
+
*/
|
|
117
|
+
function buildOperationRefOptions() {
|
|
118
|
+
return spec_1.SpecRegistry.listServices()
|
|
119
|
+
.flatMap(svc => Object.values(svc.operations).map(op => ({
|
|
120
|
+
value: `${svc.serviceId}.${op.operationId}`,
|
|
121
|
+
display: `[${svc.label}] ${op.label}`
|
|
122
|
+
})))
|
|
123
|
+
.sort((a, b) => a.display.localeCompare(b.display, 'ko'));
|
|
124
|
+
}
|
|
125
|
+
DataGoKrCall.parameterSpec = [
|
|
126
|
+
{
|
|
127
|
+
type: 'select',
|
|
128
|
+
name: 'operationRef',
|
|
129
|
+
label: 'label.operation-ref',
|
|
130
|
+
property: {
|
|
131
|
+
options: buildOperationRefOptions()
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
type: 'scenario-step-input',
|
|
136
|
+
name: 'params',
|
|
137
|
+
label: 'label.operation-params',
|
|
138
|
+
placeholder: '오퍼레이션 파라미터를 JSON 객체로. 예) 건축물대장: {"sigunguCd":"11680","bjdongCd":"10300","bun":"223"} · 단기예보: {"base_date":"20260425","base_time":"1100","nx":"60","ny":"127"}. 정확한 키는 helps/integration/services/<domain>.md 참고.'
|
|
139
|
+
}
|
|
140
|
+
];
|
|
141
|
+
DataGoKrCall.help = 'integration/task/data-go-kr-call';
|
|
142
|
+
integration_base_1.TaskRegistry.registerTaskHandler('data-go-kr-call', DataGoKrCall);
|
|
143
|
+
exports.default = DataGoKrCall;
|
|
144
|
+
//# sourceMappingURL=call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call.js","sourceRoot":"","sources":["../../../server/engine/task/call.ts"],"names":[],"mappings":"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","sourcesContent":["import { ConnectionManager, Context, TaskRegistry, evaluateTemplate } from '@things-factory/integration-base'\n\nimport { DataGoKrConnectionInstance } from '../connector/data-go-kr-connector'\nimport { DataGoKrResponse } from '../types'\nimport { OperationSpec, SpecRegistry } from '../spec'\n\n/**\n * 범용 공공데이터포털 호출 태스크.\n *\n * 하나의 태스크가 등록된 레지스트리의 **모든 오퍼레이션** 을 커버한다. TaskRegistry 가\n * 수백 개 태스크로 폭발하는 것을 방지하는 핵심 설계.\n *\n * 입력:\n * - `operationRef` : `\"<serviceId>.<operationId>\"` 형식 (예: `\"1613000/BldRgstHubService.getBrTitleInfo\"`)\n * - `params` : 오퍼레이션 파라미터 객체. 스펙 등록된 오퍼레이션이면 자동 검증·기본값·zero-pad 적용\n *\n * 레지스트리에 없는 오퍼레이션도 호출 가능 (스펙 없음 → 검증 스킵). 새 data.go.kr 서비스가\n * 등장해도 스펙 추가 전까지 범용 호출로 즉시 사용 가능하다는 장점.\n */\nasync function DataGoKrCall(step, context: Context): Promise<{ data: DataGoKrResponse<any> }> {\n const { connection: connectionName, params: stepParams } = step\n const { domain, user, data, variables, lng, logger } = context\n\n const instance: DataGoKrConnectionInstance = await ConnectionManager.getConnectionInstanceByName(\n domain,\n connectionName\n )\n if (!instance?.client) {\n throw new Error(`data-go-kr connection '${connectionName}' is not established.`)\n }\n\n const operationRef = stepParams?.operationRef\n if (!operationRef || typeof operationRef !== 'string') {\n throw new Error('data-go-kr-call: operationRef is required (format: \"<serviceId>.<operationId>\")')\n }\n\n const resolved = SpecRegistry.resolve(operationRef)\n const rawParams = stepParams?.params || {}\n\n const scope = { domain, user, lng, data, variables, console }\n const evalValue = (value: any) => {\n if (value === undefined || value === null) return value\n if (typeof value !== 'string') return value\n try {\n return evaluateTemplate(value, scope)\n } catch (err) {\n throw new Error(`data-go-kr-call: failed to evaluate parameter: ${(err as Error).message}`)\n }\n }\n\n const evaluatedParams: Record<string, any> = {}\n for (const [k, v] of Object.entries(rawParams)) {\n evaluatedParams[k] = evalValue(v)\n }\n\n const { service, operation } = resolved || { service: null, operation: null }\n const finalParams = operation\n ? normalizeAndValidateParams(operation, evaluatedParams)\n : evaluatedParams\n\n const servicePath = resolved\n ? `${service!.serviceId}/${operation!.operationId}`\n : operationRef.replace('.', '/')\n\n logger?.info?.(`[data-go-kr] call ${servicePath} params=${JSON.stringify(finalParams)}`)\n\n const response = await instance.client.get(servicePath, finalParams)\n return { data: response }\n}\n\n/**\n * 스펙 기반 파라미터 정규화·검증.\n *\n * 1. required 필수값 체크\n * 2. default 기본값 주입\n * 3. pad zero-padding (예: bun=6 → \"0006\")\n * 4. length 정확 길이 검증\n * 5. enum 허용 값 검증\n */\nfunction normalizeAndValidateParams(operation: OperationSpec, input: Record<string, any>): Record<string, any> {\n const output: Record<string, any> = {}\n const errors: string[] = []\n\n for (const p of operation.params) {\n let value = input[p.name]\n\n if (value === undefined || value === null || value === '') {\n if (p.default !== undefined) {\n value = p.default\n } else if (p.required) {\n errors.push(`'${p.name}' is required (${p.description})`)\n continue\n } else {\n continue\n }\n }\n\n value = String(value)\n\n if (p.pad && value.length < p.pad) {\n value = '0'.repeat(p.pad - value.length) + value\n }\n\n if (p.length !== undefined && value.length !== p.length) {\n errors.push(\n `'${p.name}' must be exactly ${p.length} characters (got \"${value}\", length ${value.length})`\n )\n continue\n }\n\n if (p.enum && !p.enum.some(e => e.value === value)) {\n errors.push(\n `'${p.name}' must be one of [${p.enum.map(e => e.value).join(', ')}] (got \"${value}\")`\n )\n continue\n }\n\n output[p.name] = value\n }\n\n /* 스펙에 없는 파라미터도 pass-through (미등록 필드 허용) */\n for (const [k, v] of Object.entries(input)) {\n if (!(k in output) && !operation.params.find(p => p.name === k)) {\n if (v !== undefined && v !== null && v !== '') {\n output[k] = v\n }\n }\n }\n\n if (errors.length > 0) {\n throw new Error(\n `data-go-kr-call parameter validation failed for ${operation.operationId}:\\n - ${errors.join('\\n - ')}`\n )\n }\n\n return output\n}\n\n/**\n * SpecRegistry 에 등록된 모든 오퍼레이션을 (operationRef → display) 옵션으로 펼친다.\n * 모듈 로딩 시 한 번 평가되며, 이후 register() 로 추가된 spec 은 반영되지 않음\n * (드문 케이스라 수용 — 향후 동적 parameterSpec 도입 시 해결).\n */\nfunction buildOperationRefOptions(): Array<{ value: string; display: string }> {\n return SpecRegistry.listServices()\n .flatMap(svc =>\n Object.values(svc.operations).map(op => ({\n value: `${svc.serviceId}.${op.operationId}`,\n display: `[${svc.label}] ${op.label}`\n }))\n )\n .sort((a, b) => a.display.localeCompare(b.display, 'ko'))\n}\n\nDataGoKrCall.parameterSpec = [\n {\n type: 'select',\n name: 'operationRef',\n label: 'label.operation-ref',\n property: {\n options: buildOperationRefOptions()\n }\n },\n {\n type: 'scenario-step-input',\n name: 'params',\n label: 'label.operation-params',\n placeholder:\n '오퍼레이션 파라미터를 JSON 객체로. 예) 건축물대장: {\"sigunguCd\":\"11680\",\"bjdongCd\":\"10300\",\"bun\":\"223\"} · 단기예보: {\"base_date\":\"20260425\",\"base_time\":\"1100\",\"nx\":\"60\",\"ny\":\"127\"}. 정확한 키는 helps/integration/services/<domain>.md 참고.'\n }\n]\n\nDataGoKrCall.help = 'integration/task/data-go-kr-call'\n\nTaskRegistry.registerTaskHandler('data-go-kr-call', DataGoKrCall)\n\nexport default DataGoKrCall\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 태스크 배럴.
|
|
3
|
+
*
|
|
4
|
+
* 구성 규약:
|
|
5
|
+
* - 범용 태스크 (여러 서비스에 공통 적용) 는 이 폴더 **최상단** 에 파일로 배치
|
|
6
|
+
* 예) call.ts → data-go-kr-call
|
|
7
|
+
*
|
|
8
|
+
* - 특정 도메인/서비스의 전용 태스크는 **도메인 이름 폴더** 아래 배치
|
|
9
|
+
* 예) building-ledger/ , (향후) property-trade/ , address/ ...
|
|
10
|
+
*
|
|
11
|
+
* 새 서비스 지원 우선순위:
|
|
12
|
+
* 1) 먼저 SpecRegistry 에 서비스 스펙을 등록 → `data-go-kr-call` 로 즉시 호출 가능
|
|
13
|
+
* 2) 그래도 불충분한 경우(입력 폼 필요, 응답 구조가 비표준, 도메인 가공 필수 등)만
|
|
14
|
+
* 도메인 폴더를 새로 만들고 전용 태스크 작성
|
|
15
|
+
*
|
|
16
|
+
* 즉 이 폴더에 새 파일/폴더가 추가되는 것은 "공통 경로로 풀 수 없는 예외" 뿐이다.
|
|
17
|
+
*/
|
|
18
|
+
import './call';
|
|
19
|
+
import './building-ledger';
|
|
20
|
+
import './weather';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* 태스크 배럴.
|
|
5
|
+
*
|
|
6
|
+
* 구성 규약:
|
|
7
|
+
* - 범용 태스크 (여러 서비스에 공통 적용) 는 이 폴더 **최상단** 에 파일로 배치
|
|
8
|
+
* 예) call.ts → data-go-kr-call
|
|
9
|
+
*
|
|
10
|
+
* - 특정 도메인/서비스의 전용 태스크는 **도메인 이름 폴더** 아래 배치
|
|
11
|
+
* 예) building-ledger/ , (향후) property-trade/ , address/ ...
|
|
12
|
+
*
|
|
13
|
+
* 새 서비스 지원 우선순위:
|
|
14
|
+
* 1) 먼저 SpecRegistry 에 서비스 스펙을 등록 → `data-go-kr-call` 로 즉시 호출 가능
|
|
15
|
+
* 2) 그래도 불충분한 경우(입력 폼 필요, 응답 구조가 비표준, 도메인 가공 필수 등)만
|
|
16
|
+
* 도메인 폴더를 새로 만들고 전용 태스크 작성
|
|
17
|
+
*
|
|
18
|
+
* 즉 이 폴더에 새 파일/폴더가 추가되는 것은 "공통 경로로 풀 수 없는 예외" 뿐이다.
|
|
19
|
+
*/
|
|
20
|
+
require("./call");
|
|
21
|
+
require("./building-ledger");
|
|
22
|
+
require("./weather");
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/engine/task/index.ts"],"names":[],"mappings":";;AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,kBAAe;AACf,6BAA0B;AAC1B,qBAAkB","sourcesContent":["/**\n * 태스크 배럴.\n *\n * 구성 규약:\n * - 범용 태스크 (여러 서비스에 공통 적용) 는 이 폴더 **최상단** 에 파일로 배치\n * 예) call.ts → data-go-kr-call\n *\n * - 특정 도메인/서비스의 전용 태스크는 **도메인 이름 폴더** 아래 배치\n * 예) building-ledger/ , (향후) property-trade/ , address/ ...\n *\n * 새 서비스 지원 우선순위:\n * 1) 먼저 SpecRegistry 에 서비스 스펙을 등록 → `data-go-kr-call` 로 즉시 호출 가능\n * 2) 그래도 불충분한 경우(입력 폼 필요, 응답 구조가 비표준, 도메인 가공 필수 등)만\n * 도메인 폴더를 새로 만들고 전용 태스크 작성\n *\n * 즉 이 폴더에 새 파일/폴더가 추가되는 것은 \"공통 경로로 풀 수 없는 예외\" 뿐이다.\n */\nimport './call'\nimport './building-ledger'\nimport './weather'\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KMA(기상청) 5km 격자좌표 ↔ 위경도 변환.
|
|
3
|
+
*
|
|
4
|
+
* 동네예보·초단기 시리즈는 위경도가 아닌 KMA 자체 격자(nx, ny) 로 호출하므로,
|
|
5
|
+
* 일반적으로 알고 있는 위경도 좌표를 격자로 바꿔주는 헬퍼가 필요하다.
|
|
6
|
+
*
|
|
7
|
+
* 변환식 출처: 기상청 공식 동네예보 격자 변환 문서 (Lambert Conformal Conic).
|
|
8
|
+
*/
|
|
9
|
+
export interface LatLon {
|
|
10
|
+
/** 위도 (degree). */
|
|
11
|
+
latitude: number;
|
|
12
|
+
/** 경도 (degree). */
|
|
13
|
+
longitude: number;
|
|
14
|
+
}
|
|
15
|
+
export interface KmaGrid {
|
|
16
|
+
/** 격자 X. */
|
|
17
|
+
nx: number;
|
|
18
|
+
/** 격자 Y. */
|
|
19
|
+
ny: number;
|
|
20
|
+
}
|
|
21
|
+
/** 위경도 → KMA 5km 격자좌표 (nx, ny). */
|
|
22
|
+
export declare function latLonToGrid(input: LatLon): KmaGrid;
|
|
23
|
+
/** KMA 5km 격자좌표 → 위경도 (격자 중심점). */
|
|
24
|
+
export declare function gridToLatLon(input: KmaGrid): LatLon;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* KMA(기상청) 5km 격자좌표 ↔ 위경도 변환.
|
|
4
|
+
*
|
|
5
|
+
* 동네예보·초단기 시리즈는 위경도가 아닌 KMA 자체 격자(nx, ny) 로 호출하므로,
|
|
6
|
+
* 일반적으로 알고 있는 위경도 좌표를 격자로 바꿔주는 헬퍼가 필요하다.
|
|
7
|
+
*
|
|
8
|
+
* 변환식 출처: 기상청 공식 동네예보 격자 변환 문서 (Lambert Conformal Conic).
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.latLonToGrid = latLonToGrid;
|
|
12
|
+
exports.gridToLatLon = gridToLatLon;
|
|
13
|
+
const RE = 6371.00877; /* 지구 반경 (km) */
|
|
14
|
+
const GRID = 5.0; /* 격자 간격 (km) */
|
|
15
|
+
const SLAT1 = 30.0; /* 표준 위도 1 (degree) */
|
|
16
|
+
const SLAT2 = 60.0; /* 표준 위도 2 (degree) */
|
|
17
|
+
const OLON = 126.0; /* 기준점 경도 (degree) */
|
|
18
|
+
const OLAT = 38.0; /* 기준점 위도 (degree) */
|
|
19
|
+
const XO = 43; /* 기준점 X좌표 (격자 단위) */
|
|
20
|
+
const YO = 136; /* 기준점 Y좌표 (격자 단위) */
|
|
21
|
+
const DEGRAD = Math.PI / 180.0;
|
|
22
|
+
const RADDEG = 180.0 / Math.PI;
|
|
23
|
+
const re = RE / GRID;
|
|
24
|
+
const slat1 = SLAT1 * DEGRAD;
|
|
25
|
+
const slat2 = SLAT2 * DEGRAD;
|
|
26
|
+
const olon = OLON * DEGRAD;
|
|
27
|
+
const olat = OLAT * DEGRAD;
|
|
28
|
+
const sn = Math.log(Math.cos(slat1) / Math.cos(slat2)) /
|
|
29
|
+
Math.log(Math.tan(Math.PI * 0.25 + slat2 * 0.5) / Math.tan(Math.PI * 0.25 + slat1 * 0.5));
|
|
30
|
+
const sf = (Math.pow(Math.tan(Math.PI * 0.25 + slat1 * 0.5), sn) * Math.cos(slat1)) / sn;
|
|
31
|
+
const ro = (re * sf) / Math.pow(Math.tan(Math.PI * 0.25 + olat * 0.5), sn);
|
|
32
|
+
/** 위경도 → KMA 5km 격자좌표 (nx, ny). */
|
|
33
|
+
function latLonToGrid(input) {
|
|
34
|
+
const ra = (re * sf) / Math.pow(Math.tan(Math.PI * 0.25 + input.latitude * DEGRAD * 0.5), sn);
|
|
35
|
+
let theta = input.longitude * DEGRAD - olon;
|
|
36
|
+
if (theta > Math.PI)
|
|
37
|
+
theta -= 2.0 * Math.PI;
|
|
38
|
+
if (theta < -Math.PI)
|
|
39
|
+
theta += 2.0 * Math.PI;
|
|
40
|
+
theta *= sn;
|
|
41
|
+
const x = ra * Math.sin(theta) + XO;
|
|
42
|
+
const y = ro - ra * Math.cos(theta) + YO;
|
|
43
|
+
return { nx: Math.floor(x + 1.5), ny: Math.floor(y + 1.5) };
|
|
44
|
+
}
|
|
45
|
+
/** KMA 5km 격자좌표 → 위경도 (격자 중심점). */
|
|
46
|
+
function gridToLatLon(input) {
|
|
47
|
+
const xn = input.nx - XO;
|
|
48
|
+
const yn = ro - (input.ny - YO);
|
|
49
|
+
let ra = Math.sqrt(xn * xn + yn * yn);
|
|
50
|
+
if (sn < 0.0)
|
|
51
|
+
ra = -ra;
|
|
52
|
+
let alat = Math.pow((re * sf) / ra, 1.0 / sn);
|
|
53
|
+
alat = 2.0 * Math.atan(alat) - Math.PI * 0.5;
|
|
54
|
+
let theta;
|
|
55
|
+
if (Math.abs(xn) <= 0.0) {
|
|
56
|
+
theta = 0.0;
|
|
57
|
+
}
|
|
58
|
+
else if (Math.abs(yn) <= 0.0) {
|
|
59
|
+
theta = Math.PI * 0.5;
|
|
60
|
+
if (xn < 0.0)
|
|
61
|
+
theta = -theta;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
theta = Math.atan2(xn, yn);
|
|
65
|
+
}
|
|
66
|
+
const alon = theta / sn + olon;
|
|
67
|
+
return { latitude: alat * RADDEG, longitude: alon * RADDEG };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=grid-converter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grid-converter.js","sourceRoot":"","sources":["../../../../server/engine/task/weather/grid-converter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * KMA(기상청) 5km 격자좌표 ↔ 위경도 변환.\n *\n * 동네예보·초단기 시리즈는 위경도가 아닌 KMA 자체 격자(nx, ny) 로 호출하므로,\n * 일반적으로 알고 있는 위경도 좌표를 격자로 바꿔주는 헬퍼가 필요하다.\n *\n * 변환식 출처: 기상청 공식 동네예보 격자 변환 문서 (Lambert Conformal Conic).\n */\n\nconst RE = 6371.00877 /* 지구 반경 (km) */\nconst GRID = 5.0 /* 격자 간격 (km) */\nconst SLAT1 = 30.0 /* 표준 위도 1 (degree) */\nconst SLAT2 = 60.0 /* 표준 위도 2 (degree) */\nconst OLON = 126.0 /* 기준점 경도 (degree) */\nconst OLAT = 38.0 /* 기준점 위도 (degree) */\nconst XO = 43 /* 기준점 X좌표 (격자 단위) */\nconst YO = 136 /* 기준점 Y좌표 (격자 단위) */\n\nconst DEGRAD = Math.PI / 180.0\nconst RADDEG = 180.0 / Math.PI\n\nconst re = RE / GRID\nconst slat1 = SLAT1 * DEGRAD\nconst slat2 = SLAT2 * DEGRAD\nconst olon = OLON * DEGRAD\nconst olat = OLAT * DEGRAD\n\nconst sn =\n Math.log(Math.cos(slat1) / Math.cos(slat2)) /\n Math.log(Math.tan(Math.PI * 0.25 + slat2 * 0.5) / Math.tan(Math.PI * 0.25 + slat1 * 0.5))\nconst sf = (Math.pow(Math.tan(Math.PI * 0.25 + slat1 * 0.5), sn) * Math.cos(slat1)) / sn\nconst ro = (re * sf) / Math.pow(Math.tan(Math.PI * 0.25 + olat * 0.5), sn)\n\nexport interface LatLon {\n /** 위도 (degree). */\n latitude: number\n /** 경도 (degree). */\n longitude: number\n}\n\nexport interface KmaGrid {\n /** 격자 X. */\n nx: number\n /** 격자 Y. */\n ny: number\n}\n\n/** 위경도 → KMA 5km 격자좌표 (nx, ny). */\nexport function latLonToGrid(input: LatLon): KmaGrid {\n const ra = (re * sf) / Math.pow(Math.tan(Math.PI * 0.25 + input.latitude * DEGRAD * 0.5), sn)\n let theta = input.longitude * DEGRAD - olon\n if (theta > Math.PI) theta -= 2.0 * Math.PI\n if (theta < -Math.PI) theta += 2.0 * Math.PI\n theta *= sn\n const x = ra * Math.sin(theta) + XO\n const y = ro - ra * Math.cos(theta) + YO\n return { nx: Math.floor(x + 1.5), ny: Math.floor(y + 1.5) }\n}\n\n/** KMA 5km 격자좌표 → 위경도 (격자 중심점). */\nexport function gridToLatLon(input: KmaGrid): LatLon {\n const xn = input.nx - XO\n const yn = ro - (input.ny - YO)\n let ra = Math.sqrt(xn * xn + yn * yn)\n if (sn < 0.0) ra = -ra\n let alat = Math.pow((re * sf) / ra, 1.0 / sn)\n alat = 2.0 * Math.atan(alat) - Math.PI * 0.5\n let theta: number\n if (Math.abs(xn) <= 0.0) {\n theta = 0.0\n } else if (Math.abs(yn) <= 0.0) {\n theta = Math.PI * 0.5\n if (xn < 0.0) theta = -theta\n } else {\n theta = Math.atan2(xn, yn)\n }\n const alon = theta / sn + olon\n return { latitude: alat * RADDEG, longitude: alon * RADDEG }\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 기상청(KMA) 도메인 전용 태스크 그룹.
|
|
3
|
+
*
|
|
4
|
+
* 새 KMA 전용 태스크(예: AWS 자동수집, 특보 알림 등) 추가 시:
|
|
5
|
+
* - 본 폴더에 `<variant>.ts` 파일 추가 → TaskRegistry 등록
|
|
6
|
+
* - 본 index 에 `import './<variant>'` 한 줄 추가
|
|
7
|
+
*
|
|
8
|
+
* 단순 조회는 범용 `data-go-kr-call` + 스펙 레지스트리로 충분하므로,
|
|
9
|
+
* 이 폴더에는 입력 폼·헬퍼·도메인 가공이 가치 있는 태스크만 배치.
|
|
10
|
+
*/
|
|
11
|
+
import './short-term-forecast';
|
|
12
|
+
export { latLonToGrid, gridToLatLon } from './grid-converter';
|
|
13
|
+
export type { LatLon, KmaGrid } from './grid-converter';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.gridToLatLon = exports.latLonToGrid = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 기상청(KMA) 도메인 전용 태스크 그룹.
|
|
6
|
+
*
|
|
7
|
+
* 새 KMA 전용 태스크(예: AWS 자동수집, 특보 알림 등) 추가 시:
|
|
8
|
+
* - 본 폴더에 `<variant>.ts` 파일 추가 → TaskRegistry 등록
|
|
9
|
+
* - 본 index 에 `import './<variant>'` 한 줄 추가
|
|
10
|
+
*
|
|
11
|
+
* 단순 조회는 범용 `data-go-kr-call` + 스펙 레지스트리로 충분하므로,
|
|
12
|
+
* 이 폴더에는 입력 폼·헬퍼·도메인 가공이 가치 있는 태스크만 배치.
|
|
13
|
+
*/
|
|
14
|
+
require("./short-term-forecast");
|
|
15
|
+
var grid_converter_1 = require("./grid-converter");
|
|
16
|
+
Object.defineProperty(exports, "latLonToGrid", { enumerable: true, get: function () { return grid_converter_1.latLonToGrid; } });
|
|
17
|
+
Object.defineProperty(exports, "gridToLatLon", { enumerable: true, get: function () { return grid_converter_1.gridToLatLon; } });
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../server/engine/task/weather/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;GASG;AACH,iCAA8B;AAE9B,mDAA6D;AAApD,8GAAA,YAAY,OAAA;AAAE,8GAAA,YAAY,OAAA","sourcesContent":["/**\n * 기상청(KMA) 도메인 전용 태스크 그룹.\n *\n * 새 KMA 전용 태스크(예: AWS 자동수집, 특보 알림 등) 추가 시:\n * - 본 폴더에 `<variant>.ts` 파일 추가 → TaskRegistry 등록\n * - 본 index 에 `import './<variant>'` 한 줄 추가\n *\n * 단순 조회는 범용 `data-go-kr-call` + 스펙 레지스트리로 충분하므로,\n * 이 폴더에는 입력 폼·헬퍼·도메인 가공이 가치 있는 태스크만 배치.\n */\nimport './short-term-forecast'\n\nexport { latLonToGrid, gridToLatLon } from './grid-converter'\nexport type { LatLon, KmaGrid } from './grid-converter'\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Context } from '@things-factory/integration-base';
|
|
2
|
+
/**
|
|
3
|
+
* 기상청 단기예보 (`getVilageFcst`) 조회 태스크.
|
|
4
|
+
*
|
|
5
|
+
* 입력 폼이 명시된 전용 태스크. 위경도 입력 시 KMA 격자로 자동 변환.
|
|
6
|
+
*
|
|
7
|
+
* 입력 (둘 중 하나):
|
|
8
|
+
* - `nx`, `ny` : KMA 5km 격자좌표
|
|
9
|
+
* - `latitude`, `longitude`: 위경도 (자동으로 nx/ny 로 변환)
|
|
10
|
+
*
|
|
11
|
+
* 추가:
|
|
12
|
+
* - `baseDate` : 발표일자 YYYYMMDD (필수)
|
|
13
|
+
* - `baseTime` : 발표시각 HHMM (필수, 0200/0500/0800/1100/1400/1700/2000/2300)
|
|
14
|
+
* - `numOfRows` : 페이지당 결과수 (기본 1000 — 3일치 카테고리×시점 합계 약 800+)
|
|
15
|
+
*
|
|
16
|
+
* 다른 단기예보 오퍼레이션(getUltraSrtFcst, getUltraSrtNcst) 은 동일 서비스 안에 있으므로
|
|
17
|
+
* 범용 `data-go-kr-call` 로 호출 권장.
|
|
18
|
+
*/
|
|
19
|
+
declare function ShortTermForecastTask(step: any, context: Context): Promise<{
|
|
20
|
+
data: {
|
|
21
|
+
items: any[];
|
|
22
|
+
};
|
|
23
|
+
}>;
|
|
24
|
+
declare namespace ShortTermForecastTask {
|
|
25
|
+
var parameterSpec: ({
|
|
26
|
+
type: string;
|
|
27
|
+
name: string;
|
|
28
|
+
label: string;
|
|
29
|
+
placeholder: string;
|
|
30
|
+
} | {
|
|
31
|
+
type: string;
|
|
32
|
+
name: string;
|
|
33
|
+
label: string;
|
|
34
|
+
placeholder?: undefined;
|
|
35
|
+
})[];
|
|
36
|
+
var help: string;
|
|
37
|
+
}
|
|
38
|
+
export default ShortTermForecastTask;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const integration_base_1 = require("@things-factory/integration-base");
|
|
4
|
+
const short_term_forecast_1 = require("../../spec/weather/short-term-forecast");
|
|
5
|
+
const grid_converter_1 = require("./grid-converter");
|
|
6
|
+
/**
|
|
7
|
+
* 기상청 단기예보 (`getVilageFcst`) 조회 태스크.
|
|
8
|
+
*
|
|
9
|
+
* 입력 폼이 명시된 전용 태스크. 위경도 입력 시 KMA 격자로 자동 변환.
|
|
10
|
+
*
|
|
11
|
+
* 입력 (둘 중 하나):
|
|
12
|
+
* - `nx`, `ny` : KMA 5km 격자좌표
|
|
13
|
+
* - `latitude`, `longitude`: 위경도 (자동으로 nx/ny 로 변환)
|
|
14
|
+
*
|
|
15
|
+
* 추가:
|
|
16
|
+
* - `baseDate` : 발표일자 YYYYMMDD (필수)
|
|
17
|
+
* - `baseTime` : 발표시각 HHMM (필수, 0200/0500/0800/1100/1400/1700/2000/2300)
|
|
18
|
+
* - `numOfRows` : 페이지당 결과수 (기본 1000 — 3일치 카테고리×시점 합계 약 800+)
|
|
19
|
+
*
|
|
20
|
+
* 다른 단기예보 오퍼레이션(getUltraSrtFcst, getUltraSrtNcst) 은 동일 서비스 안에 있으므로
|
|
21
|
+
* 범용 `data-go-kr-call` 로 호출 권장.
|
|
22
|
+
*/
|
|
23
|
+
async function ShortTermForecastTask(step, context) {
|
|
24
|
+
const { connection: connectionName, params } = step;
|
|
25
|
+
const { domain, user, data, variables, lng, logger } = context;
|
|
26
|
+
const instance = await integration_base_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
|
|
27
|
+
if (!instance?.client) {
|
|
28
|
+
throw new Error(`data-go-kr connection '${connectionName}' is not established.`);
|
|
29
|
+
}
|
|
30
|
+
const scope = { domain, user, lng, data, variables, console };
|
|
31
|
+
const evalString = (value) => {
|
|
32
|
+
if (value === undefined || value === null || value === '')
|
|
33
|
+
return undefined;
|
|
34
|
+
try {
|
|
35
|
+
return (0, integration_base_1.evaluateTemplate)(String(value), scope);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
throw new Error(`weather-short-term-forecast: failed to evaluate parameter: ${err.message}`);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const evalNumber = (value) => {
|
|
42
|
+
const s = evalString(value);
|
|
43
|
+
if (s === undefined)
|
|
44
|
+
return undefined;
|
|
45
|
+
const n = Number(s);
|
|
46
|
+
return Number.isFinite(n) ? n : undefined;
|
|
47
|
+
};
|
|
48
|
+
const baseDate = evalString(params.baseDate);
|
|
49
|
+
const baseTime = evalString(params.baseTime);
|
|
50
|
+
if (!baseDate)
|
|
51
|
+
throw new Error('weather-short-term-forecast: baseDate is required');
|
|
52
|
+
if (!baseTime)
|
|
53
|
+
throw new Error('weather-short-term-forecast: baseTime is required');
|
|
54
|
+
const explicitNx = evalNumber(params.nx);
|
|
55
|
+
const explicitNy = evalNumber(params.ny);
|
|
56
|
+
const lat = evalNumber(params.latitude);
|
|
57
|
+
const lon = evalNumber(params.longitude);
|
|
58
|
+
let nx;
|
|
59
|
+
let ny;
|
|
60
|
+
if (explicitNx !== undefined && explicitNy !== undefined) {
|
|
61
|
+
nx = explicitNx;
|
|
62
|
+
ny = explicitNy;
|
|
63
|
+
}
|
|
64
|
+
else if (lat !== undefined && lon !== undefined) {
|
|
65
|
+
const grid = (0, grid_converter_1.latLonToGrid)({ latitude: lat, longitude: lon });
|
|
66
|
+
nx = grid.nx;
|
|
67
|
+
ny = grid.ny;
|
|
68
|
+
logger?.info?.(`[data-go-kr] latLonToGrid lat=${lat} lon=${lon} → nx=${nx} ny=${ny}`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
throw new Error('weather-short-term-forecast: provide either (nx, ny) or (latitude, longitude). 둘 다 비어있음.');
|
|
72
|
+
}
|
|
73
|
+
const servicePath = `${short_term_forecast_1.KmaShortTermForecastService.serviceId}/getVilageFcst`;
|
|
74
|
+
const queryParams = {
|
|
75
|
+
base_date: baseDate,
|
|
76
|
+
base_time: baseTime,
|
|
77
|
+
nx,
|
|
78
|
+
ny,
|
|
79
|
+
numOfRows: evalNumber(params.numOfRows) ?? 1000,
|
|
80
|
+
pageNo: evalNumber(params.pageNo) ?? 1
|
|
81
|
+
};
|
|
82
|
+
logger?.info?.(`[data-go-kr] ${servicePath} params=${JSON.stringify(queryParams)}`);
|
|
83
|
+
const items = await instance.client.getItems(servicePath, queryParams);
|
|
84
|
+
return { data: { items } };
|
|
85
|
+
}
|
|
86
|
+
ShortTermForecastTask.parameterSpec = [
|
|
87
|
+
{ type: 'string', name: 'baseDate', label: 'label.base-date', placeholder: '예) 20260425' },
|
|
88
|
+
{ type: 'string', name: 'baseTime', label: 'label.base-time', placeholder: '예) 1100' },
|
|
89
|
+
{ type: 'string', name: 'nx', label: 'label.nx', placeholder: '예) 60' },
|
|
90
|
+
{ type: 'string', name: 'ny', label: 'label.ny', placeholder: '예) 127' },
|
|
91
|
+
{ type: 'string', name: 'latitude', label: 'label.latitude', placeholder: '위경도 입력 시 nx/ny 자동 계산' },
|
|
92
|
+
{ type: 'string', name: 'longitude', label: 'label.longitude' },
|
|
93
|
+
{ type: 'string', name: 'numOfRows', label: 'label.num-of-rows', placeholder: '기본 1000' },
|
|
94
|
+
{ type: 'string', name: 'pageNo', label: 'label.page-no', placeholder: '기본 1' }
|
|
95
|
+
];
|
|
96
|
+
ShortTermForecastTask.help = 'integration/task/weather-short-term-forecast';
|
|
97
|
+
integration_base_1.TaskRegistry.registerTaskHandler('data-go-kr-weather-short-term-forecast', ShortTermForecastTask);
|
|
98
|
+
exports.default = ShortTermForecastTask;
|
|
99
|
+
//# sourceMappingURL=short-term-forecast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"short-term-forecast.js","sourceRoot":"","sources":["../../../../server/engine/task/weather/short-term-forecast.ts"],"names":[],"mappings":"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","sourcesContent":["import { ConnectionManager, Context, TaskRegistry, evaluateTemplate } from '@things-factory/integration-base'\n\nimport { DataGoKrConnectionInstance } from '../../connector/data-go-kr-connector'\nimport { KmaShortTermForecastService } from '../../spec/weather/short-term-forecast'\nimport { latLonToGrid } from './grid-converter'\n\n/**\n * 기상청 단기예보 (`getVilageFcst`) 조회 태스크.\n *\n * 입력 폼이 명시된 전용 태스크. 위경도 입력 시 KMA 격자로 자동 변환.\n *\n * 입력 (둘 중 하나):\n * - `nx`, `ny` : KMA 5km 격자좌표\n * - `latitude`, `longitude`: 위경도 (자동으로 nx/ny 로 변환)\n *\n * 추가:\n * - `baseDate` : 발표일자 YYYYMMDD (필수)\n * - `baseTime` : 발표시각 HHMM (필수, 0200/0500/0800/1100/1400/1700/2000/2300)\n * - `numOfRows` : 페이지당 결과수 (기본 1000 — 3일치 카테고리×시점 합계 약 800+)\n *\n * 다른 단기예보 오퍼레이션(getUltraSrtFcst, getUltraSrtNcst) 은 동일 서비스 안에 있으므로\n * 범용 `data-go-kr-call` 로 호출 권장.\n */\nasync function ShortTermForecastTask(step, context: Context): Promise<{ data: { items: any[] } }> {\n const { connection: connectionName, params } = step\n const { domain, user, data, variables, lng, logger } = context\n\n const instance: DataGoKrConnectionInstance = await ConnectionManager.getConnectionInstanceByName(\n domain,\n connectionName\n )\n if (!instance?.client) {\n throw new Error(`data-go-kr connection '${connectionName}' is not established.`)\n }\n\n const scope = { domain, user, lng, data, variables, console }\n const evalString = (value: string | undefined) => {\n if (value === undefined || value === null || value === '') return undefined\n try {\n return evaluateTemplate(String(value), scope)\n } catch (err) {\n throw new Error(`weather-short-term-forecast: failed to evaluate parameter: ${(err as Error).message}`)\n }\n }\n const evalNumber = (value: any): number | undefined => {\n const s = evalString(value as string | undefined)\n if (s === undefined) return undefined\n const n = Number(s)\n return Number.isFinite(n) ? n : undefined\n }\n\n const baseDate = evalString(params.baseDate)\n const baseTime = evalString(params.baseTime)\n if (!baseDate) throw new Error('weather-short-term-forecast: baseDate is required')\n if (!baseTime) throw new Error('weather-short-term-forecast: baseTime is required')\n\n const explicitNx = evalNumber(params.nx)\n const explicitNy = evalNumber(params.ny)\n const lat = evalNumber(params.latitude)\n const lon = evalNumber(params.longitude)\n\n let nx: number\n let ny: number\n if (explicitNx !== undefined && explicitNy !== undefined) {\n nx = explicitNx\n ny = explicitNy\n } else if (lat !== undefined && lon !== undefined) {\n const grid = latLonToGrid({ latitude: lat, longitude: lon })\n nx = grid.nx\n ny = grid.ny\n logger?.info?.(`[data-go-kr] latLonToGrid lat=${lat} lon=${lon} → nx=${nx} ny=${ny}`)\n } else {\n throw new Error(\n 'weather-short-term-forecast: provide either (nx, ny) or (latitude, longitude). 둘 다 비어있음.'\n )\n }\n\n const servicePath = `${KmaShortTermForecastService.serviceId}/getVilageFcst`\n const queryParams = {\n base_date: baseDate,\n base_time: baseTime,\n nx,\n ny,\n numOfRows: evalNumber(params.numOfRows) ?? 1000,\n pageNo: evalNumber(params.pageNo) ?? 1\n }\n\n logger?.info?.(`[data-go-kr] ${servicePath} params=${JSON.stringify(queryParams)}`)\n\n const items = await instance.client.getItems<any>(servicePath, queryParams)\n\n return { data: { items } }\n}\n\nShortTermForecastTask.parameterSpec = [\n { type: 'string', name: 'baseDate', label: 'label.base-date', placeholder: '예) 20260425' },\n { type: 'string', name: 'baseTime', label: 'label.base-time', placeholder: '예) 1100' },\n { type: 'string', name: 'nx', label: 'label.nx', placeholder: '예) 60' },\n { type: 'string', name: 'ny', label: 'label.ny', placeholder: '예) 127' },\n { type: 'string', name: 'latitude', label: 'label.latitude', placeholder: '위경도 입력 시 nx/ny 자동 계산' },\n { type: 'string', name: 'longitude', label: 'label.longitude' },\n { type: 'string', name: 'numOfRows', label: 'label.num-of-rows', placeholder: '기본 1000' },\n { type: 'string', name: 'pageNo', label: 'label.page-no', placeholder: '기본 1' }\n]\n\nShortTermForecastTask.help = 'integration/task/weather-short-term-forecast'\n\nTaskRegistry.registerTaskHandler('data-go-kr-weather-short-term-forecast', ShortTermForecastTask)\n\nexport default ShortTermForecastTask\n"]}
|