Built the missing piece between the test datasheet pipeline and Dataforth's new product API. End-to-end: - Pulled DFWDS (Dataforth Web Datasheet System) VB6 source from AD1\Engineering\ENGR\ATE\Test Datasheets\DFWDS to local for analysis - Decoded its filename validation: A-J prefix decodes (A=10..J=19), all- numeric WO# valid (no leading 0), anything else bad - Ported the validation + move logic to Node (dfwds-process.js) - Built bulk uploader (upload-delta.js) for Hoffman's Swagger API (POST /api/v1/TestReportDataFiles/bulk with OAuth client_credentials) Sanitized 3 prior reference scripts (fetch-server-inventory, test-scenarios, test-upload-two) to read CF_* env vars instead of hardcoded creds. Live drain results: - 897 files moved Test_Datasheets -> For_Web (all valid, no renames, no bad), DFWDS port summary in 1.1s - Pushed entire For_Web (7,061 files) to Hoffman API in 49.7s @ 142/s: Created=803 Updated=114 Unchanged=6,144 Errors=0 - Server count: 489,579 -> 490,382 (+803 net new) Also: - Added clients/dataforth/.gitignore to exclude plaintext Oauth.txt note - Added clients/instrumental-music-center/docs/2026-04-13-ticket-notes.md (ticket write-up of 2026-04-11/12/13 IMC1 RDS removal/SQL migration work) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
48 lines
2.1 KiB
Python
48 lines
2.1 KiB
Python
"""Probe Dataforth API: get token, fetch Swagger, list relevant endpoints."""
|
|
import json, os, subprocess, urllib.request
|
|
import yaml
|
|
|
|
creds = yaml.safe_load(subprocess.run(['sops','-d','D:/vault/clients/dataforth/api-oauth.sops.yaml'],
|
|
capture_output=True, text=True, timeout=30, check=True).stdout)
|
|
TOKEN_URL = creds['endpoints']['token-url']
|
|
SWAGGER_JSON = creds['endpoints']['swagger-json']
|
|
API_BASE = creds['endpoints']['api-base']
|
|
C = creds['credentials']
|
|
|
|
# 1. Get token
|
|
print('[1] fetch OAuth token')
|
|
body = (f'grant_type={C["grant-type"]}&client_id={C["client-id"]}'
|
|
f'&client_secret={C["client-secret"]}&scope={C["scope"]}').encode()
|
|
req = urllib.request.Request(TOKEN_URL, data=body, method='POST',
|
|
headers={'Content-Type':'application/x-www-form-urlencoded'})
|
|
with urllib.request.urlopen(req, timeout=30) as r:
|
|
tok = json.loads(r.read())
|
|
print(f' access_token: {tok["access_token"][:30]}... (expires_in={tok.get("expires_in")}s)')
|
|
ACCESS = tok['access_token']
|
|
|
|
# 2. Pull swagger
|
|
print('\n[2] fetch swagger')
|
|
req = urllib.request.Request(SWAGGER_JSON,
|
|
headers={'Authorization': f'Bearer {ACCESS}'})
|
|
with urllib.request.urlopen(req, timeout=30) as r:
|
|
sw = json.loads(r.read())
|
|
print(f' title: {sw.get("info",{}).get("title")}, version: {sw.get("info",{}).get("version")}')
|
|
print(f' total paths: {len(sw.get("paths",{}))}')
|
|
|
|
# 3. Filter paths for upload/datasheet/file/test
|
|
print('\n[3] paths matching upload/datasheet/file/test/report:')
|
|
hits = []
|
|
for path, methods in sw.get('paths',{}).items():
|
|
pl = path.lower()
|
|
if any(k in pl for k in ('upload','datasheet','testreport','file','data')):
|
|
for m, op in methods.items():
|
|
if m.startswith('x-'): continue
|
|
hits.append((m.upper(), path, op.get('summary','') or op.get('operationId','')))
|
|
for m,p,desc in sorted(hits)[:40]:
|
|
print(f' {m:6} {p:60} {desc[:60]}')
|
|
|
|
# 4. Save full swagger for offline reading
|
|
out = r'D:\claudetools\projects\dataforth-dos\dfwds-research\swagger.json'
|
|
with open(out,'w',encoding='utf-8') as f: json.dump(sw, f, indent=2)
|
|
print(f'\nfull swagger saved: {out}')
|