{"id":22511342,"url":"https://github.com/dictybase/modware-stock","last_synced_at":"2026-05-01T01:31:27.569Z","repository":{"id":33927536,"uuid":"162178255","full_name":"dictyBase/modware-stock","owner":"dictyBase","description":"dictyBase grpc service to manage biological stocks ","archived":false,"fork":false,"pushed_at":"2024-11-13T21:50:53.000Z","size":1041,"stargazers_count":0,"open_issues_count":11,"forks_count":0,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2025-03-04T20:40:36.342Z","etag":null,"topics":["golang","grpc"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dictyBase.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-17T19:14:45.000Z","updated_at":"2024-11-13T21:50:58.000Z","dependencies_parsed_at":"2024-02-03T18:24:49.317Z","dependency_job_id":"859a1b87-2047-4e83-b94e-ec1eeead13f0","html_url":"https://github.com/dictyBase/modware-stock","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dictyBase%2Fmodware-stock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dictyBase%2Fmodware-stock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dictyBase%2Fmodware-stock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dictyBase%2Fmodware-stock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dictyBase","download_url":"https://codeload.github.com/dictyBase/modware-stock/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245949559,"owners_count":20698917,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","grpc"],"created_at":"2024-12-07T02:11:12.608Z","updated_at":"2026-05-01T01:31:27.555Z","avatar_url":"https://github.com/dictyBase.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# modware-stock\n[![License](https://img.shields.io/badge/License-BSD%202--Clause-blue.svg)](LICENSE)  \n![GitHub action](https://github.com/dictyBase/modware-stock/workflows/Continuous%20integration/badge.svg)\n[![codecov](https://codecov.io/gh/dictyBase/modware-stock/branch/develop/graph/badge.svg)](https://codecov.io/gh/dictyBase/modware-stock)\n[![Maintainability](https://api.codeclimate.com/v1/badges/e3681c2f3f207955c305/maintainability)](https://codeclimate.com/github/dictyBase/modware-stock/maintainability)   \n![Last commit](https://badgen.net/github/last-commit/dictyBase/modware-stock/develop)   \n[![Funding](https://badgen.net/badge/NIGMS/Rex%20L%20Chisholm,dictyBase,DCR/yellow?list=|)](https://reporter.nih.gov/project-details/10024726)\n\ngRPC microservice for managing biological stocks (strains and plasmids) in dictyBase. Backed by ArangoDB.\n\nOn every create, update, or delete mutation the service publishes a protobuf-serialized event to NATS so that downstream services can react without polling.\n\n- [Running the Server](#running-the-server)\n  - [Core Flags](#core-flags)\n  - [ArangoDB Collection Flags](#arangodb-collection-flags)\n  - [Ontology Flags](#ontology-flags)\n- [Stock IDs](#stock-ids)\n- [NATS Events](#nats-events)\n- [gRPC API](#grpc-api)\n  - [Service Methods](#service-methods)\n  - [Connect with Go](#connect-with-go)\n  - [Create a Strain](#create-a-strain)\n  - [Get a Strain](#get-a-strain)\n  - [Update a Strain](#update-a-strain)\n  - [Update a Plasmid](#update-a-plasmid)\n  - [List Strains with Pagination and Filters](#list-strains-with-pagination-and-filters)\n  - [Filtering](#filtering)\n  - [Batch Lookup by IDs](#batch-lookup-by-ids)\n  - [Create a Plasmid](#create-a-plasmid)\n  - [Delete a Stock](#delete-a-stock)\n  - [Load with Existing ID](#load-with-existing-id)\n- [ArangoDB Schema](#arangodb-schema)\n\n## Running the Server\n\n```bash\nmodware-stock start-server \\\n  --port 9560 \\\n  --arangodb-database dictybase \\\n  --arangodb-user root \\\n  --arangodb-pass secret \\\n  --arangodb-host arangodb \\\n  --arangodb-port 8529 \\\n  --nats-host nats \\\n  --nats-port 4222\n```\n\n### Core Flags\n\n| Flag | Default | Env Variable | Description |\n|------|---------|--------------|-------------|\n| `--port` | `9560` | — | gRPC server listen port |\n| `--arangodb-database, --db` | `stock` | `ARANGODB_DATABASE` | ArangoDB database name |\n| `--arangodb-user` | — | `ARANGODB_USER` | ArangoDB user |\n| `--arangodb-pass` | — | `ARANGODB_PASS` | ArangoDB password |\n| `--arangodb-host` | `arangodb` | `ARANGODB_SERVICE_HOST` | ArangoDB host |\n| `--arangodb-port` | `8529` | `ARANGODB_SERVICE_PORT` | ArangoDB port |\n| `--is-secure` | `false` | — | Use TLS for ArangoDB |\n| `--nats-host` | — | `NATS_SERVICE_HOST` | NATS host |\n| `--nats-port` | — | `NATS_SERVICE_PORT` | NATS port |\n| `--reflection, --ref` | `true` | — | Enable gRPC server reflection |\n| `--keyoffset` | `370000` | — | Starting offset for auto-generated stock IDs |\n\nGlobal flags: `--log-format` (`json`/`text`, default `json`), `--log-level` (`debug`/`warn`/`error`/`fatal`/`panic`, default `error`).\n\n### ArangoDB Collection Flags\n\nThese flags control the names of collections and graphs created in ArangoDB. Defaults match the dictyBase production configuration.\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `--stock-collection` | `stock` | Document collection for all stocks |\n| `--stockprop-collection` | `stockprop` | Document collection for stock properties (type-specific fields) |\n| `--stock-key-generator-collection` | `stock_key_generator` | Autoincrement collection used to generate stock IDs |\n| `--stock-type-edge` | `stock_type` | Edge collection linking a stock to its `stockprop` document |\n| `--parent-strain-edge` | `parent_strain` | Edge collection linking a strain to its parent strain |\n| `--stock-term-edge` | `stock_term` | Edge collection linking a stock to an ontology term |\n| `--stockproptype-graph` | `stockprop_type` | Named graph over `stock_type` edges |\n| `--strain2parent-graph` | `strain2parent` | Named graph over `parent_strain` edges |\n| `--stockonto-graph` | `stockonto` | Named graph over `stock_term` edges |\n\n### Ontology Flags\n\nThese flags configure which ontologies are used to classify and tag stocks. The referenced ontologies must be loaded into ArangoDB before the service starts (see `OboJSONFileUpload`).\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `--strain-ontology` | `dicty_strain_property` | Ontology namespace used to group strains |\n| `--strain-term` | `general strain` | Default ontology term applied when creating a strain without an explicit `dicty_strain_property` |\n| `--plasmid-ontology` | `plasmid_keywords` | Ontology namespace used to group plasmids |\n| `--plasmid-term` | `vector` | Default ontology term applied when creating a plasmid without an explicit `dicty_plasmid_property` |\n\n## Stock IDs\n\nAuto-generated IDs follow the pattern `DB(S|P)[0-9]{5,}`:\n\n- **Strains**: `DBS0370001`, `DBS0370002`, … (prefix `DBS`)\n- **Plasmids**: `DBP0370001`, `DBP0370002`, … (prefix `DBP`)\n\nThe numeric suffix is generated by ArangoDB's autoincrement key generator, seeded by `--keyoffset` (default `370000`). IDs assigned by `LoadStrain`/`LoadPlasmid` must match the same regex; the service validates them before persisting.\n\n## NATS Events\n\nThe service publishes events after every mutating operation. Payloads are protobuf-serialized `Strain` or `Plasmid` messages.\n\n| Operation | NATS Subject | Payload |\n|-----------|-------------|---------|\n| Create strain / plasmid | `StockService.Create` | `Strain` or `Plasmid` |\n| Update strain / plasmid | `StockService.Update` | `Strain` or `Plasmid` |\n| Delete any stock | `StockService.Delete` | `Strain` or `Plasmid` |\n\nDeserialize using `proto.Unmarshal` with the generated types from `github.com/dictyBase/go-genproto/dictybaseapis/stock`.\n\n## gRPC API\n\nFull protobuf definitions: [dictybaseapis/stock.proto](https://github.com/dictyBase/dictybaseapis/blob/master/dictybase/stock/stock.proto).\n\n### Service Methods\n\n| Method | Request | Response | Description |\n|--------|---------|----------|-------------|\n| `GetStrain` | `StockId` | `Strain` | Retrieve a strain by ID |\n| `CreateStrain` | `NewStrain` | `Strain` | Create a new strain |\n| `LoadStrain` | `ExistingStrain` | `Strain` | Load a strain with a pre-assigned ID and timestamps |\n| `UpdateStrain` | `StrainUpdate` | `Strain` | Update an existing strain |\n| `ListStrains` | `StockParameters` | `StrainCollection` | Paginated strain listing with filters |\n| `ListStrainsByIds` | `StockIdList` | `StrainList` | Batch lookup by IDs, no pagination metadata |\n| `GetPlasmid` | `StockId` | `Plasmid` | Retrieve a plasmid by ID |\n| `CreatePlasmid` | `NewPlasmid` | `Plasmid` | Create a new plasmid |\n| `LoadPlasmid` | `ExistingPlasmid` | `Plasmid` | Load a plasmid with a pre-assigned ID and timestamps |\n| `UpdatePlasmid` | `PlasmidUpdate` | `Plasmid` | Update an existing plasmid |\n| `ListPlasmids` | `StockParameters` | `PlasmidCollection` | Paginated plasmid listing with filters |\n| `RemoveStock` | `StockId` | `Empty` | Delete a stock (strain or plasmid) by ID |\n| `OboJSONFileUpload` | `stream FileUploadRequest` | `FileUploadResponse` | Stream-upload an OBO JSON ontology file to populate the ontology collections |\n\n### Connect with Go\n\n```go\nimport (\n    \"context\"\n    \"log\"\n\n    \"github.com/dictyBase/go-genproto/dictybaseapis/stock\"\n    \"google.golang.org/grpc\"\n    \"google.golang.org/grpc/credentials/insecure\"\n)\n\nfunc main() {\n    conn, err := grpc.NewClient(\n        \"localhost:9560\",\n        grpc.WithTransportCredentials(insecure.NewCredentials()),\n    )\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer conn.Close()\n\n    client := stock.NewStockServiceClient(conn)\n    ctx := context.Background()\n    _ = client // use client methods below\n}\n```\n\n### Create a Strain\n\n`created_by`, `updated_by`, `depositor`, `label`, and `species` are required. All other fields are optional.\n\n```go\nresp, err := client.CreateStrain(ctx, \u0026stock.NewStrain{\n    Data: \u0026stock.NewStrain_Data{\n        Type: \"strain\",\n        Attributes: \u0026stock.NewStrainAttributes{\n            CreatedBy: \"user@dictybase.org\",\n            UpdatedBy: \"user@dictybase.org\",\n            Depositor: \"Jane Doe\",\n            Label:     \"DBS0123456\",\n            Species:   \"Dictyostelium discoideum\",\n            Genes:     []string{\"gene1\"},\n            Names:     []string{\"myStrain\"},\n        },\n    },\n})\n// resp.Data.Id — auto-generated ID (e.g. \"DBS0370001\")\n// resp.Data.Attributes.DictyStrainProperty — defaults to the server's --strain-term value if omitted\n```\n\n### Get a Strain\n\n```go\nresp, err := client.GetStrain(ctx, \u0026stock.StockId{Id: \"DBS0350123\"})\n```\n\n### Update a Strain\n\nOnly `updated_by` is required. All other attributes are optional; unset fields are left unchanged.\n\n```go\nresp, err := client.UpdateStrain(ctx, \u0026stock.StrainUpdate{\n    Data: \u0026stock.StrainUpdate_Data{\n        Type: \"strain\",\n        Id:   \"DBS0350123\",\n        Attributes: \u0026stock.StrainUpdateAttributes{\n            UpdatedBy:           \"user@dictybase.org\",\n            Summary:             \"Updated description\",\n            DictyStrainProperty: \"REMI-seq\",\n        },\n    },\n})\n```\n\n### Update a Plasmid\n\nOnly `updated_by` is required. All other attributes are optional.\n\n```go\nresp, err := client.UpdatePlasmid(ctx, \u0026stock.PlasmidUpdate{\n    Data: \u0026stock.PlasmidUpdate_Data{\n        Type: \"plasmid\",\n        Id:   \"DBP0350456\",\n        Attributes: \u0026stock.PlasmidUpdateAttributes{\n            UpdatedBy: \"user@dictybase.org\",\n            Summary:   \"Updated plasmid description\",\n            Sequence:  \"ATCG...\",\n        },\n    },\n})\n```\n\n### List Strains with Pagination and Filters\n\n`ListStrains` and `ListPlasmids` use cursor-based pagination. The default page size is 10.\n\n```go\nresp, err := client.ListStrains(ctx, \u0026stock.StockParameters{\n    Limit:  10,\n    Cursor: 0,\n    Filter: \"depositor===Jane Doe\",\n})\n// resp.Data           — slice of strains for this page\n// resp.Meta.NextCursor — pass as Cursor to retrieve the next page (0 means last page)\n// resp.Meta.Total      — total number of records matching the filter\n// resp.Meta.Limit      — the effective page size\n\n// Fetch the next page\nif resp.Meta.NextCursor != 0 {\n    next, err := client.ListStrains(ctx, \u0026stock.StockParameters{\n        Limit:  10,\n        Cursor: resp.Meta.NextCursor,\n        Filter: \"depositor===Jane Doe\",\n    })\n}\n```\n\n### Filtering\n\n`ListStrains` and `ListPlasmids` accept a `filter` string in `StockParameters`. Filters follow the syntax `field operator value` and can be combined with boolean connectors.\n\n#### Boolean Connectors\n\n| Symbol | Meaning | Precedence |\n|--------|---------|------------|\n| `;` | AND | Higher |\n| `,` | OR | Lower |\n\nAND takes precedence over OR. Use `;` and `,` to build compound expressions without parentheses.\n\n#### String Operators\n\n| Operator | Meaning |\n|----------|---------|\n| `===` | Equals |\n| `!==` | Not equals |\n| `=~` | Contains substring |\n| `!~` | Does not contain substring |\n\n#### Date Operators\n\nDate values accept `YYYY-MM-DD`, `YYYY-MM`, or `YYYY` formats.\n\n| Operator | Meaning |\n|----------|---------|\n| `$==` | Equals |\n| `$\u003e` | After |\n| `$\u003c` | Before |\n| `$\u003e=` | On or after |\n| `$\u003c=` | On or before |\n\n#### Array Operators\n\nUsed for fields that hold repeated values (e.g. `gene`, `name`).\n\n| Operator | Meaning |\n|----------|---------|\n| `@==` | Element equals |\n| `@!=` | Element not equals |\n| `@=~` | Element contains substring |\n| `@!~` | Element does not contain substring |\n\n#### Filterable Fields\n\n| Filter Key | Type | Applies To | Notes |\n|------------|------|------------|-------|\n| `id` | string | Strains, Plasmids | |\n| `created_at` | date | Strains, Plasmids | Use date operators |\n| `updated_at` | date | Strains, Plasmids | Use date operators |\n| `depositor` | string | Strains, Plasmids | |\n| `summary` | string | Strains, Plasmids | |\n| `gene` | array | Strains, Plasmids | Use array operators |\n| `species` | string | Strains | |\n| `label` | string | Strains | The strain descriptor field |\n| `name` | array | Strains | Searches the `names` repeated field; use array operators |\n| `parent` | string | Strains | |\n| `plasmid` | string | Strains | Related plasmid name |\n| `plasmid_name` | string | Plasmids | |\n| `ontology` | string | Strains, Plasmids | Ontology namespace |\n| `tag` | string | Strains, Plasmids | Ontology term label |\n\n#### Examples\n\n```go\n// Strains deposited by a specific user, of a particular species\nresp, err := client.ListStrains(ctx, \u0026stock.StockParameters{\n    Limit:  10,\n    Filter: \"depositor===Jane Doe;species===Dictyostelium discoideum\",\n})\n\n// Strains created after a date\nresp, err := client.ListStrains(ctx, \u0026stock.StockParameters{\n    Limit:  10,\n    Filter: \"created_at$\u003e=2022-01-01\",\n})\n\n// Strains associated with a gene (array field)\nresp, err := client.ListStrains(ctx, \u0026stock.StockParameters{\n    Limit:  10,\n    Filter: \"gene@==act15\",\n})\n\n// Plasmids whose name contains \"pDM\"\nresp, err := client.ListPlasmids(ctx, \u0026stock.StockParameters{\n    Limit:  10,\n    Filter: \"plasmid_name=~pDM\",\n})\n\n// Strains tagged with a specific ontology term\nresp, err := client.ListStrains(ctx, \u0026stock.StockParameters{\n    Limit:  10,\n    Filter: \"tag===REMI-seq\",\n})\n```\n\n### Batch Lookup by IDs\n\n`ListStrainsByIds` returns strains for a list of IDs in a single call, without pagination metadata. IDs must match `DB(S|P)[0-9]{5,}`.\n\n```go\nresp, err := client.ListStrainsByIds(ctx, \u0026stock.StockIdList{\n    Id: []string{\"DBS0350123\", \"DBS0350456\"},\n})\n```\n\n### Create a Plasmid\n\n`created_by`, `updated_by`, `depositor`, and `name` are required.\n\n```go\nresp, err := client.CreatePlasmid(ctx, \u0026stock.NewPlasmid{\n    Data: \u0026stock.NewPlasmid_Data{\n        Type: \"plasmid\",\n        Attributes: \u0026stock.NewPlasmidAttributes{\n            CreatedBy: \"user@dictybase.org\",\n            UpdatedBy: \"user@dictybase.org\",\n            Depositor: \"Jane Doe\",\n            Name:      \"pDM304\",\n            Genes:     []string{\"act15\"},\n            Sequence:  \"ATCG...\",\n        },\n    },\n})\n```\n\n### Delete a Stock\n\n`RemoveStock` accepts both strain and plasmid IDs. The operation is idempotent.\n\n```go\n_, err := client.RemoveStock(ctx, \u0026stock.StockId{Id: \"DBS0350123\"})\n```\n\n### Load with Existing ID\n\n`LoadStrain` and `LoadPlasmid` are intended for data migration. Unlike `Create*`, they accept a pre-assigned ID and explicit `created_at`/`updated_at` timestamps, preserving historical records without re-sequencing IDs.\n\n```go\nresp, err := client.LoadStrain(ctx, \u0026stock.ExistingStrain{\n    Data: \u0026stock.ExistingStrain_Data{\n        Type: \"strain\",\n        Id:   \"DBS0350000\",\n        Attributes: \u0026stock.ExistingStrainAttributes{\n            CreatedBy: \"import@dictybase.org\",\n            UpdatedBy: \"import@dictybase.org\",\n            CreatedAt: timestamppb.New(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),\n            UpdatedAt: timestamppb.New(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),\n            Label:     \"DBS0350000\",\n            Species:   \"Dictyostelium discoideum\",\n            Depositor: \"Legacy Import\",\n        },\n    },\n})\n```\n\n## ArangoDB Schema\n\n```mermaid\nerDiagram\n    stock_key_generator ||--o| stock : \"auto-generates ID\"\n\n    stock ||--o| stockprop : \"stock_type edge\"\n    stock ||--o| stock : \"parent_strain edge\"\n    stock ||--o{ cvterm : \"stock_term edge\"\n\n    cv ||--o{ cvterm : \"contains\"\n\n    stock {\n        string _key PK\n        string stock_id UK\n        string created_by\n        string updated_by\n        string depositor\n        string summary\n        string editable_summary\n        list genes\n        list dbxrefs\n        list publications\n        timestamp created_at\n        timestamp updated_at\n    }\n\n    stockprop {\n        string _key PK\n        string label\n        string species\n        string plasmid\n        list names\n        string dicty_strain_property\n        string name\n        string image_map\n        string sequence\n    }\n\n    stock_key_generator {\n        string _key PK\n        int offset\n    }\n\n    cvterm {\n        string _key PK\n        string label\n        string name\n    }\n\n    cv {\n        string _key PK\n        string namespace\n    }\n```\n\nThe `stock` collection holds fields shared by both strains and plasmids. Type-specific fields (`label`, `species`, `names` for strains; `name`, `sequence`, `image_map` for plasmids) live in a linked `stockprop` document. Ontology classification is stored as graph edges rather than embedded fields, allowing stocks to be queried and grouped by ontology term without denormalizing the data.\n\nEdge collections and their graphs:\n\n| Edge Collection | Graph | From → To | Purpose |\n|-----------------|-------|-----------|---------|\n| `stock_type` | `stockprop_type` | `stock` → `stockprop` | Links a stock to its type-specific property document |\n| `parent_strain` | `strain2parent` | `stock` → `stock` (child → parent) | Records the parental lineage of a strain |\n| `stock_term` | `stockonto` | `stock` → `cvterm` | Associates a stock with one or more ontology terms |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdictybase%2Fmodware-stock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdictybase%2Fmodware-stock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdictybase%2Fmodware-stock/lists"}