@@ -2,6 +2,7 @@ import { writeFile } from 'node:fs/promises';
22import { ApolloClient , InMemoryCache , type TypedDocumentNode , gql } from '@apollo/client' ;
33import { SetContextLink } from '@apollo/client/link/context' ;
44import { HttpLink } from '@apollo/client/link/http' ;
5+ import { AxiosError } from 'axios' ;
56import { isValidEin } from './ein.js' ;
67import { logger } from './logger.js' ;
78import { type AccessTokenSet , getToken , oidcOptions } from './oidc.js' ;
@@ -11,6 +12,7 @@ import {
1112 postChangemakerFieldValue ,
1213 postChangemakerFieldValueBatch ,
1314 postSource ,
15+ type WritableChangemakerFieldValue ,
1416} from './pdc-api.js' ;
1517import type { CommandModule } from 'yargs' ;
1618import type { Changemaker , ChangemakerBundle , Source } from '@pdc/sdk' ;
@@ -20,6 +22,8 @@ const JSON_SPACES = 2;
2022// Fixed page size for Charity Navigator GraphQL requests; a positive
2123// constant keeps perPage valid when the EIN list is empty (GLM-5.2).
2224const PER_PAGE = 100 ;
25+ // When `@pdc/http-status-codes` is ready (issues 18-20 solved), use it instead.
26+ const HTTP_STATUS_FORBIDDEN = 403 ;
2327
2428interface NonprofitPublic {
2529 ein : string ;
@@ -288,6 +292,26 @@ const getChangemakerByEin = (ein: string, changemakers: ChangemakerBundle): Chan
288292 throw new Error ( 'How could this have happened?' ) ;
289293} ;
290294
295+ /** Light wrapper around `postChangemakerFieldValue` that logs warning on HTTP 403 */
296+ const postChangemakerFieldValueWarnOnForbidden = async (
297+ baseUrl : string ,
298+ token : AccessTokenSet ,
299+ data : WritableChangemakerFieldValue ,
300+ warnedChangemakers : Set < number > , // Mutated! This is for observation/logs, not control!
301+ ) : Promise < void > => {
302+ try {
303+ const fieldValue = await postChangemakerFieldValue ( baseUrl , token , data ) ;
304+ logger . info ( `Added changemaker field value: ${ JSON . stringify ( fieldValue ) } ` ) ;
305+ } catch ( e : unknown ) {
306+ if ( e instanceof AxiosError && e . status === HTTP_STATUS_FORBIDDEN ) {
307+ logger . warn ( `No permission (403) to create ${ JSON . stringify ( data ) } ` ) ;
308+ warnedChangemakers . add ( data . changemakerId ) ;
309+ } else {
310+ throw e ;
311+ }
312+ }
313+ } ;
314+
291315const lookupFromPdcCommand : CommandModule < unknown , LookupFromPdcCommandArgs > = {
292316 command : 'lookupFromPdc' ,
293317 describe : 'Fetch and display information about organizations present in PDC' ,
@@ -425,6 +449,7 @@ const updateAllCommand: CommandModule<unknown, UpdateAllCommandArgs> = {
425449 sourceId : source . id ,
426450 notes : `data-scripts charityNavigator.ts execution ${ Date . now ( ) } ` ,
427451 } ) ;
452+ const missingPermissionChangemakerIds : Set < number > = new Set < number > ( ) ;
428453 // Last, for each nonprofit, for each field, post the field. These are
429454 // issued sequentially rather than via Promise.all because the PDC API
430455 // times out under concurrent POSTs to /changemakerFieldValues.
@@ -438,19 +463,29 @@ const updateAllCommand: CommandModule<unknown, UpdateAllCommandArgs> = {
438463 /* eslint-disable-next-line @typescript-eslint/no-unnecessary-condition --
439464 The cnAttribute can really be null even though types say otherwise. */
440465 if ( cnAttribute !== undefined && cnAttribute !== null ) {
441- const fieldValue = await postChangemakerFieldValue ( args . pdcApiBaseUrl , token , {
466+ const fieldValue = {
442467 changemakerId : changemaker . id ,
443468 batchId : fieldBatch . id ,
444469 baseFieldShortCode,
445470 value : cnAttribute . toString ( ) ,
446471 goodAsOf : e . updatedAt ,
447- } ) ;
448- logger . info ( `Added changemaker field value: ${ JSON . stringify ( fieldValue ) } ` ) ;
472+ } ;
473+ await postChangemakerFieldValueWarnOnForbidden (
474+ args . pdcApiBaseUrl ,
475+ token ,
476+ fieldValue ,
477+ missingPermissionChangemakerIds ,
478+ ) ;
449479 }
450480 }
451481 }
452482 }
453483 /* eslint-enable no-await-in-loop */
484+ if ( missingPermissionChangemakerIds . size > 0 ) {
485+ logger . warn (
486+ `No permission for at least one field in each of these changemakers (so not updated): ${ JSON . stringify ( [ ...missingPermissionChangemakerIds ] ) } ` ,
487+ ) ;
488+ }
454489 } ,
455490} ;
456491
0 commit comments