-
Notifications
You must be signed in to change notification settings - Fork 203
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# This is a combination of 17 commits.
# This is the 1st commit message: fix build errors and create deploy file # This is the commit message #2: remove vercel from deploy yml # This is the commit message #3: fix build error related to vercel front end # This is the commit message #4: configure zapehr secrets for telemed intake zambdas # This is the commit message #5: fix deploy zambdas for demo env # This is the commit message #6: fix package json # This is the commit message #7: fix zapehr env error # This is the commit message #8: checkout the env variables from secrets repo # This is the commit message #9: update the private ssh keys # This is the commit message #10: configure deploy zambdas # This is the commit message #11: fix typo # This is the commit message #12: fix the directory for the build # This is the commit message #13: add build # This is the commit message #14: configure build with the deploy # This is the commit message #15: configure build file without pushd # This is the commit message #16: create separate build file for intake # This is the commit message #17: schedule types fix
- Loading branch information
Showing
7 changed files
with
383 additions
and
27 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 |
---|---|---|
|
@@ -45,7 +45,7 @@ jobs: | |
uses: webfactory/[email protected] | ||
with: | ||
ssh-private-key: | | ||
${{ secrets.OTTEHR_PRIVATE_SSH_KEY }} | ||
${{ secrets.OTTEHR_DEMO_PRIVATE_KEY }} | ||
${{ secrets.BUMP_VERSION }} | ||
- uses: actions/checkout@v3 | ||
|
@@ -61,15 +61,15 @@ jobs: | |
with: | ||
version: 8 | ||
|
||
- name: Create secrets file from github secrets | ||
id: create-json | ||
uses: jsdaniell/[email protected] | ||
- name: Check out secrets repo to grab the env file. | ||
uses: actions/checkout@v3 | ||
with: | ||
name: 'secrets.json' | ||
json: ${{ secrets.OTTEHR_SECRETS_JSON }} | ||
repository: masslight/ottehr-secrets | ||
ssh-key: ${{ secrets.OTTEHR_DEMO_PRIVATE_KEY }} | ||
path: 'secrets' | ||
|
||
- name: Move env file into .env folder | ||
run: mkdir -p ${{ env.ZAMBDAS_LOCATION }}/.env/ && mv secrets.json ${{ env.ZAMBDAS_LOCATION }}/.env/${{ github.event.inputs.environment }}.json | ||
- name: Move secrets file into the .env directory. | ||
run: mkdir -p ${{ env.ZAMBDAS_LOCATION }}/.env/ && cp secrets/telemed-intake/* ${{ env.ZAMBDAS_LOCATION }}/.env/ | ||
|
||
- name: Copy env file to local.env to support serverless package command | ||
run: cp ${{ env.ZAMBDAS_LOCATION }}/.env/${{ github.event.inputs.environment }}.json ${{ env.ZAMBDAS_LOCATION }}/.env/local.json | ||
|
@@ -97,11 +97,14 @@ jobs: | |
- name: Install dependencies | ||
run: pnpm install | ||
|
||
- name: Build app | ||
run: pnpm build:telemed-intake | ||
|
||
- name: Setup zapehr secrets | ||
run: cd ${{ env.ZAMBDAS_LOCATION }} && pnpm setup-zapehr-secrets:${{ github.event.inputs.environment }} | ||
|
||
- name: Deploy zambdas | ||
run: cd ${{ env.ZAMBDAS_LOCATION }} && pnpm deploy-zambas:${{ github.event.inputs.environment }} | ||
run: cd ${{ env.ZAMBDAS_LOCATION }} && pnpm deploy-zambdas:${{ github.event.inputs.environment }} | ||
|
||
- name: Configure AWS CLI | ||
uses: aws-actions/configure-aws-credentials@v1 | ||
|
@@ -112,8 +115,9 @@ jobs: | |
|
||
- name: Deploy to AWS S3 | ||
run: | | ||
aws s3 sync ./build s3://telemed.ottehr.com --delete | ||
aws s3 sync ./packages/telemed-intake/app/build s3://telemed.ottehr.com --delete | ||
<<<<<<< HEAD | ||
<<<<<<< HEAD | ||
<<<<<<< HEAD | ||
======= | ||
|
@@ -143,3 +147,24 @@ jobs: | |
{ "title": "Branch", "value": "${{ env.BRANCH }}", "short": true }, | ||
{ "title": "Environment", "value": "${{ env.ENVIRONMENT }}", "short": true }, | ||
{ "title": "Version", "value": "${{ env.PACKAGE_VERSION }}", "short": true }] | ||
======= | ||
# - name: Notify Slack | ||
# if: always() | ||
# uses: edge/[email protected] | ||
# env: | ||
# SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | ||
# BRANCH: ${{ github.ref }} | ||
# PACKAGE_VERSION: ${{ steps.package-version.outputs.current-version }} | ||
# with: | ||
# channel: '#ottehr-notifications' | ||
# status: ${{ job.status }} | ||
# success_text: '${{ env.GITHUB_WORKFLOW }} (Run #${{ env.GITHUB_RUN_NUMBER }}) deploy completed successfully' | ||
# failure_text: '${{ env.GITHUB_WORKFLOW }} (Run #${{ env.GITHUB_RUN_NUMBER }}) deploy failed' | ||
# cancelled_text: '${{ env.GITHUB_WORKFLOW }} (Run #${{ env.GITHUB_RUN_NUMBER }}) deploy was cancelled' | ||
# fields: | | ||
# [{ "title": "Service", "value": "${{ env.SLACK_NOTIFICATION_SERVICE_NAME }}", "short": true }, | ||
# { "title": "Action URL", "value": "${{ env.GITHUB_SERVER_URL }}/${{ env.GITHUB_REPOSITORY }}/actions/runs/${{ env.GITHUB_RUN_ID }}"}, | ||
# { "title": "Branch", "value": "${{ env.BRANCH }}", "short": true }, | ||
# { "title": "Environment", "value": "${{ env.ENVIRONMENT }}", "short": true }, | ||
# { "title": "Version", "value": "${{ env.PACKAGE_VERSION }}", "short": true }] | ||
>>>>>>> bf2b1b4 (checkout the env variables from secrets repo) |
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
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
293 changes: 293 additions & 0 deletions
293
packages/telemed-intake/zambdas/scripts/deploy-zambdas.ts
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,293 @@ | ||
import { BatchInputDeleteRequest, BatchInputPostRequest } from '@zapehr/sdk'; | ||
import { Subscription } from 'fhir/r4'; | ||
import fs from 'fs'; | ||
import { getM2MClientToken } from '../src/shared'; | ||
import { createZambdaClient, performEffectWithEnvFile } from './common'; | ||
import { createFhirClient } from 'ottehr-utils'; | ||
|
||
interface SubscriptionDetils { | ||
criteria: string; | ||
reason: string; | ||
event?: 'create' | 'update'; | ||
} | ||
|
||
interface DeployZambda { | ||
type: 'http_open' | 'http_auth' | 'subscription' | 'cron'; | ||
subscriptionDetils?: SubscriptionDetils[]; | ||
schedule?: { | ||
start?: string; | ||
end?: string; | ||
expression: string; | ||
}; | ||
environments?: string[]; | ||
} | ||
|
||
const ZAMBDAS: { [name: string]: DeployZambda } = { | ||
'GET-PATIENTS': { | ||
type: 'http_auth', | ||
}, | ||
'GET-PAPERWORK': { | ||
type: 'http_auth', | ||
}, | ||
'CREATE-PAPERWORK': { | ||
type: 'http_auth', | ||
}, | ||
'UPDATE-PAPERWORK': { | ||
type: 'http_auth', | ||
}, | ||
'CREATE-APPOINTMENT': { | ||
type: 'http_auth', | ||
}, | ||
'GET-APPOINTMENTS': { | ||
type: 'http_auth', | ||
}, | ||
'GET-TELEMED-STATES': { | ||
type: 'http_open', | ||
}, | ||
'CANCEL-APPOINTMENT': { | ||
type: 'http_auth', | ||
}, | ||
'GET-WAIT-STATUS': { | ||
type: 'http_open', | ||
}, | ||
'JOIN-CALL': { | ||
type: 'http_open', | ||
}, | ||
'VIDEO-CHAT-INVITES-CREATE': { | ||
type: 'http_auth', | ||
}, | ||
'VIDEO-CHAT-INVITES-CANCEL': { | ||
type: 'http_auth', | ||
}, | ||
'VIDEO-CHAT-INVITES-LIST': { | ||
type: 'http_auth', | ||
}, | ||
'GET-PRESIGNED-FILE-URL': { | ||
type: 'http_open', | ||
}, | ||
}; | ||
|
||
const updateZambdas = async (config: any): Promise<void> => { | ||
const token = await getM2MClientToken(config); | ||
const zambdaClient = await createZambdaClient(config); | ||
|
||
console.log('Getting list of zambdas'); | ||
const currentZambdas = await zambdaClient.getAllZambdas(); | ||
|
||
// First check if any zambdas are not found | ||
for await (const zambda of Object.keys(ZAMBDAS)) { | ||
const currentZambda = ZAMBDAS[zambda]; | ||
if (currentZambda.environments && !currentZambda.environments.includes(config.ENVIRONMENT)) { | ||
console.log(`\nZambda ${zambda} is not run in ${config.ENVIRONMENT}`); | ||
continue; | ||
} | ||
|
||
let currentDeployedZambda = currentZambdas.find( | ||
(tempZambda) => tempZambda.name === `telemed-${zambda.toLowerCase()}`, | ||
); | ||
|
||
if (currentDeployedZambda) { | ||
console.log(`\nZambda ${zambda} is found with ID ${currentDeployedZambda.id}`); | ||
} else { | ||
console.log(`\nZambda ${zambda} is not found, creating it`); | ||
currentDeployedZambda = await zambdaClient.createZambda({ | ||
name: `telemed-${zambda.toLowerCase()}`, | ||
}); | ||
console.log(`Zambda ${zambda} with ID ${currentDeployedZambda.id}`); | ||
} | ||
|
||
await updateProjectZambda(currentDeployedZambda.id, zambda, currentZambda, config, token); | ||
} | ||
}; | ||
|
||
async function updateProjectZambda( | ||
zambdaId: string, | ||
zambdaName: string, | ||
zambda: DeployZambda, | ||
config: any, | ||
token: string, | ||
): Promise<void> { | ||
const projectApiUrl = 'https://project-api.zapehr.com/v1'; | ||
// todo use zambda client https://github.com/masslight/zapehr/issues/2586 | ||
const endpoint = `${projectApiUrl}/zambda/${zambdaId}/s3-upload`; | ||
|
||
console.log(`Getting S3 upload URL for zambda ${zambdaName}`); | ||
const zapehrResponse = await fetch(endpoint, { | ||
method: 'post', | ||
headers: { | ||
Authorization: `Bearer ${token}`, | ||
'x-zapehr-project-id': config.PROJECT_ID, | ||
}, | ||
}); | ||
|
||
if (!zapehrResponse.ok) { | ||
const zapehrResponseJson = await zapehrResponse.json(); | ||
console.log( | ||
`status, ${zapehrResponse.status}, status text, ${ | ||
zapehrResponse.statusText | ||
}, zapehrResponseJson, ${JSON.stringify(zapehrResponseJson)}`, | ||
); | ||
throw Error('An error occurred during the zapEHR Zambda S3 URL request'); | ||
} | ||
const s3Url = (await zapehrResponse.json())['signedUrl']; | ||
console.log(`Got S3 upload URL for zambda ${zambdaName}`); | ||
|
||
console.log('Uploading zip file to S3'); | ||
// zip file names are lowercase with dashes | ||
const zipFile = zambdaName.toLowerCase().replace(/_/g, '-'); | ||
const file = fs.readFileSync(`.dist/${zipFile}.zip`); | ||
const awsResponse = await fetch(s3Url, { | ||
method: 'put', | ||
body: file, | ||
}); | ||
|
||
if (!awsResponse.ok) { | ||
const awsResponseJson = await awsResponse.json(); | ||
console.log( | ||
`status, ${awsResponse.status}, status text, ${awsResponse.statusText}, awsResponseJson, ${JSON.stringify( | ||
awsResponseJson, | ||
)}`, | ||
); | ||
throw Error('An error occurred during the AWS upload zip file request'); | ||
} | ||
console.log('Uploaded zip file to S3'); | ||
|
||
console.log('Updating zambda'); | ||
//TODO: change this code back to zambdaClient.updateZambda() function, this is temporary fix | ||
const updateZambda = await fetch(`${projectApiUrl}/zambda/${zambdaId}`, { | ||
method: 'PATCH', | ||
headers: { | ||
authorization: `Bearer ${token}`, | ||
}, | ||
body: JSON.stringify({ | ||
triggerMethod: zambda.type, | ||
schedule: zambda.schedule, | ||
}), | ||
}); | ||
if (updateZambda.status !== 200) { | ||
throw new Error(`Error updating the zambda ${JSON.stringify(await updateZambda.json())}`); | ||
} | ||
console.log('Updated zambda'); | ||
|
||
if (zambda.type === 'subscription') { | ||
if (zambda.subscriptionDetils === undefined) { | ||
console.log('Zambda is subscription type but does not have details on the subscription'); | ||
return; | ||
} | ||
const endpoint = `zapehr-lambda:${zambdaId}`; | ||
const fhirClient = await createFhirClient(config); | ||
const subscriptionsSearch: Subscription[] = await fhirClient.searchResources({ | ||
resourceType: 'Subscription', | ||
searchParams: [ | ||
{ | ||
name: 'url', | ||
value: endpoint, | ||
}, | ||
{ | ||
name: 'status', | ||
value: 'active', | ||
}, | ||
], | ||
}); | ||
console.log(`${subscriptionsSearch.length} existing subscriptions found`); | ||
|
||
const EXTENSION_URL = 'http://zapehr.com/fhir/extension/SubscriptionTriggerEvent'; | ||
|
||
const createSubscriptionRequests: BatchInputPostRequest[] = []; | ||
const deleteSubscriptionRequests: BatchInputDeleteRequest[] = []; | ||
|
||
// check existing subscriptions against current subscription details to determin if any should be deleted | ||
// if any events are changing, delete | ||
// if any existing criteria doesn't exist in the details array defined above, delete | ||
const subscriptionsNotChanging = subscriptionsSearch.reduce((acc: Subscription[], existingSubscription) => { | ||
const existingSubscriptionEvent = existingSubscription.extension?.find( | ||
(ext) => ext.url === EXTENSION_URL, | ||
)?.valueString; | ||
const subscriptionMatch = zambda.subscriptionDetils?.find((zambdaSubscritionDetail) => { | ||
const eventMatch = existingSubscriptionEvent === zambdaSubscritionDetail.event; | ||
const criteriaMatch = zambdaSubscritionDetail.criteria === existingSubscription.criteria; | ||
return eventMatch && criteriaMatch; | ||
}); | ||
if (subscriptionMatch) { | ||
console.log( | ||
`subscription with criteria: '${subscriptionMatch.criteria}' and event: '${subscriptionMatch.event}' is not changing`, | ||
); | ||
acc.push(existingSubscription); | ||
} else { | ||
console.log( | ||
`subscription with criteria: '${existingSubscription.criteria}' and event: '${existingSubscriptionEvent}' is being deleted since the criteria/event is not contained in the updated subscription details array`, | ||
); | ||
const deleteRequest: BatchInputDeleteRequest = { | ||
method: 'DELETE', | ||
url: `/Subscription/${existingSubscription.id}`, | ||
}; | ||
deleteSubscriptionRequests.push(deleteRequest); | ||
} | ||
return acc; | ||
}, []); | ||
|
||
// check current subscription details again existing subscriptions to determin if any should be created | ||
zambda.subscriptionDetils.forEach((subscriptionDetail) => { | ||
// if the subscription detail is found in subscriptions not chaning, do nothing | ||
const foundSubscription = subscriptionsNotChanging.find( | ||
(subscription) => subscription.criteria === subscriptionDetail.criteria, | ||
); | ||
// else create it | ||
if (!foundSubscription) { | ||
console.log( | ||
`Creating subscription with criteria: '${subscriptionDetail.criteria}' and event: '${subscriptionDetail.event}'`, | ||
); | ||
const extension = []; | ||
if (subscriptionDetail?.event) { | ||
extension.push({ | ||
url: EXTENSION_URL, | ||
valueString: subscriptionDetail.event, | ||
}); | ||
} | ||
const subscriptionResource: Subscription = { | ||
resourceType: 'Subscription', | ||
status: 'active', | ||
reason: subscriptionDetail.reason, | ||
criteria: subscriptionDetail.criteria, | ||
channel: { | ||
type: 'rest-hook', | ||
endpoint: endpoint, | ||
}, | ||
extension: extension, | ||
}; | ||
const subscriptionRequest: BatchInputPostRequest = { | ||
method: 'POST', | ||
url: '/Subscription', | ||
resource: subscriptionResource, | ||
}; | ||
createSubscriptionRequests.push(subscriptionRequest); | ||
} | ||
}); | ||
if (createSubscriptionRequests.length > 0 || deleteSubscriptionRequests.length > 0) { | ||
console.log('making subscription transaction request'); | ||
await fhirClient.transactionRequest({ | ||
requests: [...createSubscriptionRequests, ...deleteSubscriptionRequests], | ||
}); | ||
} | ||
console.log(`Created ${createSubscriptionRequests.length} subscriptions`); | ||
console.log(`Deleted ${deleteSubscriptionRequests.length} subscriptions`); | ||
console.log(`${subscriptionsNotChanging.length} subscriptions are not changing`); | ||
} | ||
} | ||
|
||
if (process.argv.length < 3) { | ||
console.log( | ||
'You must provide an environment and an api as command-line arguments, e.g.: npm run deploy-zambdas testing', | ||
); | ||
process.exit(); | ||
} | ||
|
||
// So we can use await | ||
const main = async (): Promise<void> => { | ||
await performEffectWithEnvFile(updateZambdas); | ||
}; | ||
|
||
main().catch((error) => { | ||
console.log('error', error); | ||
throw error; | ||
}); |
Oops, something went wrong.