Giter Club home page Giter Club logo

microservice's Introduction

⚠️ Replaced by OpenFn/lightning

OpenFn/microservice (deprecated)

Supported by OpenFn, DIAL, UNICEF, and with UK aid from the British people.

🔥 The documentation for this project can be found at docs.openfn.org. 🔥

Docker build

`docker build -t openfn/microservice:<version> .`

Docker compose or run

Assuming you've got an .env and a project directory with a project.yaml spec:

docker-compose up
docker run -v <path-to-your-project-folder>:/home/microservice/<path-to-your-project-folder> \
  --env-file <path-to-your-env-file> \
  --network host \
  openfn/microservice:<version>

Instant OpenHIE

First ensure you have cloned this repository, then from the instant folder (the folder where you'd typically run your "Instant OpenHIE commands") run the following command:

yarn docker:instant init openfnMicroservice --custom-package="<path to this folder>"

Test the deployment by posting messages to port 4001.

Development up and running guide

Installing pre-requisites

  • It's recommended to use nvm to install NodeJs. At the time of writing, the platform-app instance on US servers and the dockerized microservice instance are running on NodeJs v12.20.2.
  • It's highly recommended to use asdf for managing your Erlang and Elixir versions. After installing asdf, install Erlang and Elixir.
  • asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git
  • asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git
  • At the time of writing, we're using Erlang/OTP 23 [erts-11.1.8] and Elixir 1.11.3 (compiled with Erlang/OTP 23)
  • Also at the time of writing, we're assuming that logs are ingested by a host service so we don't depend on a database!
  • Once NodeJs, Erlang and Elixir are installed, proceed to the application build/setup section below.

Application build/setup

  • Clone this repo with git clone [email protected]:OpenFn/microservice.git
  • Enter the directory with cd microservice
  • Install dependencies with mix setup
  • Run the tests with mix test
  • Make a project directory to hold your project artifacts with mkdir sample-project
  • Create a new project specification with cp project.yaml.example ./sample-project/project.yaml
  • Create a .env file with cp .env.example .env
  • Install necessary adaptors via npm install @openfn/language-http --prefix priv/openfn/runtime/node_modules --no-save --no-package-lock --global-style
  • Start your microservice server with env $(cat .env | grep -v "#" | xargs ) iex -S mix phx.server

microservice's People

Contributors

aleksa-krolls avatar dependabot[bot] avatar ritazagoni avatar stuartc avatar taylordowns2000 avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

microservice's Issues

Replace shell API with NodeJS api?

@stuartc , i'm interested in your thoughts on this (https://github.com/revelrylabs/elixir-nodejs) for use in dispatcher: NodeJS.call(module, args \\ [], opts \\ [])

Options
:name - (optional) The name used for supervisor registration. Defaults to Elixir.NodeJS.Supervisor.
:path - (required) The path to your Node.js code's root directory.
:pool_size - (optional) The number of workers. Defaults to 4.

I wonder if we could save on the CPU and memory usage by having each microservice manage its own pool of NodeJS workers. If core will be available both with and without the CLI, does this make more sense going forward?

Potential error when running multiple jobs simultaneously

Getting a "sh: cd: line 1: can't cd to /opt/app/project/node_modules: No such file or directory" when attempting to run a project that triggers 3 jobs simultaneously from an inbound message:

web_1  | [info] POST /inbox
web_1  | [debug] Processing with MicroserviceWeb.Receiver.receive/2
web_1  |   Parameters: %{"__query_params" => %{}, "a" => 1, "baz" => "qux", "foo" => "other thing", "form" => "COVID_case_registration", "formId" => "Tier_2_Needs_Assessment_v4", "from" => "OpenFn", "ju_ju" => "bean", "nüumber" => 2, "surveyId" => 37479}
web_1  |   Pipelines: [:api]
web_1  | [debug] Receiver.receive/2 called
web_1  | [debug] env: NODE_PATH=/opt/app/project/node_modules
web_1  | cmd:   (cd $NODE_PATH && ./.bin/core execute   -e /opt/app/tmp/expression-1619185429-1-cz7ixx.js   -l @openfn/[email protected]   -s /opt/app/tmp/state-1619185429-1-1kp57ip.json   -o /opt/app/tmp/final_state-1619185429-1-ezb5td.json 
web_1  |   
web_1  |   
web_1  |   )
web_1  | 
web_1  | 
web_1  | [debug] env: NODE_PATH=/opt/app/project/node_modules
web_1  | cmd:   (cd $NODE_PATH && ./.bin/core execute   -e /opt/app/tmp/expression-1619185429-1-13qlsku.js   -l @openfn/[email protected]   -s /opt/app/tmp/state-1619185429-1-1igdd2z.json   -o /opt/app/tmp/final_state-1619185429-1-nnmxoe.json 
web_1  |   
web_1  |   
web_1  |   )
web_1  | 
web_1  | 
web_1  | [debug] env: NODE_PATH=/opt/app/project/node_modules
web_1  | cmd:   (cd $NODE_PATH && ./.bin/core execute   -e /opt/app/tmp/expression-1619185429-1-1tww7ug.js   -l @openfn/[email protected]   -s /opt/app/tmp/state-1619185429-1-14cmllp.json   -o /opt/app/tmp/final_state-1619185429-1-1kzuq7v.json 
web_1  |   
web_1  |   
web_1  |   )
web_1  | 
web_1  | 
web_1  | [info] Sent 202 in 4ms
web_1  | [debug] "sh: cd: line 1: can't cd to /opt/app/project/node_modules: No such file or directory"
web_1  | [debug] "sh: cd: line 1: can't cd to /opt/app/project/node_modules: No such file or directory"
web_1  | [debug] "sh: cd: line 1: can't cd to /opt/app/project/node_modules: No such file or directory"

Using the following project.yaml:

credentials:
  dhis2-play-covid-action:
    hostUrl: '******'
    password: '******'
    username: '******'
  google:
    accessToken: '******'
    expiresIn: '******'
    familyName: '******'
    givenName: '******'
    id: '******'
    idToken: '******'
    locale: '******'
    name: '******'
    picture: '******'
    refreshToken: '******'
    scope: '******'
    tokenType: '******'
  salesforce:
    loginUrl: '******'
    password: '******'
    securityToken: '******'
    username: '******'
jobs:
  covid-19-tei-(dhis2):
    adaptor: '@openfn/[email protected]'
    credential: 'dhis2-play-covid-action'
    expression: >
      // See list of cases here: https://covid19.dhis2.org/demo/dhis-web-tracker-capture/index.html#/?program=DM9n1bUw8W8
      createTEI({
        orgUnit: 'GD7TowwI46c',
        trackedEntityType: 'MCPQUTHX1Ze',
        attributes: [
          {
            attribute: 'he05i8FUwu3', // case id
            value: dataValue('body._id'),
          },
          {
            attribute: 'sB1IHYu2xQT', // first name
            value: dataValue('body.Patient_name'),
          },
          {
            attribute: 'ENRjVGxVL6l', // last name
            value: dataValue('body.Last_Name_of_Patient'),
          },
          {
            attribute: 'Rv8WM2mTuS5', // age
            value: dataValue('body.Age'),
          },
        ],
        enrollments: [
          {
            orgUnit: 'GD7TowwI46c',
            program: 'DM9n1bUw8W8',
            programState: 'sAV9jAajr8x',
            enrollmentDate: dataValue('body.Date'),
            incidentDate: dataValue('body.Covid_19_suspected_criteria/Speciman_Collection_date')
          },
        ],
      });
    trigger: 'covid-case-submitted'
  covid-case-registration-(gs):
    adaptor: '@openfn/[email protected]'
    credential: 'google'
    expression: >
      appendValues({
        spreadsheetId: '1EFkY4zD4qqxnJdH-QaeasKd1zXC-1sNKpEg08W-3sT0',
        range: 'COVID-19 Cases!A2',
        values: state => {
          const kobo = state.data.body;
          
          console.log('Submission data: ' + JSON.stringify(kobo, null, 2));
          
          return [
            [
              kobo['National_ID'],
              kobo['Patient_name'],
              kobo['Last_Name_of_Patient'],
              kobo['Sex'],
              kobo['Age'],
              kobo['Comments'],
              kobo['Date']
            ],
          ];
        },
      });
    trigger: 'covid-case-submitted'
  create-sf-contact:
    adaptor: '@openfn/[email protected]'
    credential: 'salesforce'
    expression: >
      create('Contact', {
        FirstName: dataValue("body.Patient_name"),
        LastName: dataValue('body.Last_Name_of_Patient'),
        Age__c: dataValue("body.Age"),
        Sex__c: dataValue("body.Sex"),	
        Case_ID__c: dataValue("body.National_ID"),	
        Comments__c: dataValue("body.Comments")
      });
    trigger: 'covid-case-submitted'
triggers:
  covid-case-submitted:
    criteria: '{"form":"COVID_case_registration"}'

Startup error on docker run

To debug:

(node:56) UnhandledPromiseRejectionWarning: Error: EACCES: permission denied, mkdir '/opt/app/assets/node_modules/.cache/hard-source'
    at Object.mkdirSync (fs.js:921:3)
    at sync (/opt/app/assets/node_modules/mkdirp/index.js:72:13)
    at sync (/opt/app/assets/node_modules/mkdirp/index.js:78:24)
    at Function.sync (/opt/app/assets/node_modules/mkdirp/index.js:78:24)
    at runReadOrReset (/opt/app/assets/node_modules/hard-source-webpack-plugin/index.js:263:16)
    at _next0 (eval at create (/opt/app/assets/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:58:17)
    at eval (eval at create (/opt/app/assets/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:76:1)
(node:56) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:56) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Documentation sections to add...

Sections to add to the docs to better qualify best-fit implementations...

  1. Prerequisites (specs & resource requirements to share with interested teams)
  2. Example use cases supported in the current state (vs. roadmap use cases... ideally every technical feature is linked to a real-world requirement)
  3. Ongoing management (resource requirements - technical & human)

Allow env to be passed through engine at runtime

This is likely an engine issue as well, but we need to make sure that env variables are properly passed to engine at runtime, not just during (for example) docker build. @stuartc , do you know why the commented out lines don't work in config/config.exs in Microservice during docker build?

config :microservice, Microservice.Engine,
  # TODO: allow dynamic configuration of project path. (this is currently set during docker build.)
  # project_config: "file://" <> System.get_env("PROJECT_PATH") <> "/project.yaml"
  project_config: "file://project/project.yaml"

Specifically, we get a "PROJECT_PATH" is nil type of error, because docker build is being run without a PROJECT_PATH env. I'm wondering why this is even being evaluated during docker build. The offending docker build command is mix do compile.

Ideally, we'd let users specify the path they want to keep their project.yaml in... some might keep it in the root, some might keep it in /my-project along with a bunch of job.js files or even credential.json files.

I know some deps allow for runtime configuration like this:
image

Thoughts?

Update docker run docs

Currently, the docs for Docker usage are based on pre-engine microservices. Update these once engine usage is confirmed.

mvp

MVP, supported by the DIAL OSC

  1. An open source server application (this application, microservice.)
  2. The open source inbox application, which will be used by both platform
    and microservice to handle HTTP requests
  3. The open source dispatcher application which will be used by both
    platform and microservice to execute jobs using OpenFn/core.
  4. A Dockerfile that, given a job, an adaptor, and a credential, is able
    to automatically generate a container that runs the microservice for that
    job.

Roadmap for this application

  • mix phx.server receives receipts and sends 200
  • Chain jobs together
  • Timer jobs can keep state
  • tmp files are deleted after job is run
  • bring core out of package.json
  • endpoint gets URL and PORT from .env
  • ShellWorker picks up config from .env
  • ShellWorker executes it, given preloaded job, cred, adaptor, and core.
  • ShellWorker can pipe to stdout.
  • Write tests for everything.

Sync responses?

Could we provide a config option that held the HTTP request open (no 202/accepted) until the job is done running?

Flow jobs bug - job with a "flow" trigger is using the credential of the parent job

Bug

See related Community post with a full description of the issue: https://community.openfn.org/t/in-microservice-the-job-using-flow-trigger-always-use-the-configuration-from-the-previous-job-even-though-it-has-its-own-credential/234

Job A, assigned with Credential A, the trigger is cron every 5 minutes
Job B, assigned with Credential B, the trigger is “Flow” trigger of Job A

Job B is always using the Credential A as its state.configuration, instead of Credential B.

@stuartc It works on platform, so it sounds like there is a Flow Jobs bug here. Can you please write a test to replicate and troubleshoot the issue?

Instant OpenHIE Compliance

Under WP1 of the DS grant, we need to make some modifications to our Docker/Kubernetes setup to acheive compliance with Instant OpenHIE. Pending updates from a discussion with Jembi, we expect this to consist of three things:

  1. an instant.json file
  2. docker compose scripts
  3. kubernetes yaml files

See full details here: https://openhie.github.io/instant/docs/how-to/creating-packages/

And the github for the Instant project: https://github.com/openhie/instant

From Jembi: Example Instant OpenHIE packages that we're currently working on, to give an idea of the structure/content of a package:
https://github.com/jembi/who-covid19-surveillance-package
https://github.com/jembi/covid19-immunization-tracking-package

Add ability to upload the project config via HTTP

Configuration via containers in a non-local (k8s, remote docker) environment is very difficult as there is no local volume mount (at least not without significant effort).

How about the ability for the app to boot without config, and have uploaded via http - storing it locally (or in a pvc/volume) for subsequent startups.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.