Only this pageAll pages
Powered by GitBook
1 of 19

Greenstand Microservices

Loading...

Loading...

Loading...

Helpful Topics

Loading...

Domain Migration

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Service Review

Node 16 update & test

CI/CD for dev and test environment, node affinity

Fix linting errors if present, fix linting warnings

Review state of testing

Review state of open issues

Review state of open PRs

Fix security audit reports

  • npm audit --production - list vulnerable packages while ignoring dev dependencies

  • npm ls <package-name> - trace vulnerable packages to the base level packages that are using them

Typescript - review appropriateness of typescript for this service if the dev knows typescript

Microservices implementation structural review

Team Projects

Schema support for db-migrate

  • We decided to assign search_path to each microservice user (service and migration users) in terraform to resolve this need.

Developer Productivity

  • Codeowners file and agreements

  • Document observability tools and access

    • ELK: kubernetes.container.image :"greenstand/treetracker-api:1.4.0"

      • deployed image version can be found by visiting the root path of any deployed api

      • (such as )

  • Document about how deployment to dev environment works.

  • Figure out if dicpher can work with node16 or if there is a replacement (npm run decrypt)

    • Password for dipher is pinned

  • Sealed secrets: treetracker-infrastructure/sealed-secrets/scripts/create-database-secret.sh

  • resolve all lint warnings

    • Update lint settings to our likely - are there rules we want to enable?

  • remove co-mocha?

kubernetes.container.name: "treetracker-api"

  • u: admin p:xRrE6vZDUcRph%1P%0H

  • grafana (Performance monitoring)

  • YES, remove from the template

  • require lint errors to be removed to commit.

    • Reject on warnings in CI, but not in husky

    • Reject on errors in husky

    • Quaid adding his settings to microservice template

  • Resolve all vulnerabilities

    • Check for vulnerabilities when a new PR comes in.

  • Resolve dependencies errors for test files

    * https://eslint.org/docs/user-guide/configuring/rules

    • "import/no-extraneous-dependencies": [ "error", { "devDependencies": ["/*.test.js", "/*.spec.js"] } ]

  • Improve Documentation

    Improve Microservices Template

    `
    https://dev-k8s.treetracker.org/messaging/
    https://www.conventionalcommits.org/en/v1.0.0/
      import/no-extraneous-dependencies: [error, { devDependencies: true }]

    Implementation Pathway

    1. Release treetracker API into all envs

    2. Web map query schema & consumer into all env

      1. Release

      2. Validate data flow for raw captures

      3. Validate data flow for captures

      4. Validate or implement cluster generation

      5. Validate or implement

    3. Implement web map for raw captures in field data schema

    4. Finalize and Release Capture Matching Tool

    5. Finalize and Release Earnings Tool

    6. Finalize and Release Stakeholder Tool

    7. Migrate admin panel users to keycloak

    8. Migrate web map to captures table in treetracker schema

    Treetracker Domain Migration Sequencing

    Migration Sequence

    1. Migrate all legacy 'tree' table data into the fielddata.raw_capture table

      1. Includes creation session and device records for these captures

    2. Admin panel application: split capture verification tool from capture search tools.

    3. Go through the migration process for capture verification

      1. Documentation:

      2. Steps

    4. Migrate all remaining approved legacy 'tree' table data into the treetracker.capture table

      1. Apply to dev

      2. Apply to test

    5. Consolidation step to fix all planting organization assignments

      1. Priority between planting org per planter, per tree, and reported in field data to be determined

      2. Clean up all capture records to have valid organization assignments in the stakeholder tool.

    6. Update admin panel to using treetracker API for capture search tools.

    7. *Now, ready to get the capture matching tool working in deployment*

    8. GIS task: Identify very small geographic area in Freetown as a pilot for capture matching, and supply this as a spatial shape file (.geojson or .shp)

    9. GIS task: Identify all the capture records in the small geographic area that are the initial captures of the tree.

    10. Extract production data from small geographic area and add that data to dev and test database.

    11. Data task: insert treetracker.tree records for each initial capture. Now we have trees to capture match against.

      1. Perform in dev, test, and prod

    12. Implement capture matching frontend feature to allow filtering trees and captures by named geographic area. We will filter for initial use by the small geographic area defined above, which should be loaded into the regions API.

    13. Now we can deploy the capture matching tool to the beta infrastructure and try it out.

    1. Some legacy planter registrations reported to not have device identifier.

      1. This is inconvenient but not fixable with the v2 bulk pack format

    Domain modeling notes

    ‘Ground user’ and ‘individual’ are synonyms. Also ‘person’

    A mobile app user registers a wallet when using the mobile phone. Wallet registrations are uploaded along with their corresponding captures. A wallet is created for the wallet registration if it does not already exist. A grower account is created for each wallet, to track the identity of the person until they can be verified. Once a person is verified, a person record is created for them in the stakeholder system. Person records can receive payments for treetracker services.

    Milestone 1: Get rabbitmq verification messages functioning all the way through into production
    1. Get it working on dev infra

    2. Release and test on test infra

    3. Release and test on beta and prod infra

  • Milestone 2: Update admin panel to move approved data from field_data.raw_capture to treetracker.capture in the verification process (using the treetracker API), but for capture search still pulls from legacy database (using the legacy API)

    1. Get it working on dev infra

    2. Release and test on test infra

    3. Release and test on beta and prod infra

  • Apply to prod

    Notes:

    https://github.com/Greenstand/system-design-docs/blob/master/domain-migration/project_plan.md

    Contract Service

    Diagram for contract tables:

    Development Paths

    Capture Matching Release Project

    Capture Match road map 2021:

    Webmap query service

    https://lucid.app/lucidchart/4beb07f7-0ef0-4bf0-b88d-5548c2cda03b/edit?page=0_0#
    https://matthew-fullstackgis.gitbook.io/capture-matching/
    https://github.com/orgs/Greenstand/projects/36

    Microservices Directory

    Domain Services

    Treetracker API

    Source Code: https://github.com/Greenstand/treetracker-api

    Mounted Endpoint: https://{env}-k8s.treetracker.org/treetracker/

    Internal Endpoint: http://treetracker-api.treetracker-api

    Field Data API

    Source Code: https://github.com/Greenstand/treetracker-field-data

    Mounted Endpoint: https://{env}-k8s.treetracker.org/field-data/

    Internal Endpoint: http://treetracker-field-data.field-data-api/

    Source Code:

    Mounted Endpoint:

    Internal Endpoint:

    Source Code:

    Mounted Endpoint:

    Internal Endpoint:

    Source Code:

    Mounted Endpoint:

    Internal Endpoint:

    Source Code:

    Mounted Endpoint:

    Internal Endpoint:

    Source Code:

    CDN Endpoint: , also tiles2, tiles3, tiles4

    Mounted Endpoint:

    Source Code:

    Mounted Endpoint:

    Internal Endpoint:

    Source Code:

    This queue message consumer exposes no API

    Source Code:

    This queue message consumer exposes no API

    Source Code:

    This scheduled job exposes no API

    Source Code:

    Mounted Endpoint: not mounted

    Internal Endpoint:

    Source Code:

    Mounted Endpoint: not mounted

    Internal Endpoint:

    Source Code:

    Mounted Endpoint:

    Internal Endpoint:

    Source Code:

    Mounted Endpoint:

    Internal Endpoint:

    Capture Verification

    1. Update to 'verified' status requested to treetracker API

    2. Start transaction against treetracker schema to insert a new capture record

      1. If the record already exists, that's OK, just accept.

    3. Call legacy API and update capture (trees table record) to approved

    4. Commit transaction on treetracker schema

    5. Call field data API and update raw_capture record to approved

    if (3) fails, we rollback the treetracker schema transaction, therefore the raw_capture record remains unprocessed, and will be reprocessed by an operator in the future, should bring data into consistency.

    if (4) fails, same as if (3) fails, we no update. Will be reprocessed

    if (5) fails, the record will be reprocessed

    When we POST to captures/, send the UUID of the raw_capture to become the UUID of the approved capture record - these should always be unique.

    • Allows for easy reprocessing when there are outages

    • Allows for multiple concurrent POST messages to be collapsed and only create 1 capture record

    Testing Methodology

    • Only access the API, do not perform database check

      • First write seeded tests for GET endpoints

      • Then write tests for create and update that use GET endpoint to validate functionality

    Stakeholder API

    Earnings API

    Regions API

    Messaging API

    Map Tile Service

    Grower Account Query Service

    Web Map Query Service Consumer

    Bulk Pack Services

    Bulk Pack Consumer

    Bulk Pack Processor

    Bulk Pack Transformer V1

    Bulk Pack Transformer V2

    Legacy Services

    Admin API

    Web Map API

    Cloud Services

    Airflow API

    CKAN API

    https://github.com/Greenstand/treetracker-stakeholder-api
    https://{env}-k8s.treetracker.org/stakeholder/
    http://treetracker-stakeholder-api.stakeholder-api/
    https://github.com/Greenstand/treetracker-earnings-api
    https://{env}-k8s.treetracker.org/earnings/
    http://treetracker-earnings-api.earnings-api/
    https://github.com/Greenstand/treetracker-regions-api
    https://{env}-k8s.treetracker.org/regions/
    http://treetracker-regions-api.regions-api/
    https://github.com/Greenstand/treetracker-messaging-api
    https://{env}-k8s.treetracker.org/messaging/
    http://treetracker-messaging-api.messaging-api/
    https://github.com/Greenstand/node-mapnik-1
    https://tiles1.treetracker.org/
    https://{env}-k8s.treetracker.org/tiles/
    https://github.com/Greenstand/treetracker-grower-account-query
    https://{env}-k8s.treetracker.org/query/
    http://treetracker-grower-account-query.query/
    https://github.com/Greenstand/webmap-query-service-consumer
    https://github.com/Greenstand/bulk-pack-consumer
    https://github.com/Greenstand/bulk-pack-processor
    https://github.com/Greenstand/bulk-pack-transformer
    http://bulk-pack-transformer.bulk-pack-services/
    https://github.com/Greenstand/bulk-pack-transformer-v2
    http://bulk-pack-transformer-v2.bulk-pack-services/
    https://github.com/Greenstand/treetracker-admin-api
    https://{env}-k8s.treetracker.org/api/admin/
    http://treetracker-admin-api.admin-api/
    https://github.com/Greenstand/treetracker-web-map-api
    https://{env}-k8s.treetracker.org/webmap/
    http://treetracker-webmap-api-ambassador-svc.webmap/

    Messaging System Rollout Decisions

    1. We will control available authors manually at first, by populating the authors table

    2. We will control access to admins using our normal process

    3. For announce and survey messages, we look up associated Grower Accounts in the treetracker API, and then match those against enabled authors

      1. We need to extend this approach to respect the organizational hierarchy, which may mean a query service for grower accounts.

      2. A query service for grower accounts would also more easily respect the region filters

    APIs should return created objects, and get queryable via these object Ids

  • Seeding should be done using knex, and use the same seeding scenarios provided to the frontend developers

  • Best Practices

    • Do specific seeding for each integration test, don't use a generic seed that isn't for the specific test

    • Objects posted to an API should be explicitly expressed within the integration test file (?)

    • Use async syntax

      • it('should do something', async function() { ...

    • Log request errors in test to facilitate debugging

      • if(res.error ) { console.log(res.error); }

    • Stub functions on a per-test basis

  • Running integration tests

    • Use the test-integration-working command to specific a specific test file rather than running all

    • Use .only to specify a single test

    • Don't leave .only in place after getting the test to pass

    • TODO

      • Need a solution to print response errors when expect fails on an error code

        • https://github.com/visionmedia/supertest/issues/12

      • What tests are required?

        • Endpoint behaviors

        • How much validation?

          • Payload structure vs 404 responses

    Unit Tests

    Integration Tests

    Software Layers

    Handler

    • HTTP Domain

    • All error and success codes

    • API payload validation

    • Calls a service or a model function

    • Orchestration between services and the domain model

      • Database session

      • External APIs

    • Domain logic

    • Accesses repositories

    • Allowances

    • Accesses the database, performs CRUD operations

    • One repository for each database table in RDMS

  • JOI tests could be unit tests

  • Cloud Services (such as RabbitMQ or S3)
    We allow HTTP 404 errors to be thrown from within the model as a convenience for now

    Service

    Model

    Repository

    Domain Migration M2

    Goal: Admin panel to not read trees table directly. Build new treetracker service with API to create capture records.

    Build a new service to be the source of truth of all approved captures.

    This service is to own all the capture records along with tagging and other details created during the approval in the admin panel.

    • Service is already created, known as treetracker-api.

    • Some verbs (GET/POST/PATCH) may be missing but capture, tree, and planter resources have been defined.

    • We may need to add additional search parameters as needed by other services or for domain goals.

    • This appears to be implemented, but has not been tested or operated

    • We added planter (ground_user) and tree to this service already. That will be the scope of the service. The service is called treetracker serivce, and is stored in the treetracker-api repository

    • Data populated in the admin panel verify tool, should ONLY be raw captures from the field data service that has not been verified (status=unprocessed)

    • This will require the admin panel frontend team to split the current verify tool into a 'verification' tool and a 'capture edit' tool.

    • Only verified captures can be 'edited' through the 'capture edit' tool, and only very limited fields can be edited. mostly this is tagging.

    • (1) POST to treetracker/capture

    • (2) PATCH to the legacy admin panel API

    • (3) PATCH back to the field service to mark as approved/rejected in field data schema.

    • Remove the feature that emitted event for approved captures from Change 1 earlier (in the legacy API). The reason we will write to trees table from admin panel (or orchestration layer) is for back-ward compatability with web-map.

    • This appears to already be complete. Need to be tested and operated.

    Migrate data from trees table in main db to captures table in the new captures service of all approved tree entries.

    • ETL job. Can be implemented as one-off airflow job.

    Update token generation process and refer to ids from captures instead of trees as reference association.

    • This refers to the token minting scripts, now being run in airflow

    • capture_id for each token needs to reference the id of treetracker.capture rather than public.trees. This also requires updating the HATEOAS link in the token payload (wallet API) to point to the new capture service.

    • Update all existing tokens to use treetracker.capture.id as capture_id

    • The events consumed in web-map layer will trigger api calls to get the tokens impacted and update the capture view in the web-layer to assign the updated wallet owner.

    • Requires endpoint to look up all tokens in any transfer by transfer id.

    • Discuss how organizational hierarchy is captured in the webmap schema (JSON blob or table structure)

    • Breaking changes will trigger a new major revision, and a new deployment mount point

      • Frontend engineers will be responsible for migrating to the new mount point

      • If database changes are necessary, if the service is not in production it's ok to make a breaking change. Otherwise check with engineering leadership.

    Later, there will also be some tools for cleaning field data, but that's out of scope for now.

    TODO: Think through what happens if one of these calls fails.
    • I think it's ok, it just causes some work to be repeated.

    • For instance, if POST to (1) succeeds but PATCH to (2) fails,

      • Then raw capture will need to be reverified (because (3) did not execute)

      • So the data will just be reprocessed by the operator in this case

    • We will probably resolve this by using an orchestration layer

      • The treetracker service itself can handle the orchestration, and also keep track of failed requests to (2) and (3) to retry.

      • This could also use a queue, or implement a separate service to handle the orchestration.

    planter, grower, and fielduser should all become 'ground_user'

  • planteridentifier should become 'ground_username'

  • planter class, table, and API resource should become 'ground_user'

  • On creation of a capture record, emit events indicating the creation of the capture record to a Topic. This event is meant for web-map to build a view of all approved captures.

    The scope of this capture service is yet to be determined if it can own more than just capture records. We could call this as captures service or treetracker-capture-records.

    Change Admin panel

    Change Admin panel to use field data API instead of directly reading trees table for tree capture verification.

    Admin panel to invoke capture service API to record the approved capture, update the trees table as usual and then update field data service to mark the data as approved/rejected.

    Add a consumer for the capture creation events in the web-map layer and build a view for all captures in web-map specific db.

    Wallet API service to emit events when transfer of tokens occur to be consumed by web-map layer for Impact owner view.

    Trigger discussion about impact manager map (the view in web-map for planter orgs) in the web-map layer and its needs to shape and update milestone 3.

    Address domain naming issues (variable and class names)

    Bulk Pack

    Bulk Pack Processing v2

    Field data schem

    Ingestion Process by Entity Type

    wallet_registration

    Wallet registrations need to be stored in the field data schema in the wallet_registrations table, but they also need to be used to upsert (create or update) grower account records in the treetracker API. A wallet registration also needs the grower account id field populated.

    Suggested workflow:

    1. Bulk pack transformer calls PUT on grower_account in treetracker API for grower account with identifier that matches wallet

      1. If account exists, update

        1. Only update the name, phone, and email for now

      2. Else, insert

        1. Insert identifier, name, phone, email, and photo

    2. POST to /planter path on bulk-pack-transformer-1 to populate legacy schema

    3. Populate grower account id in wallet registration data, and POST into field data api

      1. If wallet registration id is already present, field data api will do nothing (return 200)

    4. Return 200

    device configuration records are simply inserted in v2, but in v1 they were upserted.

    1. POST device configuration payload to bulk-pack-transformer-1

      1. bulk-pack-transformer-1 returns 200 if already present

    2. POST device configuration payload to field data API

    session does not exist in the v1 bulk-pack-transformer.

    1. POST to session path on field data API

      1. field data API returns 200 if already present

    2. Return 200

    1. POST capture data to bulk-pack-transformer-1

      1. If data exists this API returns 200, else a new record is inserted

      2. This API also propagates the record to field data API, where a queue message is emitted

    Bulk Pack Format v2 :

    Session

    • track_url

    • organization

    Wallet Registration

    • phone or email is nullable, but not both

    Raw Capture

    • session_id is nullable for v1 endpoints only

    • reference_id

    • note

    Device Configuration

    • No nullable fields

    Insert 'tree' as 'capture' will fail until corresponding device and registration records have been inserted, because there's no session Once device and registration records are inserted, then it is possible to insert a session id using SESSION UUID = device_identifier + planter_identifier In v2 bulk pack endpoint /v1/tree is responsible for ensuring that the session record exists for a v1 tree. This session can then be used as the session for inserting the v1 'tree' record as a v2 'capture' record

    1. process sends tree record to /v1/tree in v2

    2. v2 calculates SESSION UUID = device_identifier + planter_identifier using uuid-string

    3. v2 checks for a session that matches this

    Old versions:

    • greenstand/bulk-pack-processor:1.2.7

    • greenstand/bulk-pack-transformer:1.6.2

    To some extent, this decision is up to the engineer implementing this step of the domain migration.

  • field data API returns 200 if already present

  • Return 200

  • POST capture data to field data API

    1. Since step 1 already propagated to this API, a 200 should be returned based on uuid

    2. Since 200 is returned, queue message should not re-emitted

    extra_attributes
  • rejection_reason

  • If no session exists, it looks up device_configuration_id and wallet_registration_id using device_identifier and planter_identifier, and if they exist it creates a session record. If they don't exist yet, it fails until next time.
  • Assuming a session exists or was inserted in (4), we can now insert the capture record using his session uuid.

  • device_configuration

    Suggested Workflow

    session

    Suggested Workflow

    capture

    Orchestration Diagram

    Improved orchestration architecture

    Deprecated proposal for orchestration

    Nullable Columns for Field Data Tables

    Workflow Notes

    Release

    Grower Unique Identifier Notes

    treetracker.grower_account.wallet

    • is the same as messaging.author.handle

    • is the same as (public.planter.phone || public.planter.email)

    messaging.author.handle

    • does not necessarily have a corresponding treetracker.grower_account.wallet

    • does not have a matching (public.planter.phone || public.planter.email) if it does not have a treetracker.grower_account.wallet

    CORS

    • Disable CORS for development by importing "cors" and then use it to disable cors for 'development' in each microservice

      if (process.env.NODE_ENV === 'development') { log.info('disable cors'); app.use(cors()); }

      EXAMPLE w/ cors:

      EXAMPLE w/ header options:

    Chrome extension that will let you disable CORS when you want without having to shutdown

    • Simple option: Cross Domain CORS or Moesif CORS - You can click the red button to make it active when you want to disable CORS.

    • Lots of settings, but you have to create an account: Requestly

  • Try different browsers or the proxy setting for CRA. https://medium.com/swlh/avoiding-cors-errors-on-localhost-in-2020-5a656ed8cefa

  • Create your own proxy server: https://medium.com/nodejsmadeeasy/a-simple-cors-proxy-for-javascript-applications-9b36a8d39c51

  • Debugging: https://httptoolkit.tech/blog/how-to-debug-cors-errors/ Explain settings: https://web.dev/cross-origin-resource-sharing/ CORS module settings: https://expressjs.com/en/resources/middleware/cors.html

    Disable CORS for working on a microservice API with localhost

    https://github.com/Greenstand/treetracker-stakeholder-api/blob/main/server/app.js
    https://github.com/Greenstand/treetracker-web-map-api/blob/master/src/app.js

    Helpful reading!

    Treetracker Schema Attributes

    **Capture**

    id
    Not null, default uuid(), immutable

    reference_id

    Nullable, immutable

    session_id

    Not null, immutable

    **Tree**

    id
    Not null, default uuid(), immutable

    **Tag**

    id
    Not null, default uuid(), immutable

    **Capture_Tag**

    id
    Not null, default uuid(), immutable

    **Tree_Tag**

    id
    Not null, default uuid(), immutable

    **Grower_Account**

    id
    Not null, default uuid(), immutable

    **Status Enumeration** active deleted Session - Field Data Schema Device Configuration - Field Data Schema

    lon

    Not null, immutable

    gps_accuracy

    Not null, immutable

    estimated_geometric_location

    Not null, immutable

    estimated_geographic_location

    Not null, immutable

    species_id

    Nullable, mutable (for now)

    morphology

    Nullable, mutable (for now)

    age

    Nullable, mutable (for now)

    attributes

    Nullable, immutable

    status

    Not null, mutable

    created_at

    Default now() on create, not null, immutable

    updated_at

    Default now() on create or update, not null, mutable

    created_at

    Default now() on create, not null, immutable

    updated_at

    Default now() on create or update, not null, mutable

    created_at

    Default now() on create, not null, immutable

    updated_at

    Default now() on create or update, not null, mutable

    created_at

    Default now() on create, not null, immutable

    updated_at

    Default now() on create or update, not null, mutable

    organization_id

    Nullable, uuid of stakeholder

    name

    Not null, mutable

    email

    Nullable, mutable - one of email or phone required to be non-null

    phone

    Nullable, mutable - one of email or phone required to be non-null

    image_url

    Not null, mutable

    image_rotation

    Default 0, not null

    status

    Not null, default active

    first_registration_at

    Immutable, timestamp of first field registration

    created_at

    Default now() on create, not null, immutable

    updated_at

    Default now() on create or update, not null, mutable

    tree_id

    Nullable, immutable but clearable

    image_url

    Not null, immutable

    lat

    Not null, immutable

    lon

    Not null, immutable

    gps_accuracy

    Not null, immutable

    estimated_geometric_location

    Not null, immutable

    estimated_geographic_location

    Not null, immutable

    planter_id

    Not null, immutable

    planter_photo_url

    Not null, immutable

    planter_username

    Not null, immutable

    planting_organization_id

    Not null, immutable

    device_configuration_id

    Not null, immutable

    species_id

    Nullable, mutable (for now)

    morphology

    Nullable, mutable (for now)

    age

    Nullable, mutable (for now)

    attributes

    Nullable, immutable

    note

    Nullable, immutable

    domain_specific_data

    Nullable, immutable

    status

    Not null, mutable

    created_at

    Default now() on create, not null, immutable

    updated_at

    Default now() on create or update, not null, mutable

    latest_capture_id

    Not null, mutable

    image_url

    Not null, mutable

    lat

    Not null, immutable

    name

    Not null, immutable

    public

    Not null, mutable

    status

    Not null, mutable

    capture_id

    Not null, immutable

    tag_id

    Not null, immutable

    status

    Not null, mutable

    tree_id

    Not null, immutable

    tag_id

    Not null, immutable

    status

    Not null, mutable

    wallet_id

    Not null, immutable

    wallet

    Not null, immutable

    person_id

    Nullable, uuid of stakeholder

    https://app.gitbook.com/o/-MXNadx4i6aOZ12XcStA/s/-MXtAguKaWMpiXXl0UDb/upload-pack-format
    Capture Matching Production Readiness • GreenstandGitHub
    Logo