Skip to content

Commit

Permalink
Factor out some common stuff from calculator.ts
Browse files Browse the repository at this point in the history
I want to copy this file for the RI calculator. To avoid too much
duplication, this factors out some common logic: the "fetch from API
and interpret errors" code, and the static footer.

It also changes the `api-path` attribute of the calculator element to
`api-host`, which should contain only the protocol and hostname, not
the path. Since the RI calculator will be fetching multiple paths, I
want to separate path from host. `api-path` is not publicly
documented, so I think it's fine to remove it.
  • Loading branch information
oyamauchi committed Aug 4, 2023
1 parent 141aaa4 commit 19b82a2
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 53 deletions.
41 changes: 41 additions & 0 deletions src/api/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Fetches a response from the Incentives API. Handles turning an error response
* into an exception with a useful message.
*/
export async function fetchApi(
apiKey: string,
apiHost: string,
path: string,
query: URLSearchParams,
) {
const url = new URL(apiHost);
url.pathname = path;
url.search = query.toString();
const response: Response = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
if (response.status >= 400) {
console.error(response);
// statusText isn't always set, but it's a reasonable proxy for a human readable error if it is:
let message = response.statusText;
try {
const error = await response.json();
console.error(error);
if (error.title && error.detail) {
// Zuplo's API key errors have this form:
message = `${error.title}: ${error.detail}`;
} else if (error.message && error.error) {
// Rewiring America's API errors have this form:
message = `${error.error}: ${error.message}`;
}
} catch (e) {
// if we couldn't get anything off the response, just go with something generic:
message = 'Error loading incentives.';
}
throw new Error(message);
}
return response.json();
}
21 changes: 21 additions & 0 deletions src/calculator-footer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { html } from 'lit';

export const CALCULATOR_FOOTER = html`<div class="calculator__footer">
<p>
Calculator&nbsp;by&nbsp;
<a target="_blank" href="https://www.rewiringamerica.org">
Rewiring&nbsp;America
</a>
<a
target="_blank"
href="https://content.rewiringamerica.org/view/privacy-policy.pdf"
>
Privacy&nbsp;Policy
</a>
<a target="_blank" href="https://content.rewiringamerica.org/api/terms.pdf">
Terms
</a>
</p>
</div>`;
65 changes: 12 additions & 53 deletions src/calculator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
ICalculatedIncentiveResults,
OwnerStatus,
} from './calculator-types';
import { CALCULATOR_FOOTER } from './calculator-footer';
import { fetchApi } from './api/fetch';

const loadedTemplate = (
results: ICalculatedIncentiveResults,
Expand All @@ -32,8 +34,7 @@ const errorTemplate = (error: unknown) => html`
</div>
`;

const DEFAULT_CALCULATOR_API_PATH: string =
'https://api.rewiringamerica.org/api/v0/calculator';
const DEFAULT_CALCULATOR_API_HOST: string = 'https://api.rewiringamerica.org';

@customElement('rewiring-america-calculator')
export class RewiringAmericaCalculator extends LitElement {
Expand Down Expand Up @@ -62,8 +63,8 @@ export class RewiringAmericaCalculator extends LitElement {
@property({ type: String, attribute: 'api-key' })
apiKey: string = '';

@property({ type: String, attribute: 'api-path' })
apiPath: string = DEFAULT_CALCULATOR_API_PATH;
@property({ type: String, attribute: 'api-host' })
apiHost: string = DEFAULT_CALCULATOR_API_HOST;

/* supported properties to allow pre-filling the form */

Expand Down Expand Up @@ -123,34 +124,12 @@ export class RewiringAmericaCalculator extends LitElement {
tax_filing,
household_size,
});
const url = new URL(`${this.apiPath}?${query}`);
const response: Response = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${this.apiKey}`,
},
});
if (response.status >= 400) {
console.error(response);
// statusText isn't always set, but it's a reasonable proxy for a human readable error if it is:
let message = response.statusText;
try {
const error = await response.json();
console.error(error);
if (error.title && error.detail) {
// Zuplo's API key errors have this form:
message = `${error.title}: ${error.detail}`;
} else if (error.message && error.error) {
// Rewiring America's API errors have this form:
message = `${error.error}: ${error.message}`;
}
} catch (e) {
// if we couldn't get anything off the response, just go with something generic:
message = 'Error loading incentives.';
}
throw new Error(message);
}
return response.json();
return await fetchApi(
this.apiKey,
this.apiHost,
'/api/v0/calculator',
query,
);
},
// if the args array changes then the task will run again
args: () => [
Expand Down Expand Up @@ -187,27 +166,7 @@ export class RewiringAmericaCalculator extends LitElement {
error: errorTemplate,
})}
`}
<div class="calculator__footer">
<p>
Calculator&nbsp;by&nbsp;<a
target="_blank"
href="https://www.rewiringamerica.org"
>Rewiring&nbsp;America</a
>
<a
target="_blank"
href="https://content.rewiringamerica.org/view/privacy-policy.pdf"
>Privacy&nbsp;Policy</a
>
<a
target="_blank"
href="https://content.rewiringamerica.org/api/terms.pdf"
>Terms</a
>
</p>
</div>
${CALCULATOR_FOOTER}
</div>
`;
}
Expand Down

0 comments on commit 19b82a2

Please sign in to comment.