-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP carin api, fetcher and param matcher
- Loading branch information
1 parent
ff6ec29
commit 5801be0
Showing
3 changed files
with
275 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
<script lang="ts"> | ||
import { | ||
Button, | ||
Col, | ||
FormGroup, | ||
Input, | ||
Label, | ||
Row, | ||
Spinner } from 'sveltestrap'; | ||
import { SOF_HOSTS } from '$lib/config'; | ||
import type { ResourceRetrieveEvent, SOFAuthEvent, SOFHost } from '$lib/utils/types'; | ||
import { authorize, getResourcesWithReferences } from '$lib/utils/sofClient.js'; | ||
import { createEventDispatcher, onMount } from 'svelte'; | ||
// For "quick sample" in demo | ||
import { EXAMPLE_IPS, IPS_DEFAULT } from '$lib/config'; | ||
import type { IPSRetrieveEvent } from '$lib/utils/types'; | ||
const authDispatch = createEventDispatcher<{'sof-auth-init': SOFAuthEvent; 'sof-auth-fail': SOFAuthEvent}>(); | ||
const resourceDispatch = createEventDispatcher<{'update-resources': ResourceRetrieveEvent}>(); | ||
let processing = false; | ||
let loadingSample = false; | ||
let fetchError = ""; | ||
let result: ResourceRetrieveEvent = { | ||
resources: undefined | ||
}; | ||
let sofHostSelection = SOF_HOSTS[0].id; | ||
let sofHost:SOFHost | undefined = SOF_HOSTS.find(e => e.id == sofHostSelection); | ||
$: { | ||
if (sofHostSelection) { | ||
sofHost = SOF_HOSTS.find(e => e.id == sofHostSelection); | ||
} | ||
} | ||
async function prepareIps() { | ||
fetchError = ""; | ||
try { | ||
if (sofHost) { | ||
try { | ||
authorize(sofHost.url, sofHost.clientId);// , sofHost.clientSecret); | ||
authDispatch('sof-auth-init', { data: true }); | ||
} catch (e) { | ||
authDispatch('sof-auth-fail', { data: false }); | ||
} | ||
} | ||
} catch (e) { | ||
console.log('Failed', e); | ||
fetchError = "Error preparing IPS"; | ||
} | ||
} | ||
onMount(async function() { | ||
let key = sessionStorage.getItem('SMART_KEY'); | ||
if (key) { | ||
let token = sessionStorage.getItem(JSON.parse(key)); | ||
if (token) { | ||
let url = JSON.parse(token).serverUrl; | ||
let sofHostAuthd = SOF_HOSTS.find(e => e.url == url); | ||
if (sofHostAuthd) { | ||
sofHost = sofHostAuthd; | ||
sofHostSelection = sofHost.id; | ||
await fetchData(); | ||
sessionStorage.removeItem(key); | ||
sessionStorage.removeItem('SMART_KEY'); | ||
} | ||
} | ||
} | ||
}); | ||
function endSession() { | ||
let key = sessionStorage.getItem('SMART_KEY'); | ||
if (key) { | ||
sessionStorage.removeItem(JSON.parse(key)); | ||
sessionStorage.removeItem('SMART_KEY'); | ||
} | ||
} | ||
async function fetchData() { | ||
processing = true; | ||
try { | ||
let resources = await getResourcesWithReferences(1); | ||
result.resources = resources; | ||
console.log(resources) | ||
processing = false; | ||
return resourceDispatch('update-resources', result); | ||
} catch (e: any) { | ||
console.log(e.message); | ||
fetchError = e.message; | ||
processing = false; | ||
endSession(); | ||
} | ||
} | ||
// Demo quick sample loader | ||
let defaultUrl = EXAMPLE_IPS[IPS_DEFAULT]; | ||
const ipsDispatch = createEventDispatcher<{'ips-retrieved': IPSRetrieveEvent}>(); | ||
let ipsResult: IPSRetrieveEvent = { | ||
ips: undefined | ||
}; | ||
async function quickLoad() { | ||
fetchError = ""; | ||
loadingSample = true; | ||
try { | ||
let content; | ||
let hostname; | ||
const contentResponse = await fetch(defaultUrl!, { | ||
headers: { accept: 'application/fhir+json' } | ||
}).then(function(response) { | ||
if (!response.ok) { | ||
// make the promise be rejected if we didn't get a 2xx response | ||
throw new Error("Unable to fetch IPS", {cause: response}); | ||
} else { | ||
return response; | ||
} | ||
}); | ||
content = await contentResponse.json(); | ||
hostname = defaultUrl?.hostname; | ||
loadingSample = false | ||
ipsResult = { | ||
ips: content, | ||
source: hostname | ||
}; | ||
ipsDispatch('ips-retrieved', ipsResult); | ||
} catch (e) { | ||
loadingSample = false; | ||
console.log('Failed', e); | ||
fetchError = "Error preparing IPS"; | ||
} | ||
} | ||
async function testAPI() { | ||
fetchError = ""; | ||
loadingSample = true; | ||
try { | ||
let content; | ||
let hostname; | ||
const contentResponse = await fetch("/api/proxy", { | ||
method: 'POST', | ||
headers: { accept: 'application/fhir+json' }, | ||
body: JSON.stringify({ url: defaultUrl }) | ||
}).then(function(response) { | ||
if (!response.ok) { | ||
// make the promise be rejected if we didn't get a 2xx response | ||
throw new Error("Unable to fetch IPS", {cause: response}); | ||
} else { | ||
return response; | ||
} | ||
}); | ||
content = await contentResponse.json(); | ||
loadingSample = false | ||
console.log(content); | ||
} catch (e) { | ||
loadingSample = false; | ||
console.log('Failed', e); | ||
fetchError = "Error preparing IPS"; | ||
} | ||
} | ||
</script> | ||
<form on:submit|preventDefault={() => prepareIps()}> | ||
<FormGroup> | ||
<Label>Fetch US Core data via SMART authorization</Label> | ||
{#each SOF_HOSTS as host} | ||
<Row class="mx-2"> | ||
<Input type="radio" bind:group={sofHostSelection} value={host.id} label={host.name} /> | ||
{#if host.note} | ||
<p class="text-secondary" style="margin-left:25px">{@html host.note}</p> | ||
{/if} | ||
</Row> | ||
{/each} | ||
</FormGroup> | ||
|
||
<Row> | ||
<Col xs="auto"> | ||
<Button color="primary" style="width:fit-content" disabled={processing || loadingSample} type="submit"> | ||
{#if !processing} | ||
Fetch Data | ||
{:else} | ||
Fetching... | ||
{/if} | ||
</Button> | ||
</Col> | ||
<Col xs="auto"> | ||
<Button | ||
color="secondary" | ||
outline | ||
style="width:fit-content" | ||
disabled={processing || loadingSample} | ||
type="button" | ||
on:click={() => quickLoad()}> | ||
{#if !loadingSample} | ||
Quick Sample | ||
{:else} | ||
Loading... | ||
{/if} | ||
</Button> | ||
</Col> | ||
<Col xs="auto"> | ||
<Button | ||
color="secondary" | ||
outline | ||
style="width:fit-content" | ||
disabled={processing || loadingSample} | ||
type="button" | ||
on:click={() => testAPI()}> | ||
{#if !loadingSample} | ||
Test API | ||
{:else} | ||
Testing... | ||
{/if} | ||
</Button> | ||
</Col> | ||
{#if processing || loadingSample} | ||
<Col xs="auto" class="d-flex align-items-center px-0"> | ||
<Spinner color="primary" type="border" size="md"/> | ||
</Col> | ||
{/if} | ||
</Row> | ||
</form> | ||
<span class="text-danger">{fetchError}</span> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import type { ParamMatcher } from '@sveltejs/kit'; | ||
|
||
export const match = ((param: string): param is ('acentra' | 'aetna' | 'carefirst' | 'cpcds' | 'humana') => { | ||
return param === 'acentra' || param === 'aetna' || param === 'carefirst' || param === 'cpcds' || param === 'humana'; | ||
}) satisfies ParamMatcher; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { json } from '@sveltejs/kit'; | ||
import { env } from '$env/dynamic/private'; // Access private environment variables | ||
|
||
const CPCDS_URL = 'https://cpcds-server.lantanagroup.com/fhir'; | ||
const SMART_LAUNCH_URL = `${CPCDS_URL}/.well-known/smart-configuration`; | ||
|
||
export const POST = async ({ request, url }: { request: Request; url: URL }) => { | ||
try { | ||
// Get the base URL path to the current route | ||
const proxyPath = url.pathname; | ||
|
||
// Construct the forward URL by removing the base path (dynamic) | ||
const forwardPath = proxyPath.replace(url.origin, ''); // Strip the origin | ||
const forwardQuery = url.search; // Includes the "?" and any query parameters | ||
|
||
// Construct the full target API URL | ||
const targetUrl = `https://api.example.com${forwardPath}${forwardQuery}`; | ||
|
||
// Read the incoming request body | ||
const body = await request.text(); // Use `text()` for flexibility (JSON, form data, etc.) | ||
|
||
// Set up headers and include the client secret | ||
const clientSecret = env.CLIENT_SECRET; | ||
|
||
const headers = new Headers(request.headers); | ||
headers.set('Authorization', `Basic ${btoa(`client_id:${clientSecret}`)}`); | ||
headers.set('Content-Type', request.headers.get('Content-Type') || 'application/json'); | ||
|
||
// Forward the request to the target API | ||
const apiResponse = await fetch(targetUrl, { | ||
method: request.method, | ||
headers, | ||
body: ['POST', 'PUT', 'PATCH'].includes(request.method) ? body : undefined, // Include body if applicable | ||
}); | ||
|
||
// Return the API response to the client | ||
const responseText = await apiResponse.text(); | ||
return new Response(responseText, { | ||
status: apiResponse.status, | ||
headers: apiResponse.headers, // Pass response headers | ||
}); | ||
} catch (error) { | ||
console.error('Proxy error:', error); | ||
|
||
// Handle and return errors | ||
return json({ error: 'An error occurred while forwarding the request' }, { status: 500 }); | ||
} | ||
}; |