arrow-left

Only this pageAll pages
gitbookPowered 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...

CORS

hashtag
Disable CORS for working on a microservice API with localhost

  • 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: or - 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:

  • Try different browsers or the proxy setting for CRA.

  • Create your own proxy server:

hashtag
Helpful reading!

Debugging: Explain settings: CORS module settings:

https://github.com/Greenstand/treetracker-stakeholder-api/blob/main/server/app.jsarrow-up-right
https://github.com/Greenstand/treetracker-web-map-api/blob/master/src/app.jsarrow-up-right
Cross Domain CORSarrow-up-right
Moesif CORSarrow-up-right
Requestlyarrow-up-right
https://medium.com/swlh/avoiding-cors-errors-on-localhost-in-2020-5a656ed8cefaarrow-up-right
https://medium.com/nodejsmadeeasy/a-simple-cors-proxy-for-javascript-applications-9b36a8d39c51arrow-up-right
https://httptoolkit.tech/blog/how-to-debug-cors-errors/arrow-up-right
https://web.dev/cross-origin-resource-sharing/arrow-up-right
https://expressjs.com/en/resources/middleware/cors.htmlarrow-up-right

Microservices Directory

hashtag
Domain Services

hashtag
Treetracker API

Source Code:

Mounted Endpoint:

Internal Endpoint:

hashtag
Field Data API

Source Code:

Mounted Endpoint:

Internal Endpoint:

hashtag
Stakeholder API

Source Code:

Mounted Endpoint:

Internal Endpoint:

hashtag
Earnings API

Source Code:

Mounted Endpoint:

Internal Endpoint:

hashtag
Regions API

Source Code:

Mounted Endpoint:

Internal Endpoint:

hashtag
Messaging API

Source Code:

Mounted Endpoint:

Internal Endpoint:

hashtag
Map Tile Service

Source Code:

CDN Endpoint: , also tiles2, tiles3, tiles4

Mounted Endpoint:

hashtag
Grower Account Query Service

Source Code:

Mounted Endpoint:

Internal Endpoint:

hashtag
Web Map Query Service Consumer

Source Code:

This queue message consumer exposes no API

hashtag
Bulk Pack Services

hashtag
Bulk Pack Consumer

Source Code:

This queue message consumer exposes no API

hashtag
Bulk Pack Processor

Source Code:

This scheduled job exposes no API

hashtag
Bulk Pack Transformer V1

Source Code:

Mounted Endpoint: not mounted

Internal Endpoint:

hashtag
Bulk Pack Transformer V2

Source Code:

Mounted Endpoint: not mounted

Internal Endpoint:

hashtag
Legacy Services

hashtag
Admin API

Source Code:

Mounted Endpoint:

Internal Endpoint:

hashtag
Web Map API

Source Code:

Mounted Endpoint:

Internal Endpoint:

hashtag
Cloud Services

hashtag
Airflow API

hashtag
CKAN API

Treetracker Domain Migration Sequencing

hashtag
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.

hashtag
Notes:

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

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

Team Projects

hashtag
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.

https://github.com/Greenstand/treetracker-apiarrow-up-right
https://{env}-k8s.treetracker.org/treetracker/arrow-up-right
http://treetracker-api.treetracker-apiarrow-up-right
https://github.com/Greenstand/treetracker-field-dataarrow-up-right
https://{env}-k8s.treetracker.org/field-data/arrow-up-right
http://treetracker-field-data.field-data-api/arrow-up-right
https://github.com/Greenstand/treetracker-stakeholder-apiarrow-up-right
https://{env}-k8s.treetracker.org/stakeholder/arrow-up-right
http://treetracker-stakeholder-api.stakeholder-api/arrow-up-right
https://github.com/Greenstand/treetracker-earnings-apiarrow-up-right
https://{env}-k8s.treetracker.org/earnings/arrow-up-right
http://treetracker-earnings-api.earnings-api/arrow-up-right
https://github.com/Greenstand/treetracker-regions-apiarrow-up-right
https://{env}-k8s.treetracker.org/regions/arrow-up-right
http://treetracker-regions-api.regions-api/arrow-up-right
https://github.com/Greenstand/treetracker-messaging-apiarrow-up-right
https://{env}-k8s.treetracker.org/messaging/arrow-up-right
http://treetracker-messaging-api.messaging-api/arrow-up-right
https://github.com/Greenstand/node-mapnik-1arrow-up-right
https://tiles1.treetracker.org/arrow-up-right
https://{env}-k8s.treetracker.org/tiles/arrow-up-right
https://github.com/Greenstand/treetracker-grower-account-queryarrow-up-right
https://{env}-k8s.treetracker.org/query/arrow-up-right
http://treetracker-grower-account-query.query/arrow-up-right
https://github.com/Greenstand/webmap-query-service-consumerarrow-up-right
https://github.com/Greenstand/bulk-pack-consumerarrow-up-right
https://github.com/Greenstand/bulk-pack-processorarrow-up-right
https://github.com/Greenstand/bulk-pack-transformerarrow-up-right
http://bulk-pack-transformer.bulk-pack-services/arrow-up-right
https://github.com/Greenstand/bulk-pack-transformer-v2arrow-up-right
http://bulk-pack-transformer-v2.bulk-pack-services/arrow-up-right
https://github.com/Greenstand/treetracker-admin-apiarrow-up-right
https://{env}-k8s.treetracker.org/api/admin/arrow-up-right
http://treetracker-admin-api.admin-api/arrow-up-right
https://github.com/Greenstand/treetracker-web-map-apiarrow-up-right
https://{env}-k8s.treetracker.org/webmap/arrow-up-right
http://treetracker-webmap-api-ambassador-svc.webmap/arrow-up-right

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
    https://github.com/Greenstand/system-design-docs/blob/master/domain-migration/project_plan.mdarrow-up-right
    hashtag
    Developer Productivity
    • Codeowners file and agreements

    • Document observability tools and access

      • ELK: `arrow-up-rightkubernetes.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 )

        • 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

    hashtag
    Improve Documentation

    • https://www.conventionalcommits.org/en/v1.0.0/arrow-up-right

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

    hashtag
    Improve Microservices Template

    • 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

    • Resolve all vulnerabilities

      • Check for vulnerabilities when a new PR comes in.

    • Resolve dependencies errors for test files

      *

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

    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

    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

    Quaid adding his settings to microservice template
    https://dev-k8s.treetracker.org/messaging/arrow-up-right
    https://eslint.org/docs/user-guide/configuring/rulesarrow-up-right
      import/no-extraneous-dependencies: [error, { devDependencies: true }]

    Development Paths

    Capture Matching Release Project

    Capture Match road map 2021: https://matthew-fullstackgis.gitbook.io/capture-matching/arrow-up-right

    Webmap query service

    https://github.com/orgs/Greenstand/projects/36arrow-up-right

    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.

    Contract Service

    Diagram for contract tables:

    https://lucid.app/lucidchart/4beb07f7-0ef0-4bf0-b88d-5548c2cda03b/edit?page=0_0#arrow-up-right

    Implementation Pathway

    1. Release treetracker API into all envs

    2. Web map query schema & consumer into all env

      1. Release

    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

    Capture Verification

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

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

    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

  • 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

  • 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

    Testing Methodology

    hashtag
    Unit Tests

    hashtag
    Integration Tests

    • 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 (?)

    • 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

    • TODO

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

    Software Layers

    hashtag
    Handler

    • HTTP Domain

    • All error and success codes

    • API payload validation

    • Calls a service or a model function

    hashtag
    Service

    • Orchestration between services and the domain model

      • Database session

      • External APIs

    hashtag
    Model

    • Domain logic

    • Accesses repositories

    • Allowances

    hashtag
    Repository

    • Accesses the database, performs CRUD operations

    • One repository for each database table in RDMS

    Cloud Services (such as RabbitMQ or S3)

    We allow HTTP 404 errors to be thrown from within the model as a convenience for now

    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

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

    What tests are required?

    • Endpoint behaviors

    • How much validation?

      • Payload structure vs 404 responses

      • JOI tests could be unit tests

    https://github.com/visionmedia/supertest/issues/12arrow-up-right

    Domain Migration M2

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

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

    hashtag
    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.

    hashtag
    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.

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

    hashtag
    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.

    • 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

    hashtag
    Change Admin panel

    hashtag
    Change Admin panel to use field data API instead of directly reading 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.

    hashtag
    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.

    • (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)

    • 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.

    hashtag
    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.

    • 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

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

    • 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.

    hashtag
    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.

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

    hashtag
    Address domain naming issues (variable and class names)

    • 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'

    Bulk Pack

    Bulk Pack Processing v2

    Field data schem

    hashtag
    Ingestion Process by Entity Type

    hashtag
    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.

    hashtag
    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

    hashtag
    device_configuration

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

    hashtag
    Suggested Workflow

    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

    hashtag
    session

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

    hashtag
    Suggested Workflow

    1. POST to session path on field data API

      1. field data API returns 200 if already present

    2. Return 200

    hashtag
    capture

    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

    hashtag
    Orchestration Diagram

    Bulk Pack Format v2 :

    hashtag
    Improved orchestration architecture

    hashtag
    Deprecated proposal for orchestration

    hashtag
    Nullable Columns for Field Data Tables

    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

    hashtag
    Workflow Notes

    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

    hashtag
    Release

    Old versions:

    • greenstand/bulk-pack-processor:1.2.7

    • greenstand/bulk-pack-transformer:1.6.2

    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.

  • Else, insert

    1. 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

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

  • Return 200

  • 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.

  • Treetracker Schema Attributes

    **Capture**

    id
    Not null, default uuid(), immutable

    reference_id

    Nullable, immutable

    session_id

    Not null, immutable

    tree_id

    Nullable, immutable but clearable

    **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

    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

    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

    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

    lon

    Not null, immutable

    gps_accuracy

    Not null, immutable

    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_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_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

    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

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