Skip to content

Commit 53cdcfc

Browse files
committed
Dan WIP
1 parent 815a879 commit 53cdcfc

File tree

3 files changed

+47
-38
lines changed

3 files changed

+47
-38
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Candid Premier API subscription key
22
DS_CANDID_API_KEY=my-candid-api-key
33

4+
# Candid Premier API subscription key
5+
DS_CHARITY_NAVIGATOR_API_KEY=my-charity-navigator-api-key
6+
47
# Location of OpenID Connect authority
58
DS_OIDC_BASE_URL=https://auth.philanthropydatacommons.org/realms/pdc
69

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"generateBaseFieldsInserts": "ts-node src/generateBaseFieldsInserts",
1111
"generateApplicationFormJson": "ts-node src/generateApplicationFormJson",
1212
"postProposalVersions": "ts-node src/postProposalVersions",
13-
"charityNavigator": "ts-node src/charityNavigator"
13+
"charityNavigator": "ts-node src/index charityNavigator"
1414
},
1515
"author": "Open Tech Strategies",
1616
"license": "AGPL-3.0-or-later",

src/charityNavigator.ts

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,21 @@ interface NonprofitsPublicResponse {
3939
};
4040
}
4141

42-
const QueryNonprofitsPublic: TypedDocumentNode<{ perPage: number, filter: { ein: { in: string[] }, } }> = gql`
42+
interface NonprofitsPublicVariables {
43+
perPage?: number;
44+
page?: number;
45+
resultSize?: number;
46+
filter: {
47+
ein: {
48+
in: string[];
49+
};
50+
};
51+
}
52+
53+
const QueryNonprofitsPublic: TypedDocumentNode<
54+
NonprofitsPublicResponse,
55+
NonprofitsPublicVariables
56+
> = gql`
4357
query NonprofitsPublic(
4458
$perPage: Int!
4559
$filter: NonprofitFilters
@@ -69,14 +83,11 @@ const QueryNonprofitsPublic: TypedDocumentNode<{ perPage: number, filter: { ein:
6983
`;
7084

7185
const isNonprofitPublic = (edge: object): edge is NonprofitPublic => {
72-
if (typeof edge !== 'object' || edge === null) {
73-
return false;
74-
}
7586
const obj = edge as Record<string, unknown>;
7687
return (
7788
typeof obj.ein === 'string'
7889
&& typeof obj.name === 'string'
79-
&& typeof obj.updated === 'string'
90+
&& typeof obj.updatedAt === 'string'
8091
);
8192
};
8293

@@ -108,7 +119,7 @@ const API_URL = 'https://api.charitynavigator.org/graphql';
108119
const getCharityNavigatorProfiles = async (
109120
apiKey: string,
110121
eins: string[],
111-
): Promise<ApolloClient.QueryResult> => {
122+
): Promise<ApolloClient.QueryResult<NonprofitsPublicResponse>> => {
112123
logger.info(`Looking up EINs ${JSON.stringify(eins)} in Charity Navigator GraphQL API`);
113124
const apollo = apolloInit(API_URL, apiKey);
114125
const variables = {
@@ -129,13 +140,13 @@ const getCharityNavigatorProfiles = async (
129140
};
130141

131142
interface LookupCommandArgs {
132-
'charity-navigator-api-key': string;
143+
'charity-navigator-api-key'?: string;
133144
eins: string[];
134145
outputFile?: string;
135146
}
136147

137148
interface UpdateAllCommandArgs {
138-
'charity-navigator-api-key': string;
149+
'charity-navigator-api-key'?: string;
139150
'oidc-base-url': string,
140151
'oidc-client-id': string,
141152
'oidc-client-secret': string,
@@ -147,10 +158,16 @@ const lookupCommand: CommandModule<unknown, LookupCommandArgs> = {
147158
describe: 'Fetch and display information about organizations by EIN',
148159
builder: (y) => (y
149160
.option('charity-navigator-api-key', {
150-
describe: 'CharityNavigator API key; get from account management at https://developer.charitynavigator.org/',
151-
demandOption: true,
161+
describe: 'CharityNavigator API key; get from account management at https://developer.charitynavigator.org/ (can also be set via DS_CHARITY_NAVIGATOR_API_KEY env var)',
162+
demandOption: false,
152163
type: 'string',
153164
})
165+
.check((argv) => {
166+
if (!argv.charityNavigatorApiKey) {
167+
throw new Error('Missing required argument: charity-navigator-api-key (set via CLI or DS_CHARITY_NAVIGATOR_API_KEY env var)');
168+
}
169+
return true;
170+
})
154171
.option('output-file', {
155172
alias: 'write',
156173
describe: 'Write organization information to the specified JSON file',
@@ -166,7 +183,11 @@ const lookupCommand: CommandModule<unknown, LookupCommandArgs> = {
166183
.check(({ eins }) => !(new Set(eins.map(isValidEin)).has(false)))
167184
),
168185
handler: async (args) => {
169-
const result = await getCharityNavigatorProfiles(args.charityNavigatorApiKey, args.eins)
186+
const { charityNavigatorApiKey: apiKey } = args;
187+
if (!apiKey) {
188+
throw new Error('Missing required argument: charity-navigator-api-key');
189+
}
190+
const result = await getCharityNavigatorProfiles(apiKey, args.eins)
170191
.catch((err) => {
171192
logger.error(err, 'error calling primary graphql api');
172193
throw err;
@@ -206,8 +227,8 @@ const updateAllCommand: CommandModule<unknown, UpdateAllCommandArgs> = {
206227
builder: {
207228
...oidcOptions,
208229
'charity-navigator-api-key': {
209-
describe: 'CharityNavigator API key; get from account management at https://developer.charitynavigator.org/',
210-
demandOption: true,
230+
describe: 'CharityNavigator API key; get from account management at https://developer.charitynavigator.org/ (can also be set via DS_CHARITY_NAVIGATOR_API_KEY env var)',
231+
demandOption: false,
211232
type: 'string',
212233
},
213234
'pdc-api-base-url': {
@@ -217,6 +238,10 @@ const updateAllCommand: CommandModule<unknown, UpdateAllCommandArgs> = {
217238
},
218239
},
219240
handler: async (args) => {
241+
const apiKey = args.charityNavigatorApiKey;
242+
if (!apiKey) {
243+
throw new Error('Missing required argument: charity-navigator-api-key (set via CLI or DS_CHARITY_NAVIGATOR_API_KEY env var)');
244+
}
220245
const changemakers = await getChangemakers(args.pdcApiBaseUrl);
221246
const eins = changemakers.entries.flatMap((c) => c.taxId);
222247
// Charity Navigator expects no hyphens, strip them from EINs after validation.
@@ -227,7 +252,7 @@ const updateAllCommand: CommandModule<unknown, UpdateAllCommandArgs> = {
227252
}
228253
logger.info(validEins, 'Found these valid EINs which will be requested from Charity Navigator');
229254
const charityNavResponse = await getCharityNavigatorProfiles(
230-
args.charityNavigatorApiKey,
255+
apiKey,
231256
validEins,
232257
);
233258
logger.info({ charityNavResponse }, 'CharityNavigator result');
@@ -240,29 +265,10 @@ const updateAllCommand: CommandModule<unknown, UpdateAllCommandArgs> = {
240265
// First, find the existing source. As of this writing, it cannot be created by non-admins.
241266
const source = await getOrCreateSource(args.pdcApiBaseUrl, token);
242267
logger.info(source, 'The PDC Source for Charity Navigator was found');
243-
// Second, post the fields to PDC, except we need to type-safe this thing
244-
// See the difficulty shown at
245-
// https://www.apollographql.com/docs/react/data/typescript#type-narrowing-data-with-datastate
246-
// const fieldValues = charityNavResponse.data.nonprofitsPublic.edges.flatMap();
247-
248-
if (charityNavResponse.dataState !== undefined
249-
&& charityNavResponse.dataState !== null
250-
&& charityNavResponse.dataState === 'complete'
251-
&& charityNavResponse.data !== undefined
252-
&& charityNavResponse.data !== null
253-
&& typeof charityNavResponse.data === 'object'
254-
&& charityNavResponse.data.nonprofitsPublic !== undefined
255-
&& charityNavResponse.data.nonprofitsPublic !== null
256-
&& typeof charityNavResponse.data.nonprofitsPublic === 'object'
257-
&& charityNavResponse.data.nonprofitsPublic.edges !== undefined
258-
&& charityNavResponse.data.nonprofitsPublic.edges !== null
259-
&& Array.isArray(charityNavResponse.data.nonprofitsPublic.edges)
260-
) {
261-
const nonprofits: NonprofitPublic[] = charityNavResponse.data.nonprofitsPublic.edges.flatMap((e) => {
262-
if (isNonprofitPublic(e)) {
263-
return e;
264-
}
265-
});
268+
// Second, post the fields to PDC
269+
if (charityNavResponse.data) {
270+
const { edges } = charityNavResponse.data.nonprofitsPublic;
271+
const nonprofits = edges.filter((e): e is NonprofitPublic => isNonprofitPublic(e));
266272
logger.info(nonprofits, 'Found these nonprofits');
267273
}
268274
},

0 commit comments

Comments
 (0)