Skip to content

Commit

Permalink
WIP carin api, fetcher and param matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
daniellrgn committed Jan 15, 2025
1 parent ff6ec29 commit 5801be0
Show file tree
Hide file tree
Showing 3 changed files with 275 additions and 0 deletions.
222 changes: 222 additions & 0 deletions src/lib/components/app/FetchCARINBB.svelte
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>
5 changes: 5 additions & 0 deletions src/params/carin.ts
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;
48 changes: 48 additions & 0 deletions src/routes/api/proxy/+server.ts
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 });
}
};

0 comments on commit 5801be0

Please sign in to comment.