1
+ import { HapiServer } from "../types" ;
2
+ import { config } from "../plugins/utils/AdapterConfigurationSchema" ;
3
+ import Boom from "boom" ;
4
+ import wreck from "@hapi/wreck" ;
5
+
6
+ export interface FormHashResponse {
7
+ hash : string ;
8
+ }
9
+
10
+ export interface PublishedFormResponse {
11
+ configuration : any ;
12
+ hash : string ;
13
+ }
14
+
15
+ const LOGGER_DATA = {
16
+ class : "PreAwardApiService" ,
17
+ }
18
+
19
+ export class PreAwardApiService {
20
+ private logger : any ;
21
+ private apiBaseUrl : string ;
22
+ private wreck : typeof wreck ;
23
+
24
+ constructor ( server : HapiServer ) {
25
+ this . logger = server . logger ;
26
+ this . apiBaseUrl = config . formStoreApiHost ;
27
+ this . wreck = wreck . defaults ( {
28
+ timeout : 10000 ,
29
+ headers : {
30
+ 'accept' : 'application/json' ,
31
+ 'content-type' : 'application/json'
32
+ }
33
+ } ) ;
34
+ this . logger . info ( {
35
+ ...LOGGER_DATA ,
36
+ message : `Service initialized with base URL: ${ this . apiBaseUrl } `
37
+ } ) ;
38
+ }
39
+
40
+ /**
41
+ * Fetches the published form data including hash for a specific form.
42
+ * This is the primary method used by the cache service when a form
43
+ * is requested by a user. It only returns forms that have been
44
+ * explicitly published in the Pre-Award system.
45
+ */
46
+ async getPublishedForm ( name : string ) : Promise < PublishedFormResponse | null > {
47
+ const url = `${ this . apiBaseUrl } /${ name } /published` ;
48
+ this . logger . info ( {
49
+ ...LOGGER_DATA ,
50
+ message : `Fetching published form: ${ name } ` ,
51
+ url : url
52
+ } ) ;
53
+ try {
54
+ const { payload } = await this . wreck . get ( url , { json : true } ) ;
55
+ this . logger . info ( {
56
+ ...LOGGER_DATA ,
57
+ message : `Successfully fetched published form: ${ name } `
58
+ } ) ;
59
+ return payload as PublishedFormResponse ;
60
+ } catch ( error : any ) {
61
+ // Handle 404 - form doesn't exist or isn't published
62
+ if ( error . output ?. statusCode === 404 ) {
63
+ this . logger . info ( {
64
+ ...LOGGER_DATA ,
65
+ message : `Form ${ name } not found or not published in Pre-Award API`
66
+ } ) ;
67
+ return null ;
68
+ }
69
+ // Handle other errors (network, timeout, server errors)
70
+ this . logger . error ( {
71
+ ...LOGGER_DATA ,
72
+ message : `Failed to fetch published form ${ name } ` ,
73
+ error : error . message
74
+ } ) ;
75
+ // Don't expose internal error details to the client
76
+ throw Boom . serverUnavailable ( 'Pre-Award API is temporarily unavailable' ) ;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Fetches just the hash of a published form.
82
+ * This lightweight endpoint allows us to validate our cache without
83
+ * downloading the entire form definition. We use this for periodic
84
+ * cache freshness checks.
85
+ */
86
+ async getFormHash ( name : string ) : Promise < string | null > {
87
+ const url = `${ this . apiBaseUrl } /${ name } /hash` ;
88
+ try {
89
+ const { payload } = await this . wreck . get ( url , { json : true } ) ;
90
+ const data = payload as FormHashResponse ;
91
+ this . logger . debug ( {
92
+ ...LOGGER_DATA ,
93
+ message : `Retrieved hash for form ${ name } `
94
+ } ) ;
95
+ return data . hash ;
96
+ } catch ( error : any ) {
97
+ if ( error . output ?. statusCode === 404 ) {
98
+ // Form doesn't exist or isn't published - this is normal
99
+ return null ;
100
+ }
101
+ // For hash validation failures, we don't want to fail the entire request.
102
+ // We'll continue using the cached version and try again later.
103
+ this . logger . warn ( {
104
+ ...LOGGER_DATA ,
105
+ message : `Could not fetch hash for form ${ name } , will use cached version` ,
106
+ error : error . message
107
+ } ) ;
108
+ return null ;
109
+ }
110
+ }
111
+ }
0 commit comments