// Package samgov is a typed, dependency-light client + normalizer for the SAM.gov Contract // Opportunities v2 search API (https://open.gsa.gov/api/get-opportunities-public-api/). // // It has NO database dependency: the client fetches and the normalizer maps SAM's JSON into a flat // Normalized record that the sync (cmd/sam-sync) writes to the cache. Only the built-in net/http is // used, no third-party HTTP library, and the api_key is [credential removed] passed as a query parameter from // env, never logged and never exposed to the browser. package samgov import "encoding/json" // SearchResult is one page of the SAM search response paired with the per-notice raw JSON. type SearchResult struct { TotalRecords int Limit int Offset int Fetched int // count SAM returned on this page (before skipping malformed notices); paginate by this Records []Record } // Record is a decoded opportunity plus the exact JSON SAM returned for it (persisted as raw). type Record struct { Op Opportunity Raw json.RawMessage } // rawPage decodes the envelope while keeping each opportunity as raw bytes so the full notice can be // stored verbatim (SAM occasionally adds fields we don't model). type rawPage struct { TotalRecords int `json:"totalRecords"` Limit int `json:"limit"` Offset int `json:"offset"` OpportunitiesData []json.RawMessage `json:"opportunitiesData"` } // Opportunity is the subset of SAM's notice fields we map. Unknown fields are preserved via Record.Raw. type Opportunity struct { NoticeID string `json:"noticeId"` Title string `json:"title"` SolicitationNumber string `json:"solicitationNumber"` FullParentPathName string `json:"fullParentPathName"` FullParentPathCode string `json:"fullParentPathCode"` PostedDate string `json:"postedDate"` Type string `json:"type"` BaseType string `json:"baseType"` TypeOfSetAside string `json:"typeOfSetAside"` TypeOfSetAsideDescription string `json:"typeOfSetAsideDescription"` ResponseDeadLine string `json:"responseDeadLine"` NaicsCode string `json:"naicsCode"` ClassificationCode string `json:"classificationCode"` Active string `json:"active"` // "Yes" / "No" Award json.RawMessage `json:"award"` PointOfContact []PointOfContact `json:"pointOfContact"` Description string `json:"description"` // a URL to the notice text OfficeAddress json.RawMessage `json:"officeAddress"` PlaceOfPerformance Place `json:"placeOfPerformance"` UILink string `json:"uiLink"` ResourceLinks []string `json:"resourceLinks"` } // PointOfContact is one SAM contact entry. type PointOfContact struct { Type string `json:"type"` FullName string `json:"fullName"` Title string `json:"title"` Email string `json:"email"` Phone string `json:"phone"` Fax string `json:"fax"` } // Place is SAM's placeOfPerformance. city/state are objects with name/code. type Place struct { City NameCode `json:"city"` State NameCode `json:"state"` Zip string `json:"zip"` Country NameCode `json:"country"` } // NameCode is SAM's recurring { code, name } shape. type NameCode struct { Code string `json:"code"` Name string `json:"name"` }