Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
We will control available authors manually at first, by populating the authors table
We will control access to admins using our normal process
For announce and survey messages, we look up associated Grower Accounts in the treetracker API, and then match those against enabled authors
We need to extend this approach to respect the organizational hierarchy, which may mean a query service for grower accounts.
A query service for grower accounts would also more easily respect the region filters
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
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: https://github.com/Greenstand/treetracker-stakeholder-api/blob/main/server/app.js
EXAMPLE w/ header options: https://github.com/Greenstand/treetracker-web-map-api/blob/master/src/app.js
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
Source Code: https://github.com/Greenstand/treetracker-api
Mounted Endpoint: https://{env}-k8s.treetracker.org/treetracker/
Internal Endpoint: http://treetracker-api.treetracker-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: https://github.com/Greenstand/treetracker-stakeholder-api
Mounted Endpoint: https://{env}-k8s.treetracker.org/stakeholder/
Internal Endpoint: http://treetracker-stakeholder-api.stakeholder-api/
Source Code: https://github.com/Greenstand/treetracker-earnings-api
Mounted Endpoint: https://{env}-k8s.treetracker.org/earnings/
Internal Endpoint: http://treetracker-earnings-api.earnings-api/
Source Code: https://github.com/Greenstand/treetracker-regions-api
Mounted Endpoint: https://{env}-k8s.treetracker.org/regions/
Internal Endpoint: http://treetracker-regions-api.regions-api/
Source Code: https://github.com/Greenstand/treetracker-messaging-api
Mounted Endpoint: https://{env}-k8s.treetracker.org/messaging/
Internal Endpoint: http://treetracker-messaging-api.messaging-api/
Source Code: https://github.com/Greenstand/node-mapnik-1
CDN Endpoint: https://tiles1.treetracker.org/, also tiles2, tiles3, tiles4
Mounted Endpoint: https://{env}-k8s.treetracker.org/tiles/
Source Code: https://github.com/Greenstand/treetracker-grower-account-query
Mounted Endpoint: https://{env}-k8s.treetracker.org/query/
Internal Endpoint: http://treetracker-grower-account-query.query/
Source Code: https://github.com/Greenstand/webmap-query-service-consumer
This queue message consumer exposes no API
Source Code: https://github.com/Greenstand/bulk-pack-consumer
This queue message consumer exposes no API
Source Code: https://github.com/Greenstand/bulk-pack-processor
This scheduled job exposes no API
Source Code: https://github.com/Greenstand/bulk-pack-transformer
Mounted Endpoint: not mounted
Internal Endpoint: http://bulk-pack-transformer.bulk-pack-services/
Source Code: https://github.com/Greenstand/bulk-pack-transformer-v2
Mounted Endpoint: not mounted
Internal Endpoint: http://bulk-pack-transformer-v2.bulk-pack-services/
Source Code: https://github.com/Greenstand/treetracker-admin-api
Mounted Endpoint: https://{env}-k8s.treetracker.org/api/admin/
Internal Endpoint: http://treetracker-admin-api.admin-api/
Source Code: https://github.com/Greenstand/treetracker-web-map-api
Mounted Endpoint: https://{env}-k8s.treetracker.org/webmap/
Internal Endpoint: http://treetracker-webmap-api-ambassador-svc.webmap/
Migrate all legacy 'tree' table data into the fielddata.raw_capture table
Includes creation session and device records for these captures
Admin panel application: split capture verification tool from capture search tools.
Go through the migration process for capture verification
Steps
Milestone 1: Get rabbitmq verification messages functioning all the way through into production
Get it working on dev infra
Release and test on test infra
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)
Get it working on dev infra
Release and test on test infra
Release and test on beta and prod infra
Migrate all remaining approved legacy 'tree' table data into the treetracker.capture table
Apply to dev
Apply to test
Apply to prod
Consolidation step to fix all planting organization assignments
Priority between planting org per planter, per tree, and reported in field data to be determined
Clean up all capture records to have valid organization assignments in the stakeholder tool.
Update admin panel to using treetracker API for capture search tools.
*Now, ready to get the capture matching tool working in deployment*
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)
GIS task: Identify all the capture records in the small geographic area that are the initial captures of the tree.
Extract production data from small geographic area and add that data to dev and test database.
Data task: insert treetracker.tree records for each initial capture. Now we have trees to capture match against.
Perform in dev, test, and prod
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.
Now we can deploy the capture matching tool to the beta infrastructure and try it out.
Some legacy planter registrations reported to not have device identifier.
This is inconvenient but not fixable with the v2 bulk pack format
**Capture**
reference_id
Nullable, immutable
session_id
Not null, immutable
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
**Tree**
latest_capture_id
Not null, mutable
image_url
Not null, mutable
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
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
**Tag**
name
Not null, immutable
public
Not null, mutable
status
Not null, mutable
created_at
Default now() on create, not null, immutable
updated_at
Default now() on create or update, not null, mutable
**Capture_Tag**
capture_id
Not null, immutable
tag_id
Not null, 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
**Tree_Tag**
tree_id
Not null, immutable
tag_id
Not null, 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
**Grower_Account**
wallet_id
Not null, immutable
wallet
Not null, immutable
person_id
Nullable, uuid of stakeholder
organization_id
Nullable, uuid of stakeholder
name
Not null, mutable
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
**Status Enumeration** active deleted Session - Field Data Schema Device Configuration - Field Data Schema
Release treetracker API into all envs
Web map query schema & consumer into all env
Release
Validate data flow for raw captures
Validate data flow for captures
Validate or implement cluster generation
Validate or implement
Implement web map for raw captures in field data schema
Finalize and Release Capture Matching Tool
Finalize and Release Earnings Tool
Finalize and Release Stakeholder Tool
Migrate admin panel users to keycloak
Migrate web map to captures table in treetracker schema
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
Goal: Admin panel to not read trees
table directly. Build new treetracker service with API to create capture records.
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
trees
table for tree capture verification. 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.
Later, there will also be some tools for cleaning field data, but that's out of scope for now.
(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.
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.
To some extent, this decision is up to the engineer implementing this step of the domain migration.
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.
planter, grower, and fielduser should all become 'ground_user'
planteridentifier should become 'ground_username'
planter class, table, and API resource should become 'ground_user'
‘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.
Update to 'verified' status requested to treetracker API
Start transaction against treetracker schema to insert a new capture record
If the record already exists, that's OK, just accept.
Call legacy API and update capture (trees table record) to approved
Commit transaction on treetracker schema
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
Capture Matching Release Project
Capture Match road map 2021:
Webmap query service
Bulk Pack Processing v2
Field data schem
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.
Bulk pack transformer calls PUT on grower_account in treetracker API for grower account with identifier that matches wallet
If account exists, update
Only update the name, phone, and email for now
Else, insert
Insert identifier, name, phone, email, and photo
POST to /planter path on bulk-pack-transformer-1 to populate legacy schema
Populate grower account id in wallet registration data, and POST into field data api
If wallet registration id is already present, field data api will do nothing (return 200)
Return 200
device configuration records are simply inserted in v2, but in v1 they were upserted.
POST device configuration payload to bulk-pack-transformer-1
bulk-pack-transformer-1 returns 200 if already present
POST device configuration payload to field data API
field data API returns 200 if already present
Return 200
session does not exist in the v1 bulk-pack-transformer.
POST to session path on field data API
field data API returns 200 if already present
Return 200
POST capture data to bulk-pack-transformer-1
If data exists this API returns 200, else a new record is inserted
This API also propagates the record to field data API, where a queue message is emitted
POST capture data to field data API
Since step 1 already propagated to this API, a 200 should be returned based on uuid
Since 200 is returned, queue message should not re-emitted
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
extra_attributes
rejection_reason
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
process sends tree record to /v1/tree in v2
v2 calculates SESSION UUID = device_identifier + planter_identifier using uuid-string
v2 checks for a session that matches this
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.
Old versions:
greenstand/bulk-pack-processor:1.2.7
greenstand/bulk-pack-transformer:1.6.2
Bulk Pack Format v2 :
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
Cloud Services (such as RabbitMQ or S3)
Domain logic
Accesses repositories
Allowances
We allow HTTP 404 errors to be thrown from within the model as a convenience for now
Accesses the database, performs CRUD operations
One repository for each database table in RDMS
We decided to assign search_path to each microservice user (service and migration users) in terraform to resolve this need.
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
kubernetes.container.name: "treetracker-api"
u: admin p:xRrE6vZDUcRph%1P%0H
grafana (Performance monitoring)
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?
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"] } ]
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
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
What tests are required?
Endpoint behaviors
How much validation?
Payload structure vs 404 responses
JOI tests could be unit tests
Diagram for contract tables:
https://lucid.app/lucidchart/4beb07f7-0ef0-4bf0-b88d-5548c2cda03b/edit?page=0_0#