Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,26 @@ There are npm scripts for deploying to local, staging, and production, as well a
- Install Terraform [as discussed above](#terraform)
- Create an Oystehr project in the [Oystehr developer console](https://console.oystehr.com).
- Create an M2M Client with full access rights in your Oystehr project; you can use the default M2M created during project setup.
- Create an s3 bucket for your terraform state (example: ottehr-terraform-state)
- Configure your Terraform Backend ([`deploy/backend.config`](/deploy/backend.config.template)).
- Configure your local Terraform variables ([`deploy/${env}.tfvars`](/deploy/terraform.tfvars.template)).
- Configure your application variables ([`packages/zambda/.env/${env}.json`](/packages/zambdas/.env/local.template.json)).
- Configure your application variables ([`packages/zambda/.env/${env}.json`](/packages/zambdas/.env/local.template.json)):
- AUTH0_CLIENT
- AUTH0_SECRET
- ENVIRONMENT
- PROJECT_ID
- PATIENT_APP_NAME
- EHR_APP_NAME
- lab-autolab-account-number - globally unique, can be for example `ottehr-local` and so on for every env
- non-prod env: "lab-autolab-lab-id": "790b282d-77e9-4697-9f59-0cef8238033a"
- prod env: "lab-autolab-lab-id": "713d14ef-c30a-4b9a-a13a-4ad4648ff3ed"
- for prod case: first create project, convert it to live mode for Autolabs to work properly, and then run apply
- Set up Sendgrid API key
- Set up Anthropic API key
- Set up Sentry secrets and vars
- Change env names in the terraform-setup script in deploy/packages.json for envs that you want to create



All three of those configuration files have examples with the `.template` extension that you can copy to start.

Expand All @@ -68,6 +85,19 @@ npm run apply-local
npm run apps:start
```

**After applying terraform**
- Use created resources to determine variables to fill in in env.json in packages/zambdas/.env
- DEFAULT_BILLING_RESOURCE
- ORGANIZATION_ID
- Use the created m2m client for e2e tests to get client and secret vars and put it into tests.{env}.json in ehr and intake env folders so e2e tests can run
- add those client and secret as AUTH0_CLIENT_TESTS and AUTH0_SECRET_TESTS to zambda env file and to /apps/{intake|ehr}/env/tests.{env}.json
- Create a new EHR app user in console with username e2euser@masslight.com and add TEXT_USERNAME="e2euser@masslight.com" and TEXT_PASSWORD="password_you_set" fields into /apps/{intake|ehr}/env/tests.{env}.json
- Add PHONE_NUMBER, TEXT_USERNAME and TEXT_PASSWORD with username and a password to a ClickSend account so intake e2e tests can authorize

All those steps can be done executing `npm run fill-env-with-created-resources-data.ts {env}` after apply in deploy folder, except setting phone, username and passwords, you will have to do it manually



The rest of this section will discuss the configuration files in more depth.

### Terraform Backend
Expand Down
106 changes: 106 additions & 0 deletions deploy/fill-env-with-created-resources-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import Oystehr from '@oystehr/sdk';
import { Organization } from 'fhir/r4b';
import * as fs from 'fs';
import { fhirApiUrlFromAuth0Audience, projectApiUrlFromAuth0Audience } from '../packages/zambdas/src/scripts/helpers';
import { getAuth0Token } from '../packages/zambdas/src/shared';

async function main(): Promise<void> {
const env = process.argv[2];

const envFilePath = `../packages/zambdas/.env/${env}.json`;
const secrets = JSON.parse(fs.readFileSync(envFilePath, 'utf8'));

const token = await getAuth0Token(secrets);
const oystehr = new Oystehr({
accessToken: token,
fhirApiUrl: fhirApiUrlFromAuth0Audience(secrets.AUTH0_AUDIENCE),
projectApiUrl: projectApiUrlFromAuth0Audience(secrets.AUTH0_AUDIENCE),
});

// get organization
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these two organization lookups could use the name as defined in config instead of hardcoded strings

const organizationName = `${secrets['project-name']} Organization`;
const organizationSearch = await oystehr.fhir.search<Organization>({
resourceType: 'Organization',
params: [{ name: 'name', value: organizationName }],
});
const organization = (await organizationSearch.unbundle())[0];

if (!organization) {
throw new Error(`Organization ${organizationName} not found`);
} else {
secrets['ORGANIZATION_ID'] = organization.id;
fs.writeFileSync(envFilePath, JSON.stringify(secrets, null, 2));
console.log(`Updated ORGANIZATION_ID=${organization.id} in ${envFilePath}`);
}

// get default billing resource
const defaultBillingResourceSearch = await oystehr.fhir.search<Organization>({
resourceType: 'Organization',
params: [{ name: 'name', value: 'Default Billing Provider' }],
});
const defaultBillingResource = (await defaultBillingResourceSearch.unbundle())[0];
if (!defaultBillingResource) {
throw new Error('Default billing resource not found');
} else {
secrets['DEFAULT_BILLING_RESOURCE'] = `${defaultBillingResource.resourceType}/${defaultBillingResource.id}`;
fs.writeFileSync(envFilePath, JSON.stringify(secrets, null, 2));
console.log(
`Updated DEFAULT_BILLING_RESOURCE=${defaultBillingResource.resourceType}/${defaultBillingResource.id} in ${envFilePath}`
);
}

// get sendgrid api key
const sendgridApiKey = secrets['SENDGRID_API_KEY'];
if (!sendgridApiKey) {
throw new Error('Sendgrid API key not found');
}

const m2ms = await oystehr.m2m.list();
const e2eM2M = m2ms.find((m2m) => m2m.name === 'E2E Tests M2M Client');
if (!e2eM2M) {
throw new Error('E2E Tests M2M Client not found');
}
const rotateResponse = await oystehr.m2m.rotateSecret({ id: e2eM2M.id });
secrets['AUTH0_CLIENT_TESTS'] = e2eM2M.clientId;
secrets['AUTH0_SECRET_TESTS'] = rotateResponse.secret!;
fs.writeFileSync(envFilePath, JSON.stringify(secrets, null, 2));
console.log(`Updated AUTH0_CLIENT_TESTS (${e2eM2M.clientId}) and AUTH0_SECRET_TESTS in ${envFilePath}`);

// create test environment files for apps with E2E M2M credentials
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could these could be managed by terraform similar to how we now manage the app env files?

const appsToUpdate = ['ehr', 'intake'];
const testEnvData = {
AUTH0_CLIENT_TESTS: secrets['AUTH0_CLIENT_TESTS'],
AUTH0_SECRET_TESTS: secrets['AUTH0_SECRET_TESTS'],
TEXT_USERNAME: 'e2euser@masslight.com',
TEXT_PASSWORD: '',
};

const testEnvDataIntake = {
...testEnvData,
PHONE_NUMBER: '',
TEXT_USERNAME: '',
TEXT_PASSWORD: '',
};

for (const app of appsToUpdate) {
const appTestEnvPath = `../apps/${app}/env/tests.${env}.json`;

// only create file if it doesn't exist
if (fs.existsSync(appTestEnvPath)) {
console.log(`Updating existing ${appTestEnvPath} with E2E M2M credentials`);
}
if (app === 'ehr') {
fs.writeFileSync(appTestEnvPath, JSON.stringify(testEnvData, null, 2));
} else {
fs.writeFileSync(appTestEnvPath, JSON.stringify(testEnvDataIntake, null, 2));
}
console.log(`Created ${appTestEnvPath} with E2E M2M credentials`);
}
}

main()
.then(() => console.log('Completed filling env with created resources data'))
.catch((error) => {
console.error(error);
throw error;
});
3 changes: 2 additions & 1 deletion deploy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"apply-staging": "./apply.sh staging",
"apply-demo": "./apply.sh demo",
"terraform-init": "terraform init --backend-config=./backend.config",
"terraform-setup": "npm run terraform-init && terraform workspace new local && terraform workspace new development && terraform workspace new testing && terraform workspace new staging && terraform workspace new demo && terraform workspace select local"
"terraform-setup": "npm run terraform-init && terraform workspace new local && terraform workspace new development && terraform workspace new testing && terraform workspace new staging && terraform workspace new demo && terraform workspace select local",
"fill-env-with-created-resources-data": "tsx fill-env-with-created-resources-data.ts"
},
"devDependencies": {
"@types/node": "^24.2.1"
Expand Down