commit 05ee82ff49dc135783945241e77e50da7b07f22c parent f554268fbeaad91a877bc54cc820ffb7181b6dcf Author: triesap <tyson@radroots.org> Date: Sun, 28 Dec 2025 14:29:39 +0000 events: extend farm data model with event codec + ingest/sync and upgraded db schema - Add a dedicated events module/crate with a canonical event codec (JSON), geo helpers, emit/ingest flows, and tests - Add wasm bindings for end-to-end event sync/ingest (e.g., sync_all, ingest_event) to support clients - Refactor/upgrade the DB layer by centralizing schema + SQL models, adding/expanding farm/plot relations, tags, GCS links, and event/state storage needed for events - Remove legacy schema plumbing in favor of the unified, upgraded DB + events stack Diffstat:
152 files changed, 11409 insertions(+), 4318 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md @@ -21,9 +21,9 @@ - sql-core: SQL executor trait + migrations for native/web/embedded targets. - sql-wasm-bridge: wasm JS bridge for exec/query and savepoint transactions. - sql-wasm-core: wasm-bindgen exports + error marshaling for SQL. -- tangle-schema: Tangle schema models and relation types (ts-rs bindings). -- tangle-sql: SQL access layer for Tangle schema, migrations, backup/restore. -- tangle-sql-wasm: wasm-bindgen exports for Tangle SQL operations. +- tangle-db-schema: Tangle schema models and relation types (ts-rs bindings). +- tangle-db: SQL access layer for Tangle schema, migrations, backup/restore. +- tangle-db-wasm: wasm-bindgen exports for Tangle SQL operations. - trade: trade/listing domain models and tags. ## Rust Code Directives diff --git a/Cargo.lock b/Cargo.lock @@ -1873,7 +1873,18 @@ dependencies = [ ] [[package]] -name = "radroots-tangle-schema" +name = "radroots-tangle-db" +version = "0.1.0" +dependencies = [ + "radroots-sql-core", + "radroots-tangle-db-schema", + "radroots-types", + "serde", + "serde_json", +] + +[[package]] +name = "radroots-tangle-db-schema" version = "0.1.0" dependencies = [ "radroots-types", @@ -1883,24 +1894,45 @@ dependencies = [ ] [[package]] -name = "radroots-tangle-sql" +name = "radroots-tangle-db-wasm" version = "0.1.0" dependencies = [ "radroots-sql-core", - "radroots-tangle-schema", + "radroots-sql-wasm-core", + "radroots-tangle-db", + "radroots-tangle-db-schema", + "serde", + "serde-wasm-bindgen", + "serde_json", + "wasm-bindgen", +] + +[[package]] +name = "radroots-tangle-events" +version = "0.1.0" +dependencies = [ + "base64 0.22.1", + "hex", + "radroots-events", + "radroots-events-codec", + "radroots-sql-core", + "radroots-tangle-db", + "radroots-tangle-db-schema", "radroots-types", "serde", "serde_json", + "sha2", + "uuid", ] [[package]] -name = "radroots-tangle-sql-wasm" +name = "radroots-tangle-events-wasm" version = "0.1.0" dependencies = [ + "radroots-events", "radroots-sql-core", "radroots-sql-wasm-core", - "radroots-tangle-schema", - "radroots-tangle-sql", + "radroots-tangle-events", "serde", "serde-wasm-bindgen", "serde_json", diff --git a/Cargo.toml b/Cargo.toml @@ -14,9 +14,11 @@ members = [ "sql-wasm-bridge", "sql-wasm-core", "sql-core", - "tangle-schema", - "tangle-sql", - "tangle-sql-wasm", + "tangle-db-schema", + "tangle-events", + "tangle-events-wasm", + "tangle-db", + "tangle-db-wasm", "trade", "types", ] @@ -42,13 +44,16 @@ radroots-net-core = { path = "net-core", version = "0.1.0", default-features = f radroots-sql-wasm-bridge = { path = "sql-wasm-bridge", version = "0.1.0" } radroots-sql-wasm-core = { path = "sql-wasm-core", version = "0.1.0" } radroots-sql-core = { path = "sql-core", version = "0.1.0" } -radroots-tangle-schema = { path = "tangle-schema", version = "0.1.0", default-features = false } -radroots-tangle-sql = { path = "tangle-sql", version = "0.1.0", default-features = false } -radroots-tangle-sql-wasm = { path = "tangle-sql-wasm", version = "0.1.0" } +radroots-tangle-db-schema = { path = "tangle-db-schema", version = "0.1.0", default-features = false } +radroots-tangle-events = { path = "tangle-events", version = "0.1.0", default-features = false } +radroots-tangle-db = { path = "tangle-db", version = "0.1.0", default-features = false } +radroots-tangle-db-wasm = { path = "tangle-db-wasm", version = "0.1.0" } +radroots-tangle-events-wasm = { path = "tangle-events-wasm", version = "0.1.0" } radroots-trade = { path = "trade", version = "0.1.0", default-features = false } radroots-types = { path = "types", version = "0.1.0", default-features = false } anyhow = { version = "1" } +base64 = { version = "0.22" } cfg-if = { version = "1" } chrono = { version = "0.4" } clap = { version = "4" } @@ -65,6 +70,7 @@ secrecy = { version = "0.10.3" } serde = { version = "1", default-features = false, features = ["derive"] } serde_json = { version = "1", default-features = false, features = ["alloc"] } serde-wasm-bindgen = { version = "0.6" } +sha2 = { version = "0.10", default-features = false } reqwest = { version = "0.12", default-features = false } rust_decimal = { version = "1", default-features = false } rust_decimal_macros = { version = "1" } @@ -80,7 +86,7 @@ tracing-subscriber = { version = "0.3" } ts-rs = { version = "11.1" } typeshare = { version = "1" } url = { version = "2" } -uuid = { version = "1.16.0", features = ["v4"] } +uuid = { version = "1.16.0", features = ["v4", "v7"] } uniffi = { version = "0.29.4" } wasm-bindgen = { version = "0.2" } wasm-bindgen-futures = { version = "0.4" } diff --git a/Makefile b/Makefile @@ -1,6 +1,6 @@ .PHONY: all bindings clean help \ - bindings-events bindings-tangle-schema bindings-trade bindings-types \ - build build-events-codec-wasm build-tangle-sql-wasm + bindings-events bindings-tangle-db-schema bindings-trade bindings-types \ + build build-events-codec-wasm build-tangle-db-wasm SHELL := /bin/bash .SHELLFLAGS := -e -o pipefail -c @@ -8,13 +8,13 @@ TS_RS_FEATURE ?= ts-rs BINDINGS_TARGETS := \ bindings-events \ - bindings-tangle-schema \ + bindings-tangle-db-schema \ bindings-trade \ bindings-types BUILD_TARGETS := \ build-events-codec-wasm \ - build-tangle-sql-wasm + build-tangle-db-wasm all: bindings build @@ -39,9 +39,9 @@ bindings-events: @(cd events && cargo test --features $(TS_RS_FEATURE)) @(cd events/bindings/ts && npm run build) -bindings-tangle-schema: - @(cd tangle-schema && cargo test --features $(TS_RS_FEATURE)) - @(cd tangle-schema/bindings/ts && npm run build) +bindings-tangle-db-schema: + @(cd tangle-db-schema && cargo test --features $(TS_RS_FEATURE)) + @(cd tangle-db-schema/bindings/ts && npm run build) bindings-trade: @(cd trade && cargo test --features $(TS_RS_FEATURE)) @@ -51,9 +51,9 @@ bindings-types: @(cd types && cargo test --features $(TS_RS_FEATURE)) @(cd types/bindings/ts && npm run build) -build-tangle-sql-wasm: - wasm-pack build tangle-sql-wasm --release --target web \ - --out-dir ../tangle-sql-wasm/pkg/dist --scope radroots +build-tangle-db-wasm: + wasm-pack build tangle-db-wasm --release --target web \ + --out-dir ../tangle-db-wasm/pkg/dist --scope radroots build-events-codec-wasm: wasm-pack build events-codec-wasm --release --target web \ diff --git a/events-codec/src/farm/encode.rs b/events-codec/src/farm/encode.rs @@ -37,9 +37,11 @@ pub fn farm_build_tags(farm: &RadrootsFarm) -> Result<Vec<Vec<String>>, EventEnc } } if let Some(location) = farm.location.as_ref() { - if let Some(geohash) = location.geohash.as_ref().filter(|v| !v.trim().is_empty()) { - push_tag(&mut tags, TAG_G, geohash); + let geohash = location.gcs.geohash.trim(); + if geohash.is_empty() { + return Err(EventEncodeError::EmptyRequiredField("location.gcs.geohash")); } + push_tag(&mut tags, TAG_G, geohash); } Ok(tags) } diff --git a/events-codec/src/farm/mod.rs b/events-codec/src/farm/mod.rs @@ -4,7 +4,14 @@ pub mod list_sets; #[cfg(test)] mod tests { - use radroots_events::farm::{RadrootsFarm, RadrootsFarmLocation, RadrootsFarmRef}; + use radroots_events::farm::{ + RadrootsFarm, + RadrootsFarmLocation, + RadrootsFarmRef, + RadrootsGcsLocation, + RadrootsGeoJsonPoint, + RadrootsGeoJsonPolygon, + }; use radroots_events::plot::RadrootsPlot; use crate::farm::encode::{farm_build_tags, farm_ref_tags}; use crate::farm::list_sets::{ @@ -23,13 +30,42 @@ mod tests { picture: None, banner: None, location: Some(RadrootsFarmLocation { - primary: "Somewhere".to_string(), + primary: None, city: None, region: None, country: None, - lat: None, - lng: None, - geohash: Some("9q8yy".to_string()), + gcs: RadrootsGcsLocation { + lat: 37.0, + lng: -122.0, + geohash: "9q8yy".to_string(), + point: RadrootsGeoJsonPoint { + r#type: "Point".to_string(), + coordinates: [-122.0, 37.0], + }, + polygon: RadrootsGeoJsonPolygon { + r#type: "Polygon".to_string(), + coordinates: vec![vec![ + [-122.0, 37.0], + [-122.0, 37.0001], + [-122.0001, 37.0001], + [-122.0, 37.0], + ]], + }, + accuracy: None, + altitude: None, + tag_0: None, + label: None, + area: None, + elevation: None, + soil: None, + climate: None, + gc_id: None, + gc_name: None, + gc_admin1_id: None, + gc_admin1_name: None, + gc_country_id: None, + gc_country_name: None, + }, }), tags: Some(vec!["orchard".to_string()]), }; @@ -78,7 +114,6 @@ mod tests { name: "Plot 1".to_string(), about: None, location: None, - geometry: None, tags: None, }]; diff --git a/events-codec/src/plot/encode.rs b/events-codec/src/plot/encode.rs @@ -76,9 +76,11 @@ pub fn plot_build_tags(plot: &RadrootsPlot) -> Result<Vec<Vec<String>>, EventEnc } } if let Some(location) = plot.location.as_ref() { - if let Some(geohash) = location.geohash.as_ref().filter(|v| !v.trim().is_empty()) { - push_tag(&mut tags, TAG_G, geohash); + let geohash = location.gcs.geohash.trim(); + if geohash.is_empty() { + return Err(EventEncodeError::EmptyRequiredField("location.gcs.geohash")); } + push_tag(&mut tags, TAG_G, geohash); } Ok(tags) } diff --git a/events-codec/src/plot/mod.rs b/events-codec/src/plot/mod.rs @@ -3,7 +3,10 @@ pub mod encode; #[cfg(test)] mod tests { - use radroots_events::{farm::RadrootsFarmRef, plot::{RadrootsPlot, RadrootsPlotLocation}}; + use radroots_events::{ + farm::{RadrootsFarmRef, RadrootsGcsLocation, RadrootsGeoJsonPoint, RadrootsGeoJsonPolygon}, + plot::{RadrootsPlot, RadrootsPlotLocation}, + }; use crate::plot::encode::plot_build_tags; #[test] @@ -17,15 +20,43 @@ mod tests { name: "Orchard".to_string(), about: None, location: Some(RadrootsPlotLocation { - primary: "Somewhere".to_string(), + primary: None, city: None, region: None, country: None, - lat: None, - lng: None, - geohash: None, + gcs: RadrootsGcsLocation { + lat: 37.0, + lng: -122.0, + geohash: "9q8yy".to_string(), + point: RadrootsGeoJsonPoint { + r#type: "Point".to_string(), + coordinates: [-122.0, 37.0], + }, + polygon: RadrootsGeoJsonPolygon { + r#type: "Polygon".to_string(), + coordinates: vec![vec![ + [-122.0, 37.0], + [-122.0, 37.0001], + [-122.0001, 37.0001], + [-122.0, 37.0], + ]], + }, + accuracy: None, + altitude: None, + tag_0: None, + label: None, + area: None, + elevation: None, + soil: None, + climate: None, + gc_id: None, + gc_name: None, + gc_admin1_id: None, + gc_admin1_name: None, + gc_country_id: None, + gc_country_name: None, + }, }), - geometry: None, tags: Some(vec!["orchard".to_string()]), }; diff --git a/events/bindings/ts/src/schemas.ts b/events/bindings/ts/src/schemas.ts @@ -154,14 +154,44 @@ export const radroots_list_set_schema = z.object({ image: z.string().optional() }); +export const radroots_geojson_point_schema = z.object({ + type: z.string(), + coordinates: z.tuple([z.number(), z.number()]) +}); + +export const radroots_geojson_polygon_schema = z.object({ + type: z.string(), + coordinates: z.array(z.array(z.tuple([z.number(), z.number()]))) +}); + +export const radroots_gcs_location_schema = z.object({ + lat: z.number(), + lng: z.number(), + geohash: z.string(), + point: radroots_geojson_point_schema, + polygon: radroots_geojson_polygon_schema, + accuracy: z.number().optional(), + altitude: z.number().optional(), + tag_0: z.string().optional(), + label: z.string().optional(), + area: z.number().optional(), + elevation: z.number().optional(), + soil: z.string().optional(), + climate: z.string().optional(), + gc_id: z.string().optional(), + gc_name: z.string().optional(), + gc_admin1_id: z.string().optional(), + gc_admin1_name: z.string().optional(), + gc_country_id: z.string().optional(), + gc_country_name: z.string().optional() +}); + export const radroots_farm_location_schema = z.object({ - primary: z.string(), + primary: z.string().optional(), city: z.string().optional(), region: z.string().optional(), country: z.string().optional(), - lat: z.number().optional(), - lng: z.number().optional(), - geohash: z.string().optional() + gcs: radroots_gcs_location_schema }); export const radroots_farm_schema = z.object({ @@ -181,13 +211,11 @@ export const radroots_farm_ref_schema = z.object({ }); export const radroots_plot_location_schema = z.object({ - primary: z.string(), + primary: z.string().optional(), city: z.string().optional(), region: z.string().optional(), country: z.string().optional(), - lat: z.number().optional(), - lng: z.number().optional(), - geohash: z.string().optional() + gcs: radroots_gcs_location_schema }); export const radroots_plot_schema = z.object({ @@ -196,7 +224,6 @@ export const radroots_plot_schema = z.object({ name: z.string(), about: z.string().optional(), location: radroots_plot_location_schema.optional(), - geometry: z.string().optional(), tags: z.array(z.string()).optional() }); diff --git a/events/bindings/ts/src/types.ts b/events/bindings/ts/src/types.ts @@ -26,7 +26,7 @@ export type RadrootsFarmEventIndex = { event: RadrootsNostrEvent, metadata: Radr export type RadrootsFarmEventMetadata = { id: string, author: string, published_at: number, kind: number, farm: RadrootsFarm, }; -export type RadrootsFarmLocation = { primary: string, city?: string | null, region?: string | null, country?: string | null, lat?: number | null, lng?: number | null, geohash?: string | null, }; +export type RadrootsFarmLocation = { primary?: string | null, city?: string | null, region?: string | null, country?: string | null, gcs: RadrootsGcsLocation, }; export type RadrootsFarmRef = { pubkey: string, d_tag: string, }; @@ -38,6 +38,12 @@ export type RadrootsFollowEventMetadata = { id: string, author: string, publishe export type RadrootsFollowProfile = { published_at: number, public_key: string, relay_url?: string | null, contact_name?: string | null, }; +export type RadrootsGcsLocation = { lat: number, lng: number, geohash: string, point: RadrootsGeoJsonPoint, polygon: RadrootsGeoJsonPolygon, accuracy?: number | null, altitude?: number | null, tag_0?: string | null, label?: string | null, area?: number | null, elevation?: number | null, soil?: string | null, climate?: string | null, gc_id?: string | null, gc_name?: string | null, gc_admin1_id?: string | null, gc_admin1_name?: string | null, gc_country_id?: string | null, gc_country_name?: string | null, }; + +export type RadrootsGeoJsonPoint = { type: string, coordinates: [number, number], }; + +export type RadrootsGeoJsonPolygon = { type: string, coordinates: Array<Array<[number, number]>>, }; + export type RadrootsGiftWrap = { recipient: RadrootsGiftWrapRecipient, content: string, expiration?: number | null, }; export type RadrootsGiftWrapEventIndex = { event: RadrootsNostrEvent, metadata: RadrootsGiftWrapEventMetadata, }; @@ -130,13 +136,13 @@ export type RadrootsNostrEventPtr = { id: string, relays?: string | null, }; export type RadrootsNostrEventRef = { id: string, author: string, kind: number, d_tag?: string | null, relays?: string[] | null, }; -export type RadrootsPlot = { d_tag: string, farm: RadrootsFarmRef, name: string, about?: string | null, location?: RadrootsPlotLocation | null, geometry?: string | null, tags?: string[] | null, }; +export type RadrootsPlot = { d_tag: string, farm: RadrootsFarmRef, name: string, about?: string | null, location?: RadrootsPlotLocation | null, tags?: string[] | null, }; export type RadrootsPlotEventIndex = { event: RadrootsNostrEvent, metadata: RadrootsPlotEventMetadata, }; export type RadrootsPlotEventMetadata = { id: string, author: string, published_at: number, kind: number, plot: RadrootsPlot, }; -export type RadrootsPlotLocation = { primary: string, city?: string | null, region?: string | null, country?: string | null, lat?: number | null, lng?: number | null, geohash?: string | null, }; +export type RadrootsPlotLocation = { primary?: string | null, city?: string | null, region?: string | null, country?: string | null, gcs: RadrootsGcsLocation, }; export type RadrootsPost = { content: string, }; diff --git a/events/src/farm.rs b/events/src/farm.rs @@ -60,18 +60,74 @@ pub struct RadrootsFarmRef { #[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug)] +pub struct RadrootsGeoJsonPoint { + #[cfg_attr(feature = "serde", serde(rename = "type"))] + pub r#type: String, + pub coordinates: [f64; 2], +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug)] +pub struct RadrootsGeoJsonPolygon { + #[cfg_attr(feature = "serde", serde(rename = "type"))] + pub r#type: String, + pub coordinates: Vec<Vec<[f64; 2]>>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug)] +pub struct RadrootsGcsLocation { + pub lat: f64, + pub lng: f64, + pub geohash: String, + pub point: RadrootsGeoJsonPoint, + pub polygon: RadrootsGeoJsonPolygon, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub accuracy: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub altitude: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub tag_0: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub label: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub area: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub elevation: Option<u32>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub soil: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub climate: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_admin1_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_admin1_name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_country_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_country_name: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug)] pub struct RadrootsFarmLocation { - pub primary: String, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub primary: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub city: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub region: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub country: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] - pub lat: Option<f64>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] - pub lng: Option<f64>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub geohash: Option<String>, + pub gcs: RadrootsGcsLocation, } diff --git a/events/src/plot.rs b/events/src/plot.rs @@ -1,4 +1,4 @@ -use crate::{RadrootsNostrEvent, farm::RadrootsFarmRef}; +use crate::{RadrootsNostrEvent, farm::{RadrootsFarmRef, RadrootsGcsLocation}}; #[cfg(feature = "ts-rs")] use ts_rs::TS; @@ -38,8 +38,6 @@ pub struct RadrootsPlot { pub about: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional, type = "RadrootsPlotLocation | null"))] pub location: Option<RadrootsPlotLocation>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub geometry: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional, type = "string[] | null"))] pub tags: Option<Vec<String>>, } @@ -49,17 +47,13 @@ pub struct RadrootsPlot { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug)] pub struct RadrootsPlotLocation { - pub primary: String, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub primary: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub city: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub region: Option<String>, #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] pub country: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] - pub lat: Option<f64>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] - pub lng: Option<f64>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub geohash: Option<String>, + pub gcs: RadrootsGcsLocation, } diff --git a/sql-core/src/executor_sqlite.rs b/sql-core/src/executor_sqlite.rs @@ -81,7 +81,7 @@ impl SqliteExecutor { impl SqlExecutor for SqliteExecutor { fn exec(&self, sql: &str, params_json: &str) -> Result<ExecOutcome, SqlError> { let binds = self.parse_params(params_json)?; - let mut conn = self.conn.lock().map_err(|_| SqlError::Internal)?; + let conn = self.conn.lock().map_err(|_| SqlError::Internal)?; let n = conn .execute(sql, params_from_iter(binds.into_iter())) .map_err(SqlError::from)?; diff --git a/tangle-db-schema/Cargo.toml b/tangle-db-schema/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "radroots-tangle-db-schema" +version.workspace = true +edition.workspace = true +authors = ["Radroots Authors"] +rust-version.workspace = true +license.workspace = true +build = "build.rs" + +[lib] +crate-type = ["rlib"] + +[features] +default = [] +ts-rs = ["dep:ts-rs"] + +[dependencies] +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +ts-rs = { workspace = true, optional = true } +radroots-types = { workspace = true } diff --git a/tangle-db-schema/bindings/ts/package.json b/tangle-db-schema/bindings/ts/package.json @@ -0,0 +1,41 @@ +{ + "name": "@radroots/tangle-db-schema-bindings", + "version": "1.0.0", + "private": true, + "license": "AGPLv3", + "type": "module", + "main": "./dist/cjs/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, + "files": [ + "dist" + ], + "sideEffects": false, + "scripts": { + "build:esm": "tsc -p tsconfig.esm.json", + "build:cjs": "tsc -p tsconfig.cjs.json", + "build": "npm run build:esm && npm run build:cjs", + "prebuild": "npm run clean && npm run prepend-imports", + "prepend-imports": "bash -c 'f=./src/types.ts; line=\"import type { IResult, IResultList, IResultPass } from \\\"@radroots/types-bindings\\\";\"; grep -qxF \"$line\" \"$f\" || (echo -e \"$line\\n\\n$(cat $f)\" > \"$f\")'", + "clean": "rimraf dist", + "dev": "npm run watch", + "watch": "tsc -w" + }, + "devDependencies": { + "@radroots/tsconfig": "workspace:*", + "rimraf": "^6.0.1" + }, + "dependencies": { + "@radroots/types-bindings": "workspace:*" + }, + "publishConfig": { + "access": "public" + } +} +\ No newline at end of file diff --git a/tangle-schema/bindings/ts/src/index.ts b/tangle-db-schema/bindings/ts/src/index.ts diff --git a/tangle-db-schema/bindings/ts/src/types.ts b/tangle-db-schema/bindings/ts/src/types.ts @@ -0,0 +1,567 @@ +import type { IResult, IResultList, IResultPass } from "@radroots/types-bindings"; + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Farm = { id: string, created_at: string, updated_at: string, d_tag: string, pubkey: string, name: string, about: string | null, website: string | null, picture: string | null, banner: string | null, location_primary: string | null, location_city: string | null, location_region: string | null, location_country: string | null, }; + +export type FarmFindManyRel = never; + +export type FarmGcsLocation = { id: string, created_at: string, updated_at: string, farm_id: string, gcs_location_id: string, role: string, }; + +export type FarmGcsLocationFindManyRel = never; + +export type FarmGcsLocationQueryBindValues = { id: string, } | { farm_id: string, } | { gcs_location_id: string, }; + +export type FarmMember = { id: string, created_at: string, updated_at: string, farm_id: string, member_pubkey: string, role: string, }; + +export type FarmMemberClaim = { id: string, created_at: string, updated_at: string, member_pubkey: string, farm_pubkey: string, }; + +export type FarmMemberClaimFindManyRel = never; + +export type FarmMemberClaimQueryBindValues = { id: string, } | { member_pubkey: string, } | { farm_pubkey: string, }; + +export type FarmMemberFindManyRel = never; + +export type FarmMemberQueryBindValues = { id: string, } | { farm_id: string, } | { member_pubkey: string, }; + +export type FarmQueryBindValues = { id: string, } | { d_tag: string, } | { pubkey: string, }; + +export type FarmTag = { id: string, created_at: string, updated_at: string, farm_id: string, tag: string, }; + +export type FarmTagFindManyRel = never; + +export type FarmTagQueryBindValues = { id: string, } | { farm_id: string, } | { tag: string, }; + +export type GcsLocation = { id: string, created_at: string, updated_at: string, d_tag: string, lat: number, lng: number, geohash: string, point: string, polygon: string, accuracy: number | null, altitude: number | null, tag_0: string | null, label: string | null, area: number | null, elevation: number | null, soil: string | null, climate: string | null, gc_id: string | null, gc_name: string | null, gc_admin1_id: string | null, gc_admin1_name: string | null, gc_country_id: string | null, gc_country_name: string | null, }; + +export type GcsLocationFarmArgs = { id: string, }; + +export type GcsLocationFindManyRel = { "on_trade_product": GcsLocationTradeProductArgs } | { "off_trade_product": GcsLocationTradeProductArgs } | { "on_farm": GcsLocationFarmArgs } | { "off_farm": GcsLocationFarmArgs } | { "on_plot": GcsLocationPlotArgs } | { "off_plot": GcsLocationPlotArgs }; + +export type GcsLocationPlotArgs = { id: string, }; + +export type GcsLocationQueryBindValues = { id: string, } | { d_tag: string, } | { geohash: string, }; + +export type GcsLocationTradeProductArgs = { id: string, }; + +export type IFarmCreate = IFarmFields; + +export type IFarmCreateResolve = IResult<Farm>; + +export type IFarmDelete = IFarmFindOne; + +export type IFarmDeleteResolve = IResult<string>; + +export type IFarmFields = { d_tag: string, pubkey: string, name: string, about?: string | null, website?: string | null, picture?: string | null, banner?: string | null, location_primary?: string | null, location_city?: string | null, location_region?: string | null, location_country?: string | null, }; + +export type IFarmFieldsFilter = { id?: string, created_at?: string, updated_at?: string, d_tag?: string, pubkey?: string, name?: string, about?: string, website?: string, picture?: string, banner?: string, location_primary?: string, location_city?: string, location_region?: string, location_country?: string, }; + +export type IFarmFieldsPartial = { d_tag?: string | null, pubkey?: string | null, name?: string | null, about?: string | null, website?: string | null, picture?: string | null, banner?: string | null, location_primary?: string | null, location_city?: string | null, location_region?: string | null, location_country?: string | null, }; + +export type IFarmFindMany = { filter: IFarmFieldsFilter | null, }; + +export type IFarmFindManyResolve = IResultList<Farm>; + +export type IFarmFindOne = IFarmFindOneArgs | IFarmFindOneRelArgs; + +export type IFarmFindOneArgs = { on: FarmQueryBindValues, }; + +export type IFarmFindOneRelArgs = { rel: FarmFindManyRel, }; + +export type IFarmFindOneResolve = IResult<Farm>; + +export type IFarmGcsLocationCreate = IFarmGcsLocationFields; + +export type IFarmGcsLocationCreateResolve = IResult<FarmGcsLocation>; + +export type IFarmGcsLocationDelete = IFarmGcsLocationFindOne; + +export type IFarmGcsLocationDeleteResolve = IResult<string>; + +export type IFarmGcsLocationFields = { farm_id: string, gcs_location_id: string, role: string, }; + +export type IFarmGcsLocationFieldsFilter = { id?: string, created_at?: string, updated_at?: string, farm_id?: string, gcs_location_id?: string, role?: string, }; + +export type IFarmGcsLocationFieldsPartial = { farm_id?: string | null, gcs_location_id?: string | null, role?: string | null, }; + +export type IFarmGcsLocationFindMany = { filter: IFarmGcsLocationFieldsFilter | null, }; + +export type IFarmGcsLocationFindManyResolve = IResultList<FarmGcsLocation>; + +export type IFarmGcsLocationFindOne = IFarmGcsLocationFindOneArgs | IFarmGcsLocationFindOneRelArgs; + +export type IFarmGcsLocationFindOneArgs = { on: FarmGcsLocationQueryBindValues, }; + +export type IFarmGcsLocationFindOneRelArgs = { rel: FarmGcsLocationFindManyRel, }; + +export type IFarmGcsLocationFindOneResolve = IResult<FarmGcsLocation>; + +export type IFarmGcsLocationUpdate = { on: FarmGcsLocationQueryBindValues, fields: IFarmGcsLocationFieldsPartial, }; + +export type IFarmGcsLocationUpdateResolve = IResult<FarmGcsLocation>; + +export type IFarmMemberClaimCreate = IFarmMemberClaimFields; + +export type IFarmMemberClaimCreateResolve = IResult<FarmMemberClaim>; + +export type IFarmMemberClaimDelete = IFarmMemberClaimFindOne; + +export type IFarmMemberClaimDeleteResolve = IResult<string>; + +export type IFarmMemberClaimFields = { member_pubkey: string, farm_pubkey: string, }; + +export type IFarmMemberClaimFieldsFilter = { id?: string, created_at?: string, updated_at?: string, member_pubkey?: string, farm_pubkey?: string, }; + +export type IFarmMemberClaimFieldsPartial = { member_pubkey?: string | null, farm_pubkey?: string | null, }; + +export type IFarmMemberClaimFindMany = { filter: IFarmMemberClaimFieldsFilter | null, }; + +export type IFarmMemberClaimFindManyResolve = IResultList<FarmMemberClaim>; + +export type IFarmMemberClaimFindOne = IFarmMemberClaimFindOneArgs | IFarmMemberClaimFindOneRelArgs; + +export type IFarmMemberClaimFindOneArgs = { on: FarmMemberClaimQueryBindValues, }; + +export type IFarmMemberClaimFindOneRelArgs = { rel: FarmMemberClaimFindManyRel, }; + +export type IFarmMemberClaimFindOneResolve = IResult<FarmMemberClaim>; + +export type IFarmMemberClaimUpdate = { on: FarmMemberClaimQueryBindValues, fields: IFarmMemberClaimFieldsPartial, }; + +export type IFarmMemberClaimUpdateResolve = IResult<FarmMemberClaim>; + +export type IFarmMemberCreate = IFarmMemberFields; + +export type IFarmMemberCreateResolve = IResult<FarmMember>; + +export type IFarmMemberDelete = IFarmMemberFindOne; + +export type IFarmMemberDeleteResolve = IResult<string>; + +export type IFarmMemberFields = { farm_id: string, member_pubkey: string, role: string, }; + +export type IFarmMemberFieldsFilter = { id?: string, created_at?: string, updated_at?: string, farm_id?: string, member_pubkey?: string, role?: string, }; + +export type IFarmMemberFieldsPartial = { farm_id?: string | null, member_pubkey?: string | null, role?: string | null, }; + +export type IFarmMemberFindMany = { filter: IFarmMemberFieldsFilter | null, }; + +export type IFarmMemberFindManyResolve = IResultList<FarmMember>; + +export type IFarmMemberFindOne = IFarmMemberFindOneArgs | IFarmMemberFindOneRelArgs; + +export type IFarmMemberFindOneArgs = { on: FarmMemberQueryBindValues, }; + +export type IFarmMemberFindOneRelArgs = { rel: FarmMemberFindManyRel, }; + +export type IFarmMemberFindOneResolve = IResult<FarmMember>; + +export type IFarmMemberUpdate = { on: FarmMemberQueryBindValues, fields: IFarmMemberFieldsPartial, }; + +export type IFarmMemberUpdateResolve = IResult<FarmMember>; + +export type IFarmTagCreate = IFarmTagFields; + +export type IFarmTagCreateResolve = IResult<FarmTag>; + +export type IFarmTagDelete = IFarmTagFindOne; + +export type IFarmTagDeleteResolve = IResult<string>; + +export type IFarmTagFields = { farm_id: string, tag: string, }; + +export type IFarmTagFieldsFilter = { id?: string, created_at?: string, updated_at?: string, farm_id?: string, tag?: string, }; + +export type IFarmTagFieldsPartial = { farm_id?: string | null, tag?: string | null, }; + +export type IFarmTagFindMany = { filter: IFarmTagFieldsFilter | null, }; + +export type IFarmTagFindManyResolve = IResultList<FarmTag>; + +export type IFarmTagFindOne = IFarmTagFindOneArgs | IFarmTagFindOneRelArgs; + +export type IFarmTagFindOneArgs = { on: FarmTagQueryBindValues, }; + +export type IFarmTagFindOneRelArgs = { rel: FarmTagFindManyRel, }; + +export type IFarmTagFindOneResolve = IResult<FarmTag>; + +export type IFarmTagUpdate = { on: FarmTagQueryBindValues, fields: IFarmTagFieldsPartial, }; + +export type IFarmTagUpdateResolve = IResult<FarmTag>; + +export type IFarmUpdate = { on: FarmQueryBindValues, fields: IFarmFieldsPartial, }; + +export type IFarmUpdateResolve = IResult<Farm>; + +export type IGcsLocationCreate = IGcsLocationFields; + +export type IGcsLocationCreateResolve = IResult<GcsLocation>; + +export type IGcsLocationDelete = IGcsLocationFindOne; + +export type IGcsLocationDeleteResolve = IResult<string>; + +export type IGcsLocationFields = { d_tag: string, lat: number, lng: number, geohash: string, point: string, polygon: string, accuracy?: number | null, altitude?: number | null, tag_0?: string | null, label?: string | null, area?: number | null, elevation?: number | null, soil?: string | null, climate?: string | null, gc_id?: string | null, gc_name?: string | null, gc_admin1_id?: string | null, gc_admin1_name?: string | null, gc_country_id?: string | null, gc_country_name?: string | null, }; + +export type IGcsLocationFieldsFilter = { id?: string, created_at?: string, updated_at?: string, d_tag?: string, lat?: number, lng?: number, geohash?: string, point?: string, polygon?: string, accuracy?: number, altitude?: number, tag_0?: string, label?: string, area?: number, elevation?: number, soil?: string, climate?: string, gc_id?: string, gc_name?: string, gc_admin1_id?: string, gc_admin1_name?: string, gc_country_id?: string, gc_country_name?: string, }; + +export type IGcsLocationFieldsPartial = { d_tag?: string | null, lat?: number | null, lng?: number | null, geohash?: string | null, point?: string | null, polygon?: string | null, accuracy?: number | null, altitude?: number | null, tag_0?: string | null, label?: string | null, area?: number | null, elevation?: number | null, soil?: string | null, climate?: string | null, gc_id?: string | null, gc_name?: string | null, gc_admin1_id?: string | null, gc_admin1_name?: string | null, gc_country_id?: string | null, gc_country_name?: string | null, }; + +export type IGcsLocationFindMany = { filter: IGcsLocationFieldsFilter | null, } | { rel: GcsLocationFindManyRel, }; + +export type IGcsLocationFindManyResolve = IResultList<GcsLocation>; + +export type IGcsLocationFindOne = IGcsLocationFindOneArgs | IGcsLocationFindOneRelArgs; + +export type IGcsLocationFindOneArgs = { on: GcsLocationQueryBindValues, }; + +export type IGcsLocationFindOneRelArgs = { rel: GcsLocationFindManyRel, }; + +export type IGcsLocationFindOneResolve = IResult<GcsLocation>; + +export type IGcsLocationUpdate = { on: GcsLocationQueryBindValues, fields: IGcsLocationFieldsPartial, }; + +export type IGcsLocationUpdateResolve = IResult<GcsLocation>; + +export type ILogErrorCreate = ILogErrorFields; + +export type ILogErrorCreateResolve = IResult<LogError>; + +export type ILogErrorDelete = ILogErrorFindOne; + +export type ILogErrorDeleteResolve = IResult<string>; + +export type ILogErrorFields = { error: string, message: string, stack_trace?: string | null, cause?: string | null, app_system: string, app_version: string, nostr_pubkey: string, data?: string | null, }; + +export type ILogErrorFieldsFilter = { id?: string, created_at?: string, updated_at?: string, error?: string, message?: string, stack_trace?: string, cause?: string, app_system?: string, app_version?: string, nostr_pubkey?: string, data?: string, }; + +export type ILogErrorFieldsPartial = { error?: string | null, message?: string | null, stack_trace?: string | null, cause?: string | null, app_system?: string | null, app_version?: string | null, nostr_pubkey?: string | null, data?: string | null, }; + +export type ILogErrorFindMany = { filter: ILogErrorFieldsFilter | null, }; + +export type ILogErrorFindManyResolve = IResultList<LogError>; + +export type ILogErrorFindOne = ILogErrorFindOneArgs | ILogErrorFindOneRelArgs; + +export type ILogErrorFindOneArgs = { on: LogErrorQueryBindValues, }; + +export type ILogErrorFindOneRelArgs = { rel: LogErrorFindManyRel, }; + +export type ILogErrorFindOneResolve = IResult<LogError>; + +export type ILogErrorUpdate = { on: LogErrorQueryBindValues, fields: ILogErrorFieldsPartial, }; + +export type ILogErrorUpdateResolve = IResult<LogError>; + +export type IMediaImageCreate = IMediaImageFields; + +export type IMediaImageCreateResolve = IResult<MediaImage>; + +export type IMediaImageDelete = IMediaImageFindOne; + +export type IMediaImageDeleteResolve = IResult<string>; + +export type IMediaImageFields = { file_path: string, mime_type: string, res_base: string, res_path: string, label?: string | null, description?: string | null, }; + +export type IMediaImageFieldsFilter = { id?: string, created_at?: string, updated_at?: string, file_path?: string, mime_type?: string, res_base?: string, res_path?: string, label?: string, description?: string, }; + +export type IMediaImageFieldsPartial = { file_path?: string | null, mime_type?: string | null, res_base?: string | null, res_path?: string | null, label?: string | null, description?: string | null, }; + +export type IMediaImageFindMany = { filter: IMediaImageFieldsFilter | null, } | { rel: MediaImageFindManyRel, }; + +export type IMediaImageFindManyResolve = IResultList<MediaImage>; + +export type IMediaImageFindOne = IMediaImageFindOneArgs | IMediaImageFindOneRelArgs; + +export type IMediaImageFindOneArgs = { on: MediaImageQueryBindValues, }; + +export type IMediaImageFindOneRelArgs = { rel: MediaImageFindManyRel, }; + +export type IMediaImageFindOneResolve = IResult<MediaImage>; + +export type IMediaImageUpdate = { on: MediaImageQueryBindValues, fields: IMediaImageFieldsPartial, }; + +export type IMediaImageUpdateResolve = IResult<MediaImage>; + +export type INostrEventStateCreate = INostrEventStateFields; + +export type INostrEventStateCreateResolve = IResult<NostrEventState>; + +export type INostrEventStateDelete = INostrEventStateFindOne; + +export type INostrEventStateDeleteResolve = IResult<string>; + +export type INostrEventStateFields = { key: string, kind: number, pubkey: string, d_tag: string, last_event_id: string, last_created_at: number, content_hash: string, }; + +export type INostrEventStateFieldsFilter = { id?: string, created_at?: string, updated_at?: string, key?: string, kind?: number, pubkey?: string, d_tag?: string, last_event_id?: string, last_created_at?: number, content_hash?: string, }; + +export type INostrEventStateFieldsPartial = { key?: string | null, kind?: number | null, pubkey?: string | null, d_tag?: string | null, last_event_id?: string | null, last_created_at?: number | null, content_hash?: string | null, }; + +export type INostrEventStateFindMany = { filter: INostrEventStateFieldsFilter | null, }; + +export type INostrEventStateFindManyResolve = IResultList<NostrEventState>; + +export type INostrEventStateFindOne = INostrEventStateFindOneArgs | INostrEventStateFindOneRelArgs; + +export type INostrEventStateFindOneArgs = { on: NostrEventStateQueryBindValues, }; + +export type INostrEventStateFindOneRelArgs = { rel: NostrEventStateFindManyRel, }; + +export type INostrEventStateFindOneResolve = IResult<NostrEventState>; + +export type INostrEventStateUpdate = { on: NostrEventStateQueryBindValues, fields: INostrEventStateFieldsPartial, }; + +export type INostrEventStateUpdateResolve = IResult<NostrEventState>; + +export type INostrProfileCreate = INostrProfileFields; + +export type INostrProfileCreateResolve = IResult<NostrProfile>; + +export type INostrProfileDelete = INostrProfileFindOne; + +export type INostrProfileDeleteResolve = IResult<string>; + +export type INostrProfileFields = { public_key: string, profile_type: string, name: string, display_name?: string | null, about?: string | null, website?: string | null, picture?: string | null, banner?: string | null, nip05?: string | null, lud06?: string | null, lud16?: string | null, }; + +export type INostrProfileFieldsFilter = { id?: string, created_at?: string, updated_at?: string, public_key?: string, profile_type?: string, name?: string, display_name?: string, about?: string, website?: string, picture?: string, banner?: string, nip05?: string, lud06?: string, lud16?: string, }; + +export type INostrProfileFieldsPartial = { public_key?: string | null, profile_type?: string | null, name?: string | null, display_name?: string | null, about?: string | null, website?: string | null, picture?: string | null, banner?: string | null, nip05?: string | null, lud06?: string | null, lud16?: string | null, }; + +export type INostrProfileFindMany = { filter: INostrProfileFieldsFilter | null, } | { rel: NostrProfileFindManyRel, }; + +export type INostrProfileFindManyResolve = IResultList<NostrProfile>; + +export type INostrProfileFindOne = INostrProfileFindOneArgs | INostrProfileFindOneRelArgs; + +export type INostrProfileFindOneArgs = { on: NostrProfileQueryBindValues, }; + +export type INostrProfileFindOneRelArgs = { rel: NostrProfileFindManyRel, }; + +export type INostrProfileFindOneResolve = IResult<NostrProfile>; + +export type INostrProfileRelayRelation = { nostr_profile: NostrProfileQueryBindValues, nostr_relay: NostrRelayQueryBindValues, }; + +export type INostrProfileRelayResolve = IResultPass; + +export type INostrProfileUpdate = { on: NostrProfileQueryBindValues, fields: INostrProfileFieldsPartial, }; + +export type INostrProfileUpdateResolve = IResult<NostrProfile>; + +export type INostrRelayCreate = INostrRelayFields; + +export type INostrRelayCreateResolve = IResult<NostrRelay>; + +export type INostrRelayDelete = INostrRelayFindOne; + +export type INostrRelayDeleteResolve = IResult<string>; + +export type INostrRelayFields = { url: string, relay_id?: string | null, name?: string | null, description?: string | null, pubkey?: string | null, contact?: string | null, supported_nips?: string | null, software?: string | null, version?: string | null, data?: string | null, }; + +export type INostrRelayFieldsFilter = { id?: string, created_at?: string, updated_at?: string, url?: string, relay_id?: string, name?: string, description?: string, pubkey?: string, contact?: string, supported_nips?: string, software?: string, version?: string, data?: string, }; + +export type INostrRelayFieldsPartial = { url?: string | null, relay_id?: string | null, name?: string | null, description?: string | null, pubkey?: string | null, contact?: string | null, supported_nips?: string | null, software?: string | null, version?: string | null, data?: string | null, }; + +export type INostrRelayFindMany = { filter: INostrRelayFieldsFilter | null, } | { rel: NostrRelayFindManyRel, }; + +export type INostrRelayFindManyResolve = IResultList<NostrRelay>; + +export type INostrRelayFindOne = INostrRelayFindOneArgs | INostrRelayFindOneRelArgs; + +export type INostrRelayFindOneArgs = { on: NostrRelayQueryBindValues, }; + +export type INostrRelayFindOneRelArgs = { rel: NostrRelayFindManyRel, }; + +export type INostrRelayFindOneResolve = IResult<NostrRelay>; + +export type INostrRelayUpdate = { on: NostrRelayQueryBindValues, fields: INostrRelayFieldsPartial, }; + +export type INostrRelayUpdateResolve = IResult<NostrRelay>; + +export type IPlotCreate = IPlotFields; + +export type IPlotCreateResolve = IResult<Plot>; + +export type IPlotDelete = IPlotFindOne; + +export type IPlotDeleteResolve = IResult<string>; + +export type IPlotFields = { d_tag: string, farm_id: string, name: string, about?: string | null, location_primary?: string | null, location_city?: string | null, location_region?: string | null, location_country?: string | null, }; + +export type IPlotFieldsFilter = { id?: string, created_at?: string, updated_at?: string, d_tag?: string, farm_id?: string, name?: string, about?: string, location_primary?: string, location_city?: string, location_region?: string, location_country?: string, }; + +export type IPlotFieldsPartial = { d_tag?: string | null, farm_id?: string | null, name?: string | null, about?: string | null, location_primary?: string | null, location_city?: string | null, location_region?: string | null, location_country?: string | null, }; + +export type IPlotFindMany = { filter: IPlotFieldsFilter | null, }; + +export type IPlotFindManyResolve = IResultList<Plot>; + +export type IPlotFindOne = IPlotFindOneArgs | IPlotFindOneRelArgs; + +export type IPlotFindOneArgs = { on: PlotQueryBindValues, }; + +export type IPlotFindOneRelArgs = { rel: PlotFindManyRel, }; + +export type IPlotFindOneResolve = IResult<Plot>; + +export type IPlotGcsLocationCreate = IPlotGcsLocationFields; + +export type IPlotGcsLocationCreateResolve = IResult<PlotGcsLocation>; + +export type IPlotGcsLocationDelete = IPlotGcsLocationFindOne; + +export type IPlotGcsLocationDeleteResolve = IResult<string>; + +export type IPlotGcsLocationFields = { plot_id: string, gcs_location_id: string, role: string, }; + +export type IPlotGcsLocationFieldsFilter = { id?: string, created_at?: string, updated_at?: string, plot_id?: string, gcs_location_id?: string, role?: string, }; + +export type IPlotGcsLocationFieldsPartial = { plot_id?: string | null, gcs_location_id?: string | null, role?: string | null, }; + +export type IPlotGcsLocationFindMany = { filter: IPlotGcsLocationFieldsFilter | null, }; + +export type IPlotGcsLocationFindManyResolve = IResultList<PlotGcsLocation>; + +export type IPlotGcsLocationFindOne = IPlotGcsLocationFindOneArgs | IPlotGcsLocationFindOneRelArgs; + +export type IPlotGcsLocationFindOneArgs = { on: PlotGcsLocationQueryBindValues, }; + +export type IPlotGcsLocationFindOneRelArgs = { rel: PlotGcsLocationFindManyRel, }; + +export type IPlotGcsLocationFindOneResolve = IResult<PlotGcsLocation>; + +export type IPlotGcsLocationUpdate = { on: PlotGcsLocationQueryBindValues, fields: IPlotGcsLocationFieldsPartial, }; + +export type IPlotGcsLocationUpdateResolve = IResult<PlotGcsLocation>; + +export type IPlotTagCreate = IPlotTagFields; + +export type IPlotTagCreateResolve = IResult<PlotTag>; + +export type IPlotTagDelete = IPlotTagFindOne; + +export type IPlotTagDeleteResolve = IResult<string>; + +export type IPlotTagFields = { plot_id: string, tag: string, }; + +export type IPlotTagFieldsFilter = { id?: string, created_at?: string, updated_at?: string, plot_id?: string, tag?: string, }; + +export type IPlotTagFieldsPartial = { plot_id?: string | null, tag?: string | null, }; + +export type IPlotTagFindMany = { filter: IPlotTagFieldsFilter | null, }; + +export type IPlotTagFindManyResolve = IResultList<PlotTag>; + +export type IPlotTagFindOne = IPlotTagFindOneArgs | IPlotTagFindOneRelArgs; + +export type IPlotTagFindOneArgs = { on: PlotTagQueryBindValues, }; + +export type IPlotTagFindOneRelArgs = { rel: PlotTagFindManyRel, }; + +export type IPlotTagFindOneResolve = IResult<PlotTag>; + +export type IPlotTagUpdate = { on: PlotTagQueryBindValues, fields: IPlotTagFieldsPartial, }; + +export type IPlotTagUpdateResolve = IResult<PlotTag>; + +export type IPlotUpdate = { on: PlotQueryBindValues, fields: IPlotFieldsPartial, }; + +export type IPlotUpdateResolve = IResult<Plot>; + +export type ITradeProductCreate = ITradeProductFields; + +export type ITradeProductCreateResolve = IResult<TradeProduct>; + +export type ITradeProductDelete = ITradeProductFindOne; + +export type ITradeProductDeleteResolve = IResult<string>; + +export type ITradeProductFields = { key: string, category: string, title: string, summary: string, process: string, lot: string, profile: string, year: bigint, qty_amt: bigint, qty_unit: string, qty_label?: string | null, qty_avail?: number | null, price_amt: number, price_currency: string, price_qty_amt: number, price_qty_unit: string, notes?: string | null, }; + +export type ITradeProductFieldsFilter = { id?: string, created_at?: string, updated_at?: string, key?: string, category?: string, title?: string, summary?: string, process?: string, lot?: string, profile?: string, year?: bigint, qty_amt?: bigint, qty_unit?: string, qty_label?: string, qty_avail?: bigint, price_amt?: number, price_currency?: string, price_qty_amt?: number, price_qty_unit?: string, notes?: string, }; + +export type ITradeProductFieldsPartial = { key?: string | null, category?: string | null, title?: string | null, summary?: string | null, process?: string | null, lot?: string | null, profile?: string | null, year?: number | null, qty_amt?: number | null, qty_unit?: string | null, qty_label?: string | null, qty_avail?: number | null, price_amt?: number | null, price_currency?: string | null, price_qty_amt?: number | null, price_qty_unit?: string | null, notes?: string | null, }; + +export type ITradeProductFindMany = { filter: ITradeProductFieldsFilter | null, }; + +export type ITradeProductFindManyResolve = IResultList<TradeProduct>; + +export type ITradeProductFindOne = ITradeProductFindOneArgs | ITradeProductFindOneRelArgs; + +export type ITradeProductFindOneArgs = { on: TradeProductQueryBindValues, }; + +export type ITradeProductFindOneRelArgs = { rel: TradeProductFindManyRel, }; + +export type ITradeProductFindOneResolve = IResult<TradeProduct>; + +export type ITradeProductLocationRelation = { trade_product: TradeProductQueryBindValues, gcs_location: GcsLocationQueryBindValues, }; + +export type ITradeProductLocationResolve = IResultPass; + +export type ITradeProductMediaRelation = { trade_product: TradeProductQueryBindValues, media_image: MediaImageQueryBindValues, }; + +export type ITradeProductMediaResolve = IResultPass; + +export type ITradeProductUpdate = { on: TradeProductQueryBindValues, fields: ITradeProductFieldsPartial, }; + +export type ITradeProductUpdateResolve = IResult<TradeProduct>; + +export type LogError = { id: string, created_at: string, updated_at: string, error: string, message: string, stack_trace: string | null, cause: string | null, app_system: string, app_version: string, nostr_pubkey: string, data: string | null, }; + +export type LogErrorFindManyRel = never; + +export type LogErrorQueryBindValues = { id: string, } | { nostr_pubkey: string, }; + +export type MediaImage = { id: string, created_at: string, updated_at: string, file_path: string, mime_type: string, res_base: string, res_path: string, label: string | null, description: string | null, }; + +export type MediaImageFindManyRel = { "on_trade_product": MediaImageTradeProductArgs } | { "off_trade_product": MediaImageTradeProductArgs }; + +export type MediaImageQueryBindValues = { id: string, } | { file_path: string, }; + +export type MediaImageTradeProductArgs = { id: string, }; + +export type NostrEventState = { id: string, created_at: string, updated_at: string, key: string, kind: number, pubkey: string, d_tag: string, last_event_id: string, last_created_at: number, content_hash: string, }; + +export type NostrEventStateFindManyRel = never; + +export type NostrEventStateQueryBindValues = { id: string, } | { key: string, }; + +export type NostrProfile = { id: string, created_at: string, updated_at: string, public_key: string, profile_type: string, name: string, display_name: string | null, about: string | null, website: string | null, picture: string | null, banner: string | null, nip05: string | null, lud06: string | null, lud16: string | null, }; + +export type NostrProfileFindManyRel = { "on_relay": NostrProfileRelayArgs } | { "off_relay": NostrProfileRelayArgs }; + +export type NostrProfileQueryBindValues = { id: string, } | { public_key: string, }; + +export type NostrProfileRelayArgs = { id: string, }; + +export type NostrRelay = { id: string, created_at: string, updated_at: string, url: string, relay_id: string | null, name: string | null, description: string | null, pubkey: string | null, contact: string | null, supported_nips: string | null, software: string | null, version: string | null, data: string | null, }; + +export type NostrRelayFindManyRel = { "on_profile": NostrRelayProfileArgs } | { "off_profile": NostrRelayProfileArgs }; + +export type NostrRelayProfileArgs = { public_key: string, }; + +export type NostrRelayQueryBindValues = { id: string, } | { url: string, }; + +export type Plot = { id: string, created_at: string, updated_at: string, d_tag: string, farm_id: string, name: string, about: string | null, location_primary: string | null, location_city: string | null, location_region: string | null, location_country: string | null, }; + +export type PlotFindManyRel = never; + +export type PlotGcsLocation = { id: string, created_at: string, updated_at: string, plot_id: string, gcs_location_id: string, role: string, }; + +export type PlotGcsLocationFindManyRel = never; + +export type PlotGcsLocationQueryBindValues = { id: string, } | { plot_id: string, } | { gcs_location_id: string, }; + +export type PlotQueryBindValues = { id: string, } | { d_tag: string, } | { farm_id: string, }; + +export type PlotTag = { id: string, created_at: string, updated_at: string, plot_id: string, tag: string, }; + +export type PlotTagFindManyRel = never; + +export type PlotTagQueryBindValues = { id: string, } | { plot_id: string, } | { tag: string, }; + +export type TradeProduct = { id: string, created_at: string, updated_at: string, key: string, category: string, title: string, summary: string, process: string, lot: string, profile: string, year: bigint, qty_amt: bigint, qty_unit: string, qty_label: string | null, qty_avail: bigint | null, price_amt: number, price_currency: string, price_qty_amt: number, price_qty_unit: string, notes: string | null, }; + +export type TradeProductFindManyRel = never; + +export type TradeProductQueryBindValues = { id: string, }; diff --git a/tangle-schema/bindings/ts/tsconfig.cjs.json b/tangle-db-schema/bindings/ts/tsconfig.cjs.json diff --git a/tangle-schema/bindings/ts/tsconfig.esm.json b/tangle-db-schema/bindings/ts/tsconfig.esm.json diff --git a/tangle-schema/bindings/ts/tsconfig.json b/tangle-db-schema/bindings/ts/tsconfig.json diff --git a/tangle-schema/build.rs b/tangle-db-schema/build.rs diff --git a/tangle-schema/src/lib.rs b/tangle-db-schema/src/lib.rs diff --git a/tangle-db-schema/src/models/farm.rs b/tangle-db-schema/src/models/farm.rs @@ -0,0 +1,285 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct Farm { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub d_tag: String, + pub pubkey: String, + pub name: String, + pub about: Option<String>, + pub website: Option<String>, + pub picture: Option<String>, + pub banner: Option<String>, + pub location_primary: Option<String>, + pub location_city: Option<String>, + pub location_region: Option<String>, + pub location_country: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmFields { + pub d_tag: String, + pub pubkey: String, + pub name: String, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub about: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub website: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub picture: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub banner: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_primary: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_city: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_region: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_country: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub d_tag: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub pubkey: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub name: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub about: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub website: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub picture: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub banner: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_primary: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_city: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_region: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_country: Option<serde_json::Value>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub d_tag: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub pubkey: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub about: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub website: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub picture: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub banner: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub location_primary: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub location_city: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub location_region: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub location_country: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum FarmQueryBindValues { + Id { id: String }, + DTag { d_tag: String }, + Pubkey { pubkey: String }, +} +impl FarmQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::DTag { d_tag } => ("d_tag", Value::from(d_tag.clone())), + Self::Pubkey { pubkey } => ("pubkey", Value::from(pubkey.clone())), + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::DTag { d_tag } => d_tag.clone(), + Self::Pubkey { pubkey } => pubkey.clone(), + } + } +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum FarmFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmCreate", + type = "IFarmFields" + ) +)] +pub struct IFarmCreateTs; +pub type IFarmCreate = IFarmFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmCreateResolve", + type = "IResult<Farm>" + ) +)] +pub struct IFarmCreateResolveTs; +pub type IFarmCreateResolve = IResult<Farm>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmFindOneArgs { + pub on: FarmQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmFindOneRelArgs { + pub rel: FarmFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum IFarmFindOne { + On(IFarmFindOneArgs), + Rel(IFarmFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmFindOneResolve", + type = "IResult<Farm>" + ) +)] +pub struct IFarmFindOneResolveTs; +pub type IFarmFindOneResolve = IResult<Option<Farm>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmFindMany") +)] +#[derive(Deserialize, Serialize)] +pub struct IFarmFindManyArgs { + pub filter: Option<IFarmFieldsFilter>, +} +pub type IFarmFindMany = IFarmFindManyArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmFindManyResolve", + type = "IResultList<Farm>" + ) +)] +pub struct IFarmFindManyResolveTs; +pub type IFarmFindManyResolve = IResultList<Farm>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmDelete", + type = "IFarmFindOne" + ) +)] +pub struct IFarmDeleteTs; +pub type IFarmDelete = IFarmFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct IFarmDeleteResolveTs; +pub type IFarmDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmUpdate") +)] +#[derive(Deserialize, Serialize)] +pub struct IFarmUpdateArgs { + pub on: FarmQueryBindValues, + pub fields: IFarmFieldsPartial, +} +pub type IFarmUpdate = IFarmUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmUpdateResolve", + type = "IResult<Farm>" + ) +)] +pub struct IFarmUpdateResolveTs; +pub type IFarmUpdateResolve = IResult<Farm>; diff --git a/tangle-db-schema/src/models/farm_gcs_location.rs b/tangle-db-schema/src/models/farm_gcs_location.rs @@ -0,0 +1,228 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct FarmGcsLocation { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub farm_id: String, + pub gcs_location_id: String, + pub role: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmGcsLocationFields { + pub farm_id: String, + pub gcs_location_id: String, + pub role: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmGcsLocationFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub farm_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gcs_location_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub role: Option<serde_json::Value>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmGcsLocationFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub farm_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub gcs_location_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub role: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum FarmGcsLocationQueryBindValues { + Id { id: String }, + FarmId { farm_id: String }, + GcsLocationId { gcs_location_id: String }, +} +impl FarmGcsLocationQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::FarmId { farm_id } => ("farm_id", Value::from(farm_id.clone())), + Self::GcsLocationId { gcs_location_id } => { + ("gcs_location_id", Value::from(gcs_location_id.clone())) + } + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::FarmId { farm_id } => farm_id.clone(), + Self::GcsLocationId { gcs_location_id } => gcs_location_id.clone(), + } + } +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum FarmGcsLocationFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmGcsLocationCreate", + type = "IFarmGcsLocationFields" + ) +)] +pub struct IFarmGcsLocationCreateTs; +pub type IFarmGcsLocationCreate = IFarmGcsLocationFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmGcsLocationCreateResolve", + type = "IResult<FarmGcsLocation>" + ) +)] +pub struct IFarmGcsLocationCreateResolveTs; +pub type IFarmGcsLocationCreateResolve = IResult<FarmGcsLocation>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmGcsLocationFindOneArgs { + pub on: FarmGcsLocationQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmGcsLocationFindOneRelArgs { + pub rel: FarmGcsLocationFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmGcsLocationFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum IFarmGcsLocationFindOne { + On(IFarmGcsLocationFindOneArgs), + Rel(IFarmGcsLocationFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmGcsLocationFindOneResolve", + type = "IResult<FarmGcsLocation>" + ) +)] +pub struct IFarmGcsLocationFindOneResolveTs; +pub type IFarmGcsLocationFindOneResolve = IResult<Option<FarmGcsLocation>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmGcsLocationFindMany") +)] +#[derive(Deserialize, Serialize)] +pub struct IFarmGcsLocationFindManyArgs { + pub filter: Option<IFarmGcsLocationFieldsFilter>, +} +pub type IFarmGcsLocationFindMany = IFarmGcsLocationFindManyArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmGcsLocationFindManyResolve", + type = "IResultList<FarmGcsLocation>" + ) +)] +pub struct IFarmGcsLocationFindManyResolveTs; +pub type IFarmGcsLocationFindManyResolve = IResultList<FarmGcsLocation>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmGcsLocationDelete", + type = "IFarmGcsLocationFindOne" + ) +)] +pub struct IFarmGcsLocationDeleteTs; +pub type IFarmGcsLocationDelete = IFarmGcsLocationFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmGcsLocationDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct IFarmGcsLocationDeleteResolveTs; +pub type IFarmGcsLocationDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts", rename = "IFarmGcsLocationUpdate"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmGcsLocationUpdateArgs { + pub on: FarmGcsLocationQueryBindValues, + pub fields: IFarmGcsLocationFieldsPartial, +} +pub type IFarmGcsLocationUpdate = IFarmGcsLocationUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmGcsLocationUpdateResolve", + type = "IResult<FarmGcsLocation>" + ) +)] +pub struct IFarmGcsLocationUpdateResolveTs; +pub type IFarmGcsLocationUpdateResolve = IResult<FarmGcsLocation>; diff --git a/tangle-db-schema/src/models/farm_member.rs b/tangle-db-schema/src/models/farm_member.rs @@ -0,0 +1,228 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct FarmMember { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub farm_id: String, + pub member_pubkey: String, + pub role: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmMemberFields { + pub farm_id: String, + pub member_pubkey: String, + pub role: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmMemberFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub farm_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub member_pubkey: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub role: Option<serde_json::Value>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmMemberFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub farm_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub member_pubkey: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub role: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum FarmMemberQueryBindValues { + Id { id: String }, + FarmId { farm_id: String }, + MemberPubkey { member_pubkey: String }, +} +impl FarmMemberQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::FarmId { farm_id } => ("farm_id", Value::from(farm_id.clone())), + Self::MemberPubkey { member_pubkey } => { + ("member_pubkey", Value::from(member_pubkey.clone())) + } + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::FarmId { farm_id } => farm_id.clone(), + Self::MemberPubkey { member_pubkey } => member_pubkey.clone(), + } + } +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum FarmMemberFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberCreate", + type = "IFarmMemberFields" + ) +)] +pub struct IFarmMemberCreateTs; +pub type IFarmMemberCreate = IFarmMemberFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberCreateResolve", + type = "IResult<FarmMember>" + ) +)] +pub struct IFarmMemberCreateResolveTs; +pub type IFarmMemberCreateResolve = IResult<FarmMember>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmMemberFindOneArgs { + pub on: FarmMemberQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmMemberFindOneRelArgs { + pub rel: FarmMemberFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmMemberFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum IFarmMemberFindOne { + On(IFarmMemberFindOneArgs), + Rel(IFarmMemberFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberFindOneResolve", + type = "IResult<FarmMember>" + ) +)] +pub struct IFarmMemberFindOneResolveTs; +pub type IFarmMemberFindOneResolve = IResult<Option<FarmMember>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmMemberFindMany") +)] +#[derive(Deserialize, Serialize)] +pub struct IFarmMemberFindManyArgs { + pub filter: Option<IFarmMemberFieldsFilter>, +} +pub type IFarmMemberFindMany = IFarmMemberFindManyArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberFindManyResolve", + type = "IResultList<FarmMember>" + ) +)] +pub struct IFarmMemberFindManyResolveTs; +pub type IFarmMemberFindManyResolve = IResultList<FarmMember>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberDelete", + type = "IFarmMemberFindOne" + ) +)] +pub struct IFarmMemberDeleteTs; +pub type IFarmMemberDelete = IFarmMemberFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct IFarmMemberDeleteResolveTs; +pub type IFarmMemberDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts", rename = "IFarmMemberUpdate"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmMemberUpdateArgs { + pub on: FarmMemberQueryBindValues, + pub fields: IFarmMemberFieldsPartial, +} +pub type IFarmMemberUpdate = IFarmMemberUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberUpdateResolve", + type = "IResult<FarmMember>" + ) +)] +pub struct IFarmMemberUpdateResolveTs; +pub type IFarmMemberUpdateResolve = IResult<FarmMember>; diff --git a/tangle-db-schema/src/models/farm_member_claim.rs b/tangle-db-schema/src/models/farm_member_claim.rs @@ -0,0 +1,224 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct FarmMemberClaim { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub member_pubkey: String, + pub farm_pubkey: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmMemberClaimFields { + pub member_pubkey: String, + pub farm_pubkey: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmMemberClaimFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub member_pubkey: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub farm_pubkey: Option<serde_json::Value>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmMemberClaimFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub member_pubkey: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub farm_pubkey: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum FarmMemberClaimQueryBindValues { + Id { id: String }, + MemberPubkey { member_pubkey: String }, + FarmPubkey { farm_pubkey: String }, +} +impl FarmMemberClaimQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::MemberPubkey { member_pubkey } => { + ("member_pubkey", Value::from(member_pubkey.clone())) + } + Self::FarmPubkey { farm_pubkey } => { + ("farm_pubkey", Value::from(farm_pubkey.clone())) + } + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::MemberPubkey { member_pubkey } => member_pubkey.clone(), + Self::FarmPubkey { farm_pubkey } => farm_pubkey.clone(), + } + } +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum FarmMemberClaimFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberClaimCreate", + type = "IFarmMemberClaimFields" + ) +)] +pub struct IFarmMemberClaimCreateTs; +pub type IFarmMemberClaimCreate = IFarmMemberClaimFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberClaimCreateResolve", + type = "IResult<FarmMemberClaim>" + ) +)] +pub struct IFarmMemberClaimCreateResolveTs; +pub type IFarmMemberClaimCreateResolve = IResult<FarmMemberClaim>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmMemberClaimFindOneArgs { + pub on: FarmMemberClaimQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmMemberClaimFindOneRelArgs { + pub rel: FarmMemberClaimFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmMemberClaimFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum IFarmMemberClaimFindOne { + On(IFarmMemberClaimFindOneArgs), + Rel(IFarmMemberClaimFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberClaimFindOneResolve", + type = "IResult<FarmMemberClaim>" + ) +)] +pub struct IFarmMemberClaimFindOneResolveTs; +pub type IFarmMemberClaimFindOneResolve = IResult<Option<FarmMemberClaim>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmMemberClaimFindMany") +)] +#[derive(Deserialize, Serialize)] +pub struct IFarmMemberClaimFindManyArgs { + pub filter: Option<IFarmMemberClaimFieldsFilter>, +} +pub type IFarmMemberClaimFindMany = IFarmMemberClaimFindManyArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberClaimFindManyResolve", + type = "IResultList<FarmMemberClaim>" + ) +)] +pub struct IFarmMemberClaimFindManyResolveTs; +pub type IFarmMemberClaimFindManyResolve = IResultList<FarmMemberClaim>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberClaimDelete", + type = "IFarmMemberClaimFindOne" + ) +)] +pub struct IFarmMemberClaimDeleteTs; +pub type IFarmMemberClaimDelete = IFarmMemberClaimFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberClaimDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct IFarmMemberClaimDeleteResolveTs; +pub type IFarmMemberClaimDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts", rename = "IFarmMemberClaimUpdate"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmMemberClaimUpdateArgs { + pub on: FarmMemberClaimQueryBindValues, + pub fields: IFarmMemberClaimFieldsPartial, +} +pub type IFarmMemberClaimUpdate = IFarmMemberClaimUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmMemberClaimUpdateResolve", + type = "IResult<FarmMemberClaim>" + ) +)] +pub struct IFarmMemberClaimUpdateResolveTs; +pub type IFarmMemberClaimUpdateResolve = IResult<FarmMemberClaim>; diff --git a/tangle-db-schema/src/models/farm_tag.rs b/tangle-db-schema/src/models/farm_tag.rs @@ -0,0 +1,220 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct FarmTag { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub farm_id: String, + pub tag: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmTagFields { + pub farm_id: String, + pub tag: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmTagFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub farm_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub tag: Option<serde_json::Value>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IFarmTagFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub farm_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub tag: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum FarmTagQueryBindValues { + Id { id: String }, + FarmId { farm_id: String }, + Tag { tag: String }, +} +impl FarmTagQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::FarmId { farm_id } => ("farm_id", Value::from(farm_id.clone())), + Self::Tag { tag } => ("tag", Value::from(tag.clone())), + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::FarmId { farm_id } => farm_id.clone(), + Self::Tag { tag } => tag.clone(), + } + } +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum FarmTagFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmTagCreate", + type = "IFarmTagFields" + ) +)] +pub struct IFarmTagCreateTs; +pub type IFarmTagCreate = IFarmTagFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmTagCreateResolve", + type = "IResult<FarmTag>" + ) +)] +pub struct IFarmTagCreateResolveTs; +pub type IFarmTagCreateResolve = IResult<FarmTag>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmTagFindOneArgs { + pub on: FarmTagQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmTagFindOneRelArgs { + pub rel: FarmTagFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmTagFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum IFarmTagFindOne { + On(IFarmTagFindOneArgs), + Rel(IFarmTagFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmTagFindOneResolve", + type = "IResult<FarmTag>" + ) +)] +pub struct IFarmTagFindOneResolveTs; +pub type IFarmTagFindOneResolve = IResult<Option<FarmTag>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IFarmTagFindMany") +)] +#[derive(Deserialize, Serialize)] +pub struct IFarmTagFindManyArgs { + pub filter: Option<IFarmTagFieldsFilter>, +} +pub type IFarmTagFindMany = IFarmTagFindManyArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmTagFindManyResolve", + type = "IResultList<FarmTag>" + ) +)] +pub struct IFarmTagFindManyResolveTs; +pub type IFarmTagFindManyResolve = IResultList<FarmTag>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmTagDelete", + type = "IFarmTagFindOne" + ) +)] +pub struct IFarmTagDeleteTs; +pub type IFarmTagDelete = IFarmTagFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmTagDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct IFarmTagDeleteResolveTs; +pub type IFarmTagDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts", rename = "IFarmTagUpdate"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmTagUpdateArgs { + pub on: FarmTagQueryBindValues, + pub fields: IFarmTagFieldsPartial, +} +pub type IFarmTagUpdate = IFarmTagUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IFarmTagUpdateResolve", + type = "IResult<FarmTag>" + ) +)] +pub struct IFarmTagUpdateResolveTs; +pub type IFarmTagUpdateResolve = IResult<FarmTag>; diff --git a/tangle-db-schema/src/models/gcs_location.rs b/tangle-db-schema/src/models/gcs_location.rs @@ -0,0 +1,379 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct GcsLocation { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub d_tag: String, + pub lat: f64, + pub lng: f64, + pub geohash: String, + pub point: String, + pub polygon: String, + pub accuracy: Option<f64>, + pub altitude: Option<f64>, + pub tag_0: Option<String>, + pub label: Option<String>, + pub area: Option<f64>, + pub elevation: Option<u32>, + pub soil: Option<String>, + pub climate: Option<String>, + pub gc_id: Option<String>, + pub gc_name: Option<String>, + pub gc_admin1_id: Option<String>, + pub gc_admin1_name: Option<String>, + pub gc_country_id: Option<String>, + pub gc_country_name: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IGcsLocationFields { + pub d_tag: String, + pub lat: f64, + pub lng: f64, + pub geohash: String, + pub point: String, + pub polygon: String, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub accuracy: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub altitude: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub tag_0: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub label: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub area: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub elevation: Option<u32>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub soil: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub climate: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_admin1_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_admin1_name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_country_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_country_name: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IGcsLocationFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub d_tag: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub lat: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub lng: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub geohash: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub point: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub polygon: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub accuracy: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub altitude: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub tag_0: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub label: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub area: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub elevation: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub soil: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub climate: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_name: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_admin1_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_admin1_name: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_country_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gc_country_name: Option<serde_json::Value>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IGcsLocationFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub d_tag: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub lat: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub lng: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub geohash: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub point: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub polygon: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub accuracy: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub altitude: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub tag_0: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub label: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub area: Option<f64>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub elevation: Option<u32>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub soil: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub climate: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub gc_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub gc_name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub gc_admin1_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub gc_admin1_name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub gc_country_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub gc_country_name: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum GcsLocationQueryBindValues { + Id { id: String }, + DTag { d_tag: String }, + Geohash { geohash: String }, +} +impl GcsLocationQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::DTag { d_tag } => ("d_tag", Value::from(d_tag.clone())), + Self::Geohash { geohash } => ("geohash", Value::from(geohash.clone())), + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::DTag { d_tag } => d_tag.clone(), + Self::Geohash { geohash } => geohash.clone(), + } + } +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct GcsLocationTradeProductArgs { + pub id: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct GcsLocationFarmArgs { + pub id: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct GcsLocationPlotArgs { + pub id: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum GcsLocationFindManyRel { + #[serde(rename = "on_trade_product")] + OnTradeProduct(GcsLocationTradeProductArgs), + #[serde(rename = "off_trade_product")] + OffTradeProduct(GcsLocationTradeProductArgs), + #[serde(rename = "on_farm")] + OnFarm(GcsLocationFarmArgs), + #[serde(rename = "off_farm")] + OffFarm(GcsLocationFarmArgs), + #[serde(rename = "on_plot")] + OnPlot(GcsLocationPlotArgs), + #[serde(rename = "off_plot")] + OffPlot(GcsLocationPlotArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IGcsLocationCreate", + type = "IGcsLocationFields" + ) +)] +pub struct IGcsLocationCreateTs; +pub type IGcsLocationCreate = IGcsLocationFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IGcsLocationCreateResolve", + type = "IResult<GcsLocation>" + ) +)] +pub struct IGcsLocationCreateResolveTs; +pub type IGcsLocationCreateResolve = IResult<GcsLocation>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IGcsLocationFindOneArgs { + pub on: GcsLocationQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IGcsLocationFindOneRelArgs { + pub rel: GcsLocationFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IGcsLocationFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum IGcsLocationFindOne { + On(IGcsLocationFindOneArgs), + Rel(IGcsLocationFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IGcsLocationFindOneResolve", + type = "IResult<GcsLocation>" + ) +)] +pub struct IGcsLocationFindOneResolveTs; +pub type IGcsLocationFindOneResolve = IResult<Option<GcsLocation>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IGcsLocationFindMany") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum IGcsLocationFindMany { + Filter { + filter: Option<IGcsLocationFieldsFilter>, + }, + Rel { + rel: GcsLocationFindManyRel, + }, +} +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IGcsLocationFindManyResolve", + type = "IResultList<GcsLocation>" + ) +)] +pub struct IGcsLocationFindManyResolveTs; +pub type IGcsLocationFindManyResolve = IResultList<GcsLocation>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IGcsLocationDelete", + type = "IGcsLocationFindOne" + ) +)] +pub struct IGcsLocationDeleteTs; +pub type IGcsLocationDelete = IGcsLocationFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IGcsLocationDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct IGcsLocationDeleteResolveTs; +pub type IGcsLocationDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts", rename = "IGcsLocationUpdate"))] +#[derive(Deserialize, Serialize)] +pub struct IGcsLocationUpdateArgs { + pub on: GcsLocationQueryBindValues, + pub fields: IGcsLocationFieldsPartial, +} +pub type IGcsLocationUpdate = IGcsLocationUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IGcsLocationUpdateResolve", + type = "IResult<GcsLocation>" + ) +)] +pub struct IGcsLocationUpdateResolveTs; +pub type IGcsLocationUpdateResolve = IResult<GcsLocation>; diff --git a/tangle-schema/src/models/log_error.rs b/tangle-db-schema/src/models/log_error.rs diff --git a/tangle-schema/src/models/media_image.rs b/tangle-db-schema/src/models/media_image.rs diff --git a/tangle-db-schema/src/models/mod.rs b/tangle-db-schema/src/models/mod.rs @@ -0,0 +1,18 @@ +pub mod farm; +pub mod farm_gcs_location; +pub mod farm_member; +pub mod farm_member_claim; +pub mod farm_tag; +pub mod gcs_location; +pub mod log_error; +pub mod media_image; +pub mod nostr_event_state; +pub mod nostr_profile; +pub mod nostr_profile_relay; +pub mod nostr_relay; +pub mod plot; +pub mod plot_gcs_location; +pub mod plot_tag; +pub mod trade_product; +pub mod trade_product_location; +pub mod trade_product_media; diff --git a/tangle-db-schema/src/models/nostr_event_state.rs b/tangle-db-schema/src/models/nostr_event_state.rs @@ -0,0 +1,247 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct NostrEventState { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub key: String, + pub kind: u32, + pub pubkey: String, + pub d_tag: String, + pub last_event_id: String, + pub last_created_at: u32, + pub content_hash: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct INostrEventStateFields { + pub key: String, + pub kind: u32, + pub pubkey: String, + pub d_tag: String, + pub last_event_id: String, + pub last_created_at: u32, + pub content_hash: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct INostrEventStateFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub key: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub kind: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub pubkey: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub d_tag: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub last_event_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] + pub last_created_at: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub content_hash: Option<serde_json::Value>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct INostrEventStateFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub key: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub kind: Option<u32>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub pubkey: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub d_tag: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub last_event_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub last_created_at: Option<u32>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub content_hash: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum NostrEventStateQueryBindValues { + Id { id: String }, + Key { key: String }, +} +impl NostrEventStateQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::Key { key } => ("key", Value::from(key.clone())), + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::Key { key } => key.clone(), + } + } +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum NostrEventStateFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrEventStateCreate", + type = "INostrEventStateFields" + ) +)] +pub struct INostrEventStateCreateTs; +pub type INostrEventStateCreate = INostrEventStateFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrEventStateCreateResolve", + type = "IResult<NostrEventState>" + ) +)] +pub struct INostrEventStateCreateResolveTs; +pub type INostrEventStateCreateResolve = IResult<NostrEventState>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct INostrEventStateFindOneArgs { + pub on: NostrEventStateQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct INostrEventStateFindOneRelArgs { + pub rel: NostrEventStateFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "INostrEventStateFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum INostrEventStateFindOne { + On(INostrEventStateFindOneArgs), + Rel(INostrEventStateFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrEventStateFindOneResolve", + type = "IResult<NostrEventState>" + ) +)] +pub struct INostrEventStateFindOneResolveTs; +pub type INostrEventStateFindOneResolve = IResult<Option<NostrEventState>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "INostrEventStateFindMany") +)] +#[derive(Deserialize, Serialize)] +pub struct INostrEventStateFindManyArgs { + pub filter: Option<INostrEventStateFieldsFilter>, +} +pub type INostrEventStateFindMany = INostrEventStateFindManyArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrEventStateFindManyResolve", + type = "IResultList<NostrEventState>" + ) +)] +pub struct INostrEventStateFindManyResolveTs; +pub type INostrEventStateFindManyResolve = IResultList<NostrEventState>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrEventStateDelete", + type = "INostrEventStateFindOne" + ) +)] +pub struct INostrEventStateDeleteTs; +pub type INostrEventStateDelete = INostrEventStateFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrEventStateDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct INostrEventStateDeleteResolveTs; +pub type INostrEventStateDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts", rename = "INostrEventStateUpdate"))] +#[derive(Deserialize, Serialize)] +pub struct INostrEventStateUpdateArgs { + pub on: NostrEventStateQueryBindValues, + pub fields: INostrEventStateFieldsPartial, +} +pub type INostrEventStateUpdate = INostrEventStateUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrEventStateUpdateResolve", + type = "IResult<NostrEventState>" + ) +)] +pub struct INostrEventStateUpdateResolveTs; +pub type INostrEventStateUpdateResolve = IResult<NostrEventState>; diff --git a/tangle-db-schema/src/models/nostr_profile.rs b/tangle-db-schema/src/models/nostr_profile.rs @@ -0,0 +1,291 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct NostrProfile { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub public_key: String, + pub profile_type: String, + pub name: String, + pub display_name: Option<String>, + pub about: Option<String>, + pub website: Option<String>, + pub picture: Option<String>, + pub banner: Option<String>, + pub nip05: Option<String>, + pub lud06: Option<String>, + pub lud16: Option<String>, +} +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct INostrProfileFields { + pub public_key: String, + pub profile_type: String, + pub name: String, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub display_name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub about: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub website: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub picture: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub banner: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub nip05: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub lud06: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub lud16: Option<String>, +} +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct INostrProfileFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub public_key: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub profile_type: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub name: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub display_name: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub about: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub website: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub picture: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub banner: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub nip05: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub lud06: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub lud16: Option<serde_json::Value>, +} +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct INostrProfileFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub public_key: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub profile_type: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub display_name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub about: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub website: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub picture: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub banner: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub nip05: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub lud06: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub lud16: Option<String>, +} +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum NostrProfileQueryBindValues { + Id { id: String }, + PublicKey { public_key: String }, +} +impl NostrProfileQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::PublicKey { public_key } => ("public_key", Value::from(public_key.clone())), + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::PublicKey { public_key } => public_key.clone(), + } + } +} +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct NostrProfileRelayArgs { + pub id: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum NostrProfileFindManyRel { + #[serde(rename = "on_relay")] + OnRelay(NostrProfileRelayArgs), + #[serde(rename = "off_relay")] + OffRelay(NostrProfileRelayArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrProfileCreate", + type = "INostrProfileFields" + ) +)] +pub struct INostrProfileCreateTs; +pub type INostrProfileCreate = INostrProfileFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrProfileCreateResolve", + type = "IResult<NostrProfile>" + ) +)] +pub struct INostrProfileCreateResolveTs; +pub type INostrProfileCreateResolve = IResult<NostrProfile>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct INostrProfileFindOneArgs { + pub on: NostrProfileQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct INostrProfileFindOneRelArgs { + pub rel: NostrProfileFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "INostrProfileFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum INostrProfileFindOne { + On(INostrProfileFindOneArgs), + Rel(INostrProfileFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrProfileFindOneResolve", + type = "IResult<NostrProfile>" + ) +)] +pub struct INostrProfileFindOneResolveTs; +pub type INostrProfileFindOneResolve = IResult<Option<NostrProfile>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "INostrProfileFindMany") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum INostrProfileFindMany { + Filter { + filter: Option<INostrProfileFieldsFilter>, + }, + Rel { + rel: NostrProfileFindManyRel, + }, +} +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrProfileFindManyResolve", + type = "IResultList<NostrProfile>" + ) +)] +pub struct INostrProfileFindManyResolveTs; +pub type INostrProfileFindManyResolve = IResultList<NostrProfile>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrProfileDelete", + type = "INostrProfileFindOne" + ) +)] +pub struct INostrProfileDeleteTs; +pub type INostrProfileDelete = INostrProfileFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrProfileDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct INostrProfileDeleteResolveTs; +pub type INostrProfileDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "INostrProfileUpdate") +)] +#[derive(Deserialize, Serialize)] +pub struct INostrProfileUpdateArgs { + pub on: NostrProfileQueryBindValues, + pub fields: INostrProfileFieldsPartial, +} +pub type INostrProfileUpdate = INostrProfileUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "INostrProfileUpdateResolve", + type = "IResult<NostrProfile>" + ) +)] +pub struct INostrProfileUpdateResolveTs; +pub type INostrProfileUpdateResolve = IResult<NostrProfile>; diff --git a/tangle-schema/src/models/nostr_profile_relay.rs b/tangle-db-schema/src/models/nostr_profile_relay.rs diff --git a/tangle-schema/src/models/nostr_relay.rs b/tangle-db-schema/src/models/nostr_relay.rs diff --git a/tangle-db-schema/src/models/plot.rs b/tangle-db-schema/src/models/plot.rs @@ -0,0 +1,261 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct Plot { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub d_tag: String, + pub farm_id: String, + pub name: String, + pub about: Option<String>, + pub location_primary: Option<String>, + pub location_city: Option<String>, + pub location_region: Option<String>, + pub location_country: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IPlotFields { + pub d_tag: String, + pub farm_id: String, + pub name: String, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub about: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_primary: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_city: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_region: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_country: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IPlotFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub d_tag: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub farm_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub name: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub about: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_primary: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_city: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_region: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub location_country: Option<serde_json::Value>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IPlotFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub d_tag: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub farm_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub name: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub about: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub location_primary: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub location_city: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub location_region: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub location_country: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum PlotQueryBindValues { + Id { id: String }, + DTag { d_tag: String }, + FarmId { farm_id: String }, +} +impl PlotQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::DTag { d_tag } => ("d_tag", Value::from(d_tag.clone())), + Self::FarmId { farm_id } => ("farm_id", Value::from(farm_id.clone())), + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::DTag { d_tag } => d_tag.clone(), + Self::FarmId { farm_id } => farm_id.clone(), + } + } +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum PlotFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotCreate", + type = "IPlotFields" + ) +)] +pub struct IPlotCreateTs; +pub type IPlotCreate = IPlotFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotCreateResolve", + type = "IResult<Plot>" + ) +)] +pub struct IPlotCreateResolveTs; +pub type IPlotCreateResolve = IResult<Plot>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IPlotFindOneArgs { + pub on: PlotQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IPlotFindOneRelArgs { + pub rel: PlotFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IPlotFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum IPlotFindOne { + On(IPlotFindOneArgs), + Rel(IPlotFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotFindOneResolve", + type = "IResult<Plot>" + ) +)] +pub struct IPlotFindOneResolveTs; +pub type IPlotFindOneResolve = IResult<Option<Plot>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IPlotFindMany") +)] +#[derive(Deserialize, Serialize)] +pub struct IPlotFindManyArgs { + pub filter: Option<IPlotFieldsFilter>, +} +pub type IPlotFindMany = IPlotFindManyArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotFindManyResolve", + type = "IResultList<Plot>" + ) +)] +pub struct IPlotFindManyResolveTs; +pub type IPlotFindManyResolve = IResultList<Plot>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotDelete", + type = "IPlotFindOne" + ) +)] +pub struct IPlotDeleteTs; +pub type IPlotDelete = IPlotFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct IPlotDeleteResolveTs; +pub type IPlotDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts", rename = "IPlotUpdate"))] +#[derive(Deserialize, Serialize)] +pub struct IPlotUpdateArgs { + pub on: PlotQueryBindValues, + pub fields: IPlotFieldsPartial, +} +pub type IPlotUpdate = IPlotUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotUpdateResolve", + type = "IResult<Plot>" + ) +)] +pub struct IPlotUpdateResolveTs; +pub type IPlotUpdateResolve = IResult<Plot>; diff --git a/tangle-db-schema/src/models/plot_gcs_location.rs b/tangle-db-schema/src/models/plot_gcs_location.rs @@ -0,0 +1,228 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct PlotGcsLocation { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub plot_id: String, + pub gcs_location_id: String, + pub role: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IPlotGcsLocationFields { + pub plot_id: String, + pub gcs_location_id: String, + pub role: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IPlotGcsLocationFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub plot_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub gcs_location_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub role: Option<serde_json::Value>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IPlotGcsLocationFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub plot_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub gcs_location_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub role: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum PlotGcsLocationQueryBindValues { + Id { id: String }, + PlotId { plot_id: String }, + GcsLocationId { gcs_location_id: String }, +} +impl PlotGcsLocationQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::PlotId { plot_id } => ("plot_id", Value::from(plot_id.clone())), + Self::GcsLocationId { gcs_location_id } => { + ("gcs_location_id", Value::from(gcs_location_id.clone())) + } + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::PlotId { plot_id } => plot_id.clone(), + Self::GcsLocationId { gcs_location_id } => gcs_location_id.clone(), + } + } +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum PlotGcsLocationFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotGcsLocationCreate", + type = "IPlotGcsLocationFields" + ) +)] +pub struct IPlotGcsLocationCreateTs; +pub type IPlotGcsLocationCreate = IPlotGcsLocationFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotGcsLocationCreateResolve", + type = "IResult<PlotGcsLocation>" + ) +)] +pub struct IPlotGcsLocationCreateResolveTs; +pub type IPlotGcsLocationCreateResolve = IResult<PlotGcsLocation>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IPlotGcsLocationFindOneArgs { + pub on: PlotGcsLocationQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IPlotGcsLocationFindOneRelArgs { + pub rel: PlotGcsLocationFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IPlotGcsLocationFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum IPlotGcsLocationFindOne { + On(IPlotGcsLocationFindOneArgs), + Rel(IPlotGcsLocationFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotGcsLocationFindOneResolve", + type = "IResult<PlotGcsLocation>" + ) +)] +pub struct IPlotGcsLocationFindOneResolveTs; +pub type IPlotGcsLocationFindOneResolve = IResult<Option<PlotGcsLocation>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IPlotGcsLocationFindMany") +)] +#[derive(Deserialize, Serialize)] +pub struct IPlotGcsLocationFindManyArgs { + pub filter: Option<IPlotGcsLocationFieldsFilter>, +} +pub type IPlotGcsLocationFindMany = IPlotGcsLocationFindManyArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotGcsLocationFindManyResolve", + type = "IResultList<PlotGcsLocation>" + ) +)] +pub struct IPlotGcsLocationFindManyResolveTs; +pub type IPlotGcsLocationFindManyResolve = IResultList<PlotGcsLocation>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotGcsLocationDelete", + type = "IPlotGcsLocationFindOne" + ) +)] +pub struct IPlotGcsLocationDeleteTs; +pub type IPlotGcsLocationDelete = IPlotGcsLocationFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotGcsLocationDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct IPlotGcsLocationDeleteResolveTs; +pub type IPlotGcsLocationDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts", rename = "IPlotGcsLocationUpdate"))] +#[derive(Deserialize, Serialize)] +pub struct IPlotGcsLocationUpdateArgs { + pub on: PlotGcsLocationQueryBindValues, + pub fields: IPlotGcsLocationFieldsPartial, +} +pub type IPlotGcsLocationUpdate = IPlotGcsLocationUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotGcsLocationUpdateResolve", + type = "IResult<PlotGcsLocation>" + ) +)] +pub struct IPlotGcsLocationUpdateResolveTs; +pub type IPlotGcsLocationUpdateResolve = IResult<PlotGcsLocation>; diff --git a/tangle-db-schema/src/models/plot_tag.rs b/tangle-db-schema/src/models/plot_tag.rs @@ -0,0 +1,220 @@ +use radroots_types::types::{IResult, IResultList}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Serialize, Deserialize)] +pub struct PlotTag { + pub id: String, + pub created_at: String, + pub updated_at: String, + pub plot_id: String, + pub tag: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IPlotTagFields { + pub plot_id: String, + pub tag: String, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IPlotTagFieldsPartial { + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub plot_id: Option<serde_json::Value>, + #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] + pub tag: Option<serde_json::Value>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct IPlotTagFieldsFilter { + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub created_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub updated_at: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub plot_id: Option<String>, + #[cfg_attr(feature = "ts-rs", ts(optional))] + pub tag: Option<String>, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum PlotTagQueryBindValues { + Id { id: String }, + PlotId { plot_id: String }, + Tag { tag: String }, +} +impl PlotTagQueryBindValues { + pub fn to_filter_param(&self) -> (&'static str, Value) { + match self { + Self::Id { id } => ("id", Value::from(id.clone())), + Self::PlotId { plot_id } => ("plot_id", Value::from(plot_id.clone())), + Self::Tag { tag } => ("tag", Value::from(tag.clone())), + } + } + + pub fn primary_key(&self) -> Option<String> { + match self { + Self::Id { id } => Some(id.clone()), + _ => None, + } + } + + pub fn lookup_key(&self) -> String { + match self { + Self::Id { id } => id.clone(), + Self::PlotId { plot_id } => plot_id.clone(), + Self::Tag { tag } => tag.clone(), + } + } +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum PlotTagFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotTagCreate", + type = "IPlotTagFields" + ) +)] +pub struct IPlotTagCreateTs; +pub type IPlotTagCreate = IPlotTagFields; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotTagCreateResolve", + type = "IResult<PlotTag>" + ) +)] +pub struct IPlotTagCreateResolveTs; +pub type IPlotTagCreateResolve = IResult<PlotTag>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IPlotTagFindOneArgs { + pub on: PlotTagQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IPlotTagFindOneRelArgs { + pub rel: PlotTagFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IPlotTagFindOne") +)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum IPlotTagFindOne { + On(IPlotTagFindOneArgs), + Rel(IPlotTagFindOneRelArgs), +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotTagFindOneResolve", + type = "IResult<PlotTag>" + ) +)] +pub struct IPlotTagFindOneResolveTs; +pub type IPlotTagFindOneResolve = IResult<Option<PlotTag>>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts(export, export_to = "types.ts", rename = "IPlotTagFindMany") +)] +#[derive(Deserialize, Serialize)] +pub struct IPlotTagFindManyArgs { + pub filter: Option<IPlotTagFieldsFilter>, +} +pub type IPlotTagFindMany = IPlotTagFindManyArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotTagFindManyResolve", + type = "IResultList<PlotTag>" + ) +)] +pub struct IPlotTagFindManyResolveTs; +pub type IPlotTagFindManyResolve = IResultList<PlotTag>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotTagDelete", + type = "IPlotTagFindOne" + ) +)] +pub struct IPlotTagDeleteTs; +pub type IPlotTagDelete = IPlotTagFindOne; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotTagDeleteResolve", + type = "IResult<string>" + ) +)] +pub struct IPlotTagDeleteResolveTs; +pub type IPlotTagDeleteResolve = IResult<String>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts", rename = "IPlotTagUpdate"))] +#[derive(Deserialize, Serialize)] +pub struct IPlotTagUpdateArgs { + pub on: PlotTagQueryBindValues, + pub fields: IPlotTagFieldsPartial, +} +pub type IPlotTagUpdate = IPlotTagUpdateArgs; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "IPlotTagUpdateResolve", + type = "IResult<PlotTag>" + ) +)] +pub struct IPlotTagUpdateResolveTs; +pub type IPlotTagUpdateResolve = IResult<PlotTag>; diff --git a/tangle-schema/src/models/trade_product.rs b/tangle-db-schema/src/models/trade_product.rs diff --git a/tangle-db-schema/src/models/trade_product_location.rs b/tangle-db-schema/src/models/trade_product_location.rs @@ -0,0 +1,27 @@ +use radroots_types::types::IResultPass; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "ts-rs")] +use ts_rs::TS; +use crate::trade_product::TradeProductQueryBindValues; +use crate::gcs_location::GcsLocationQueryBindValues; + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub struct ITradeProductLocationRelation { + pub trade_product: TradeProductQueryBindValues, + pub gcs_location: GcsLocationQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr( + feature = "ts-rs", + ts( + export, + export_to = "types.ts", + rename = "ITradeProductLocationResolve", + type = "IResultPass" + ) +)] +pub struct ITradeProductLocationResolveTs; +pub type ITradeProductLocationResolve = IResultPass; diff --git a/tangle-schema/src/models/trade_product_media.rs b/tangle-db-schema/src/models/trade_product_media.rs diff --git a/tangle-db-wasm/Cargo.toml b/tangle-db-wasm/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "radroots-tangle-db-wasm" +version.workspace = true +edition.workspace = true +authors = ["Radroots Authors"] +rust-version.workspace = true +license.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +radroots-sql-core = { workspace = true, features = ["web"] } +radroots-sql-wasm-core = { workspace = true } +radroots-tangle-db = { workspace = true } +radroots-tangle-db-schema = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +serde-wasm-bindgen = { workspace = true } +wasm-bindgen = { workspace = true } diff --git a/tangle-db-wasm/pkg/package.json b/tangle-db-wasm/pkg/package.json @@ -0,0 +1,19 @@ +{ + "name": "@radroots/tangle-db-wasm", + "version": "0.1.0", + "private": true, + "type": "module", + "files": [ + "dist" + ], + "main": "./dist/radroots_tangle_db_wasm.js", + "types": "./dist/radroots_tangle_db_wasm.d.ts", + "exports": { + ".": { + "types": "./dist/radroots_tangle_db_wasm.d.ts", + "import": "./dist/radroots_tangle_db_wasm.js", + "default": "./dist/radroots_tangle_db_wasm.js" + } + }, + "sideEffects": false +} +\ No newline at end of file diff --git a/tangle-db-wasm/src/lib.rs b/tangle-db-wasm/src/lib.rs @@ -0,0 +1,895 @@ +#![cfg(target_arch = "wasm32")] + +use radroots_sql_core::WasmSqlExecutor; +use radroots_sql_wasm_core::{err_js, parse_json}; +use radroots_tangle_db::migrations; +use wasm_bindgen::prelude::*; + +use radroots_tangle_db_schema::farm::{ + IFarmCreate, + IFarmDelete, + IFarmFindMany, + IFarmFindOne, + IFarmUpdate, +}; + +use radroots_tangle_db_schema::farm_gcs_location::{ + IFarmGcsLocationCreate, + IFarmGcsLocationDelete, + IFarmGcsLocationFindMany, + IFarmGcsLocationFindOne, + IFarmGcsLocationUpdate, +}; + +use radroots_tangle_db_schema::farm_member::{ + IFarmMemberCreate, + IFarmMemberDelete, + IFarmMemberFindMany, + IFarmMemberFindOne, + IFarmMemberUpdate, +}; + +use radroots_tangle_db_schema::farm_member_claim::{ + IFarmMemberClaimCreate, + IFarmMemberClaimDelete, + IFarmMemberClaimFindMany, + IFarmMemberClaimFindOne, + IFarmMemberClaimUpdate, +}; + +use radroots_tangle_db_schema::farm_tag::{ + IFarmTagCreate, + IFarmTagDelete, + IFarmTagFindMany, + IFarmTagFindOne, + IFarmTagUpdate, +}; + +use radroots_tangle_db_schema::gcs_location::{ + IGcsLocationCreate, + IGcsLocationDelete, + IGcsLocationFindMany, + IGcsLocationFindOne, + IGcsLocationUpdate, +}; + +use radroots_tangle_db_schema::log_error::{ + ILogErrorCreate, + ILogErrorDelete, + ILogErrorFindMany, + ILogErrorFindOne, + ILogErrorUpdate, +}; + +use radroots_tangle_db_schema::media_image::{ + IMediaImageCreate, + IMediaImageDelete, + IMediaImageFindMany, + IMediaImageFindOne, + IMediaImageUpdate, +}; + +use radroots_tangle_db_schema::nostr_profile::{ + INostrProfileCreate, + INostrProfileDelete, + INostrProfileFindMany, + INostrProfileFindOne, + INostrProfileUpdate, +}; + +use radroots_tangle_db_schema::nostr_event_state::{ + INostrEventStateCreate, + INostrEventStateDelete, + INostrEventStateFindMany, + INostrEventStateFindOne, + INostrEventStateUpdate, +}; + +use radroots_tangle_db_schema::nostr_relay::{ + INostrRelayCreate, + INostrRelayDelete, + INostrRelayFindMany, + INostrRelayFindOne, + INostrRelayUpdate, +}; + +use radroots_tangle_db_schema::trade_product::{ + ITradeProductCreate, + ITradeProductDelete, + ITradeProductFindMany, + ITradeProductFindOne, + ITradeProductUpdate, +}; + +use radroots_tangle_db_schema::plot::{ + IPlotCreate, + IPlotDelete, + IPlotFindMany, + IPlotFindOne, + IPlotUpdate, +}; + +use radroots_tangle_db_schema::plot_gcs_location::{ + IPlotGcsLocationCreate, + IPlotGcsLocationDelete, + IPlotGcsLocationFindMany, + IPlotGcsLocationFindOne, + IPlotGcsLocationUpdate, +}; + +use radroots_tangle_db_schema::plot_tag::{ + IPlotTagCreate, + IPlotTagDelete, + IPlotTagFindMany, + IPlotTagFindOne, + IPlotTagUpdate, +}; + +use radroots_tangle_db_schema::nostr_profile_relay::{ + INostrProfileRelayRelation, +}; + +use radroots_tangle_db_schema::trade_product_location::{ + ITradeProductLocationRelation, +}; + +use radroots_tangle_db_schema::trade_product_media::{ + ITradeProductMediaRelation, +}; + +pub mod utils; +pub use utils::*; + +#[wasm_bindgen(js_name = tangle_db_run_migrations)] +pub fn tangle_db_run_migrations() -> Result<(), JsValue> { + let exec = WasmSqlExecutor::new(); + migrations::run_all_up(&exec).map_err(err_js) +} + +#[wasm_bindgen(js_name = tangle_db_reset_database)] +pub fn tangle_db_reset_database() -> Result<(), JsValue> { + let exec = WasmSqlExecutor::new(); + migrations::run_all_down(&exec).map_err(err_js) +} + +#[wasm_bindgen(js_name = tangle_db_export_backup)] +pub fn tangle_db_export_backup() -> Result<JsValue, JsValue> { + let exec = WasmSqlExecutor::new(); + let dump = radroots_tangle_db::backup::export_database_backup(&exec).map_err(err_js)?; + value_to_js(dump) +} + +#[wasm_bindgen(js_name = tangle_db_import_backup)] +pub fn tangle_db_import_backup(dump_json: &str) -> Result<(), JsValue> { + let exec = WasmSqlExecutor::new(); + radroots_tangle_db::backup::restore_database_backup_json(&exec, dump_json).map_err(err_js) +} + +#[wasm_bindgen(js_name = tangle_db_farm_create)] +pub fn tangle_db_farm_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_find_one)] +pub fn tangle_db_farm_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_find_many)] +pub fn tangle_db_farm_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_update)] +pub fn tangle_db_farm_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_delete)] +pub fn tangle_db_farm_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_create)] +pub fn tangle_db_plot_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_find_one)] +pub fn tangle_db_plot_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_find_many)] +pub fn tangle_db_plot_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_update)] +pub fn tangle_db_plot_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_delete)] +pub fn tangle_db_plot_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_gcs_location_create)] +pub fn tangle_db_gcs_location_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IGcsLocationCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::gcs_location::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_gcs_location_find_one)] +pub fn tangle_db_gcs_location_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IGcsLocationFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::gcs_location::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_gcs_location_find_many)] +pub fn tangle_db_gcs_location_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IGcsLocationFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::gcs_location::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_gcs_location_update)] +pub fn tangle_db_gcs_location_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IGcsLocationUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::gcs_location::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_gcs_location_delete)] +pub fn tangle_db_gcs_location_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IGcsLocationDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::gcs_location::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_gcs_location_create)] +pub fn tangle_db_farm_gcs_location_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmGcsLocationCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_gcs_location::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_gcs_location_find_one)] +pub fn tangle_db_farm_gcs_location_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmGcsLocationFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_gcs_location::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_gcs_location_find_many)] +pub fn tangle_db_farm_gcs_location_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmGcsLocationFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_gcs_location::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_gcs_location_update)] +pub fn tangle_db_farm_gcs_location_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmGcsLocationUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_gcs_location::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_gcs_location_delete)] +pub fn tangle_db_farm_gcs_location_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmGcsLocationDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_gcs_location::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_gcs_location_create)] +pub fn tangle_db_plot_gcs_location_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotGcsLocationCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot_gcs_location::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_gcs_location_find_one)] +pub fn tangle_db_plot_gcs_location_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotGcsLocationFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot_gcs_location::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_gcs_location_find_many)] +pub fn tangle_db_plot_gcs_location_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotGcsLocationFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot_gcs_location::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_gcs_location_update)] +pub fn tangle_db_plot_gcs_location_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotGcsLocationUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot_gcs_location::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_gcs_location_delete)] +pub fn tangle_db_plot_gcs_location_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotGcsLocationDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot_gcs_location::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_tag_create)] +pub fn tangle_db_farm_tag_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmTagCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_tag::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_tag_find_one)] +pub fn tangle_db_farm_tag_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmTagFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_tag::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_tag_find_many)] +pub fn tangle_db_farm_tag_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmTagFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_tag::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_tag_update)] +pub fn tangle_db_farm_tag_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmTagUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_tag::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_tag_delete)] +pub fn tangle_db_farm_tag_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmTagDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_tag::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_tag_create)] +pub fn tangle_db_plot_tag_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotTagCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot_tag::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_tag_find_one)] +pub fn tangle_db_plot_tag_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotTagFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot_tag::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_tag_find_many)] +pub fn tangle_db_plot_tag_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotTagFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot_tag::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_tag_update)] +pub fn tangle_db_plot_tag_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotTagUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot_tag::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_plot_tag_delete)] +pub fn tangle_db_plot_tag_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IPlotTagDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::plot_tag::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_member_create)] +pub fn tangle_db_farm_member_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmMemberCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_member::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_member_find_one)] +pub fn tangle_db_farm_member_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmMemberFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_member::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_member_find_many)] +pub fn tangle_db_farm_member_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmMemberFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_member::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_member_update)] +pub fn tangle_db_farm_member_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmMemberUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_member::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_member_delete)] +pub fn tangle_db_farm_member_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmMemberDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_member::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_member_claim_create)] +pub fn tangle_db_farm_member_claim_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmMemberClaimCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_member_claim::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_member_claim_find_one)] +pub fn tangle_db_farm_member_claim_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmMemberClaimFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_member_claim::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_member_claim_find_many)] +pub fn tangle_db_farm_member_claim_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmMemberClaimFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_member_claim::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_member_claim_update)] +pub fn tangle_db_farm_member_claim_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmMemberClaimUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_member_claim::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_farm_member_claim_delete)] +pub fn tangle_db_farm_member_claim_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IFarmMemberClaimDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::farm_member_claim::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_log_error_create)] +pub fn tangle_db_log_error_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ILogErrorCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::log_error::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_log_error_find_one)] +pub fn tangle_db_log_error_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ILogErrorFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::log_error::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_log_error_find_many)] +pub fn tangle_db_log_error_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ILogErrorFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::log_error::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_log_error_update)] +pub fn tangle_db_log_error_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ILogErrorUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::log_error::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_log_error_delete)] +pub fn tangle_db_log_error_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ILogErrorDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::log_error::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_media_image_create)] +pub fn tangle_db_media_image_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IMediaImageCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::media_image::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_media_image_find_one)] +pub fn tangle_db_media_image_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IMediaImageFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::media_image::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_media_image_find_many)] +pub fn tangle_db_media_image_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IMediaImageFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::media_image::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_media_image_update)] +pub fn tangle_db_media_image_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IMediaImageUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::media_image::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_media_image_delete)] +pub fn tangle_db_media_image_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: IMediaImageDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::media_image::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_profile_create)] +pub fn tangle_db_nostr_profile_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrProfileCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_profile::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_profile_find_one)] +pub fn tangle_db_nostr_profile_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrProfileFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_profile::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_profile_find_many)] +pub fn tangle_db_nostr_profile_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrProfileFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_profile::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_profile_update)] +pub fn tangle_db_nostr_profile_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrProfileUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_profile::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_profile_delete)] +pub fn tangle_db_nostr_profile_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrProfileDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_profile::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_event_state_create)] +pub fn tangle_db_nostr_event_state_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrEventStateCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_event_state::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_event_state_find_one)] +pub fn tangle_db_nostr_event_state_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrEventStateFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_event_state::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_event_state_find_many)] +pub fn tangle_db_nostr_event_state_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrEventStateFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_event_state::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_event_state_update)] +pub fn tangle_db_nostr_event_state_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrEventStateUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_event_state::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_event_state_delete)] +pub fn tangle_db_nostr_event_state_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrEventStateDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_event_state::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_relay_create)] +pub fn tangle_db_nostr_relay_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrRelayCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_relay::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_relay_find_one)] +pub fn tangle_db_nostr_relay_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrRelayFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_relay::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_relay_find_many)] +pub fn tangle_db_nostr_relay_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrRelayFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_relay::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_relay_update)] +pub fn tangle_db_nostr_relay_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrRelayUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_relay::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_relay_delete)] +pub fn tangle_db_nostr_relay_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrRelayDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_relay::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_trade_product_create)] +pub fn tangle_db_trade_product_create(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ITradeProductCreate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::trade_product::create(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_trade_product_find_one)] +pub fn tangle_db_trade_product_find_one(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ITradeProductFindOne = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::trade_product::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_trade_product_find_many)] +pub fn tangle_db_trade_product_find_many(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ITradeProductFindMany = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::trade_product::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_trade_product_update)] +pub fn tangle_db_trade_product_update(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ITradeProductUpdate = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::trade_product::update(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_trade_product_delete)] +pub fn tangle_db_trade_product_delete(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ITradeProductDelete = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::trade_product::delete(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_profile_relay_set)] +pub fn tangle_db_nostr_profile_relay_set(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrProfileRelayRelation = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_profile_relay::set(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_nostr_profile_relay_unset)] +pub fn tangle_db_nostr_profile_relay_unset(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: INostrProfileRelayRelation = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::nostr_profile_relay::unset(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_trade_product_location_set)] +pub fn tangle_db_trade_product_location_set(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ITradeProductLocationRelation = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::trade_product_location::set(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_trade_product_location_unset)] +pub fn tangle_db_trade_product_location_unset(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ITradeProductLocationRelation = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::trade_product_location::unset(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_trade_product_media_set)] +pub fn tangle_db_trade_product_media_set(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ITradeProductMediaRelation = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::trade_product_media::set(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} + +#[wasm_bindgen(js_name = tangle_db_trade_product_media_unset)] +pub fn tangle_db_trade_product_media_unset(opts_json: &str) -> Result<JsValue, JsValue> { + let opts: ITradeProductMediaRelation = parse_json(opts_json).map_err(err_js)?; + let exec = WasmSqlExecutor::new(); + let out = + radroots_tangle_db::trade_product_media::unset(&exec, &opts).map_err(|e| err_js(e.err))?; + value_to_js(out) +} diff --git a/tangle-sql-wasm/src/utils.rs b/tangle-db-wasm/src/utils.rs diff --git a/tangle-db/Cargo.toml b/tangle-db/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "radroots-tangle-db" +version.workspace = true +edition.workspace = true +authors = ["Radroots Authors"] +rust-version.workspace = true +license.workspace = true + +[lib] +crate-type = ["rlib"] + +[features] +default = [] +web = ["radroots-sql-core/web"] +native = ["radroots-sql-core/native"] +embedded = ["radroots-sql-core/embedded"] + +[dependencies] +radroots-sql-core = { workspace = true } +radroots-tangle-db-schema = { workspace = true } +radroots-types = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } diff --git a/tangle-sql/migrations/0000_init.down.sql b/tangle-db/migrations/0000_init.down.sql diff --git a/tangle-sql/migrations/0000_init.up.sql b/tangle-db/migrations/0000_init.up.sql diff --git a/tangle-sql/migrations/0001_log_error.down.sql b/tangle-db/migrations/0001_log_error.down.sql diff --git a/tangle-sql/migrations/0001_log_error.up.sql b/tangle-db/migrations/0001_log_error.up.sql diff --git a/tangle-sql/migrations/0002_farm.down.sql b/tangle-db/migrations/0002_farm.down.sql diff --git a/tangle-db/migrations/0002_farm.up.sql b/tangle-db/migrations/0002_farm.up.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS farm ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + d_tag TEXT NOT NULL, + pubkey TEXT NOT NULL, + name TEXT NOT NULL, + about TEXT, + website TEXT, + picture TEXT, + banner TEXT, + location_primary TEXT, + location_city TEXT, + location_region TEXT, + location_country TEXT +); + +CREATE UNIQUE INDEX IF NOT EXISTS farm_pubkey_d_tag_idx ON farm(pubkey, d_tag); diff --git a/tangle-db/migrations/0003_gcs_location.down.sql b/tangle-db/migrations/0003_gcs_location.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS gcs_location; diff --git a/tangle-db/migrations/0003_gcs_location.up.sql b/tangle-db/migrations/0003_gcs_location.up.sql @@ -0,0 +1,27 @@ +CREATE TABLE IF NOT EXISTS gcs_location ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + d_tag TEXT NOT NULL, + lat REAL NOT NULL, + lng REAL NOT NULL, + geohash TEXT NOT NULL, + point TEXT NOT NULL, + polygon TEXT NOT NULL, + accuracy REAL, + altitude REAL, + tag_0 TEXT, + label TEXT, + area REAL, + elevation INTEGER, + soil TEXT, + climate TEXT, + gc_id TEXT, + gc_name TEXT, + gc_admin1_id TEXT, + gc_admin1_name TEXT, + gc_country_id TEXT, + gc_country_name TEXT +); + +CREATE INDEX IF NOT EXISTS gcs_location_geohash_idx ON gcs_location(geohash); diff --git a/tangle-sql/migrations/0004_trade_product.down.sql b/tangle-db/migrations/0004_trade_product.down.sql diff --git a/tangle-sql/migrations/0004_trade_product.up.sql b/tangle-db/migrations/0004_trade_product.up.sql diff --git a/tangle-sql/migrations/0005_nostr_profile.down.sql b/tangle-db/migrations/0005_nostr_profile.down.sql diff --git a/tangle-db/migrations/0005_nostr_profile.up.sql b/tangle-db/migrations/0005_nostr_profile.up.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS nostr_profile ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + public_key CHAR(64) NOT NULL CHECK(length(public_key) = 64), + profile_type TEXT NOT NULL, + name TEXT NOT NULL, + display_name TEXT, + about TEXT, + website TEXT, + picture TEXT, + banner TEXT, + nip05 TEXT, + lud06 TEXT, + lud16 TEXT +); diff --git a/tangle-sql/migrations/0006_nostr_relay.down.sql b/tangle-db/migrations/0006_nostr_relay.down.sql diff --git a/tangle-sql/migrations/0006_nostr_relay.up.sql b/tangle-db/migrations/0006_nostr_relay.up.sql diff --git a/tangle-sql/migrations/0007_media_image.down.sql b/tangle-db/migrations/0007_media_image.down.sql diff --git a/tangle-sql/migrations/0007_media_image.up.sql b/tangle-db/migrations/0007_media_image.up.sql diff --git a/tangle-db/migrations/0008_farm_gcs_location.down.sql b/tangle-db/migrations/0008_farm_gcs_location.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS farm_gcs_location; diff --git a/tangle-db/migrations/0008_farm_gcs_location.up.sql b/tangle-db/migrations/0008_farm_gcs_location.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS farm_gcs_location ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + farm_id CHAR(36) NOT NULL, + gcs_location_id CHAR(36) NOT NULL, + role TEXT NOT NULL, + FOREIGN KEY (farm_id) REFERENCES farm(id) ON DELETE CASCADE, + FOREIGN KEY (gcs_location_id) REFERENCES gcs_location(id) ON DELETE CASCADE, + UNIQUE (farm_id, gcs_location_id, role) +); diff --git a/tangle-sql/migrations/0009_nostr_profile_relay.down.sql b/tangle-db/migrations/0009_nostr_profile_relay.down.sql diff --git a/tangle-sql/migrations/0009_nostr_profile_relay.up.sql b/tangle-db/migrations/0009_nostr_profile_relay.up.sql diff --git a/tangle-sql/migrations/0010_trade_product_location.down.sql b/tangle-db/migrations/0010_trade_product_location.down.sql diff --git a/tangle-db/migrations/0010_trade_product_location.up.sql b/tangle-db/migrations/0010_trade_product_location.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS trade_product_location ( + tb_tp CHAR(36), + tb_gl CHAR(36), + FOREIGN KEY (tb_tp) REFERENCES trade_product(id) ON DELETE CASCADE, + FOREIGN KEY (tb_gl) REFERENCES gcs_location(id) ON DELETE CASCADE, + PRIMARY KEY (tb_tp, tb_gl) +); diff --git a/tangle-sql/migrations/0011_trade_product_media.down.sql b/tangle-db/migrations/0011_trade_product_media.down.sql diff --git a/tangle-sql/migrations/0011_trade_product_media.up.sql b/tangle-db/migrations/0011_trade_product_media.up.sql diff --git a/tangle-db/migrations/0012_plot.down.sql b/tangle-db/migrations/0012_plot.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS plot; diff --git a/tangle-db/migrations/0012_plot.up.sql b/tangle-db/migrations/0012_plot.up.sql @@ -0,0 +1,16 @@ +CREATE TABLE IF NOT EXISTS plot ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + d_tag TEXT NOT NULL, + farm_id CHAR(36) NOT NULL, + name TEXT NOT NULL, + about TEXT, + location_primary TEXT, + location_city TEXT, + location_region TEXT, + location_country TEXT, + FOREIGN KEY (farm_id) REFERENCES farm(id) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX IF NOT EXISTS plot_farm_d_tag_idx ON plot(farm_id, d_tag); diff --git a/tangle-db/migrations/0013_plot_gcs_location.down.sql b/tangle-db/migrations/0013_plot_gcs_location.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS plot_gcs_location; diff --git a/tangle-db/migrations/0013_plot_gcs_location.up.sql b/tangle-db/migrations/0013_plot_gcs_location.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS plot_gcs_location ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + plot_id CHAR(36) NOT NULL, + gcs_location_id CHAR(36) NOT NULL, + role TEXT NOT NULL, + FOREIGN KEY (plot_id) REFERENCES plot(id) ON DELETE CASCADE, + FOREIGN KEY (gcs_location_id) REFERENCES gcs_location(id) ON DELETE CASCADE, + UNIQUE (plot_id, gcs_location_id, role) +); diff --git a/tangle-db/migrations/0014_farm_tag.down.sql b/tangle-db/migrations/0014_farm_tag.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS farm_tag; diff --git a/tangle-db/migrations/0014_farm_tag.up.sql b/tangle-db/migrations/0014_farm_tag.up.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS farm_tag ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + farm_id CHAR(36) NOT NULL, + tag TEXT NOT NULL, + FOREIGN KEY (farm_id) REFERENCES farm(id) ON DELETE CASCADE, + UNIQUE (farm_id, tag) +); diff --git a/tangle-db/migrations/0015_plot_tag.down.sql b/tangle-db/migrations/0015_plot_tag.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS plot_tag; diff --git a/tangle-db/migrations/0015_plot_tag.up.sql b/tangle-db/migrations/0015_plot_tag.up.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS plot_tag ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + plot_id CHAR(36) NOT NULL, + tag TEXT NOT NULL, + FOREIGN KEY (plot_id) REFERENCES plot(id) ON DELETE CASCADE, + UNIQUE (plot_id, tag) +); diff --git a/tangle-db/migrations/0016_farm_member.down.sql b/tangle-db/migrations/0016_farm_member.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS farm_member; diff --git a/tangle-db/migrations/0016_farm_member.up.sql b/tangle-db/migrations/0016_farm_member.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS farm_member ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + farm_id CHAR(36) NOT NULL, + member_pubkey CHAR(64) NOT NULL CHECK(length(member_pubkey) = 64), + role TEXT NOT NULL, + FOREIGN KEY (farm_id) REFERENCES farm(id) ON DELETE CASCADE, + UNIQUE (farm_id, member_pubkey, role) +); diff --git a/tangle-db/migrations/0017_farm_member_claim.down.sql b/tangle-db/migrations/0017_farm_member_claim.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS farm_member_claim; diff --git a/tangle-db/migrations/0017_farm_member_claim.up.sql b/tangle-db/migrations/0017_farm_member_claim.up.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS farm_member_claim ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + member_pubkey CHAR(64) NOT NULL CHECK(length(member_pubkey) = 64), + farm_pubkey CHAR(64) NOT NULL CHECK(length(farm_pubkey) = 64), + UNIQUE (member_pubkey, farm_pubkey) +); diff --git a/tangle-db/migrations/0018_nostr_event_state.down.sql b/tangle-db/migrations/0018_nostr_event_state.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS nostr_event_state; diff --git a/tangle-db/migrations/0018_nostr_event_state.up.sql b/tangle-db/migrations/0018_nostr_event_state.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS nostr_event_state ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + key TEXT NOT NULL UNIQUE, + kind INTEGER NOT NULL, + pubkey CHAR(64) NOT NULL CHECK(length(pubkey) = 64), + d_tag TEXT NOT NULL, + last_event_id CHAR(64) NOT NULL CHECK(length(last_event_id) = 64), + last_created_at INTEGER NOT NULL, + content_hash TEXT NOT NULL +); + +CREATE INDEX IF NOT EXISTS nostr_event_state_kind_idx ON nostr_event_state(kind); diff --git a/tangle-db/src/backup.rs b/tangle-db/src/backup.rs @@ -0,0 +1,290 @@ +use radroots_sql_core::{SqlExecutor, error::SqlError, utils}; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; +use std::collections::{BTreeMap, HashMap}; + +pub const DATABASE_BACKUP_VERSION: &str = "1.0.0"; +pub const TANGLE_DB_VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SchemaEntry { + pub object_type: String, + pub name: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub table_name: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub sql: Option<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TableData { + pub name: String, + pub rows: Vec<Map<String, Value>>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MigrationBackup { + pub name: String, + pub up_sql: String, + pub down_sql: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DatabaseBackup { + pub format_version: String, + pub tangle_db_version: String, + pub schema: Vec<SchemaEntry>, + pub migrations: Vec<MigrationBackup>, + pub data: Vec<TableData>, +} + +pub fn export_database_backup<E: SqlExecutor>(executor: &E) -> Result<DatabaseBackup, SqlError> { + let schema = load_schema(executor)?; + let data = read_tables_for_backup(executor, &schema)?; + let migrations = crate::migrations::MIGRATIONS + .iter() + .map(|m| MigrationBackup { + name: m.name.to_string(), + up_sql: m.up_sql.to_string(), + down_sql: m.down_sql.to_string(), + }) + .collect(); + Ok(DatabaseBackup { + format_version: DATABASE_BACKUP_VERSION.to_string(), + tangle_db_version: TANGLE_DB_VERSION.to_string(), + schema, + migrations, + data, + }) +} + +pub fn export_database_backup_json<E: SqlExecutor>(executor: &E) -> Result<String, SqlError> { + let backup = export_database_backup(executor)?; + serde_json::to_string(&backup).map_err(SqlError::from) +} + +pub fn restore_database_backup<E: SqlExecutor>( + executor: &E, + backup: &DatabaseBackup, +) -> Result<(), SqlError> { + validate_backup_version(backup)?; + executor.exec("PRAGMA foreign_keys = OFF;", "[]")?; + executor.begin()?; + let result = (|| { + drop_existing_objects(executor)?; + create_schema_from_backup(executor, &backup.schema)?; + insert_rows_from_backup(executor, backup)?; + Ok(()) + })(); + + match result { + Ok(()) => { + executor.commit()?; + let _ = executor.exec("PRAGMA foreign_keys = ON;", "[]")?; + Ok(()) + } + Err(err) => { + let _ = executor.rollback(); + let _ = executor.exec("PRAGMA foreign_keys = ON;", "[]"); + Err(err) + } + } +} + +pub fn restore_database_backup_json<E: SqlExecutor>( + executor: &E, + backup_json: &str, +) -> Result<(), SqlError> { + let backup: DatabaseBackup = serde_json::from_str(backup_json).map_err(SqlError::from)?; + restore_database_backup(executor, &backup) +} + +fn drop_existing_objects<E: SqlExecutor>(executor: &E) -> Result<(), SqlError> { + #[derive(Deserialize)] + struct MasterRow { + #[serde(rename = "type")] + object_type: Option<String>, + name: Option<String>, + } + let json = executor.query_raw( + "select type, name from sqlite_master where name not like 'sqlite_%'", + "[]", + )?; + let rows: Vec<MasterRow> = utils::parse_json(&json)?; + + let mut groups: HashMap<String, Vec<String>> = HashMap::new(); + for row in rows.into_iter() { + let obj_type = row.object_type.unwrap_or_default(); + let name = match row.name { + Some(n) => n, + None => continue, + }; + groups.entry(obj_type).or_default().push(name); + } + + for object_type in ["trigger", "view", "index", "table"] { + if let Some(names) = groups.get(object_type) { + for name in names { + let stmt = match object_type { + "trigger" => format!("DROP TRIGGER IF EXISTS {};", escape_identifier(name)), + "view" => format!("DROP VIEW IF EXISTS {};", escape_identifier(name)), + "index" => format!("DROP INDEX IF EXISTS {};", escape_identifier(name)), + _ => format!("DROP TABLE IF EXISTS {};", escape_identifier(name)), + }; + let _ = executor.exec(&stmt, "[]")?; + } + } + } + Ok(()) +} + +fn create_schema_from_backup<E: SqlExecutor>( + executor: &E, + schema: &[SchemaEntry], +) -> Result<(), SqlError> { + for entry in schema.iter().filter(|s| s.object_type == "table") { + if let Some(sql) = &entry.sql { + executor.exec(sql, "[]")?; + } + } + for entry in schema + .iter() + .filter(|s| s.object_type != "table" && s.sql.is_some()) + { + if let Some(sql) = &entry.sql { + executor.exec(sql, "[]")?; + } + } + Ok(()) +} + +fn insert_rows_from_backup<E: SqlExecutor>( + executor: &E, + backup: &DatabaseBackup, +) -> Result<(), SqlError> { + let mut row_sources: HashMap<&str, &Vec<Map<String, Value>>> = HashMap::new(); + for table in &backup.data { + row_sources.insert(table.name.as_str(), &table.rows); + } + for entry in backup.schema.iter().filter(|s| s.object_type == "table") { + let rows = match row_sources.get(entry.name.as_str()) { + Some(r) => *r, + None => continue, + }; + for row in rows { + insert_row(executor, &entry.name, row)?; + } + } + Ok(()) +} + +fn insert_row<E: SqlExecutor>( + executor: &E, + table: &str, + row: &Map<String, Value>, +) -> Result<(), SqlError> { + if row.is_empty() { + return Ok(()); + } + + let mut cols: BTreeMap<String, &Value> = BTreeMap::new(); + for (k, v) in row { + cols.insert(k.clone(), v); + } + + let column_names: Vec<String> = cols.keys().cloned().collect(); + let placeholders = (0..column_names.len()) + .map(|_| "?") + .collect::<Vec<_>>() + .join(","); + let sql = format!( + "INSERT INTO {} ({}) VALUES ({});", + escape_identifier(table), + column_names + .iter() + .map(|c| escape_identifier(c)) + .collect::<Vec<_>>() + .join(","), + placeholders + ); + + let binds: Vec<Value> = cols.values().map(|v| utils::to_db_bind_value(*v)).collect(); + let params_json = serde_json::to_string(&binds).map_err(SqlError::from)?; + executor.exec(&sql, ¶ms_json)?; + Ok(()) +} + +fn load_schema<E: SqlExecutor>(executor: &E) -> Result<Vec<SchemaEntry>, SqlError> { + let json = executor.query_raw( + "select type, name, tbl_name as table_name, sql from sqlite_master where name not like 'sqlite_%' order by type, name", + "[]", + )?; + #[derive(Deserialize)] + struct RawSchema { + #[serde(rename = "type")] + object_type: Option<String>, + name: Option<String>, + table_name: Option<String>, + sql: Option<String>, + } + let rows: Vec<RawSchema> = utils::parse_json(&json)?; + Ok(rows + .into_iter() + .filter_map(|row| { + let name = row.name?; + let object_type = row.object_type.unwrap_or_default(); + Some(SchemaEntry { + object_type, + name, + table_name: row.table_name, + sql: row.sql, + }) + }) + .collect()) +} + +fn read_tables_for_backup<E: SqlExecutor>( + executor: &E, + schema: &[SchemaEntry], +) -> Result<Vec<TableData>, SqlError> { + let mut data = Vec::new(); + for entry in schema.iter().filter(|s| s.object_type == "table") { + let select_sql = format!("SELECT * FROM {};", escape_identifier(&entry.name)); + let json = executor.query_raw(&select_sql, "[]")?; + let rows: Vec<Map<String, Value>> = utils::parse_json(&json)?; + data.push(TableData { + name: entry.name.clone(), + rows, + }); + } + Ok(data) +} + +fn escape_identifier(name: &str) -> String { + let mut escaped = String::with_capacity(name.len() + 2); + escaped.push('"'); + for c in name.chars() { + if c == '"' { + escaped.push('"'); + } + escaped.push(c); + } + escaped.push('"'); + escaped +} + +fn validate_backup_version(backup: &DatabaseBackup) -> Result<(), SqlError> { + if backup.format_version != DATABASE_BACKUP_VERSION { + return Err(SqlError::InvalidArgument(format!( + "unsupported backup format {}, expected {}", + backup.format_version, DATABASE_BACKUP_VERSION + ))); + } + if backup.tangle_db_version != TANGLE_DB_VERSION { + return Err(SqlError::InvalidArgument(format!( + "unsupported tangle-db version {}, expected {}", + backup.tangle_db_version, TANGLE_DB_VERSION + ))); + } + Ok(()) +} diff --git a/tangle-db/src/lib.rs b/tangle-db/src/lib.rs @@ -0,0 +1,825 @@ +pub use radroots_sql_core::error::SqlError; +pub use radroots_sql_core::{ExecOutcome, SqlExecutor}; +use radroots_types::types::IError; + +use radroots_tangle_db_schema::farm::{ + IFarmCreate, + IFarmCreateResolve, + IFarmDelete, + IFarmDeleteResolve, + IFarmFindMany, + IFarmFindManyResolve, + IFarmFindOne, + IFarmFindOneResolve, + IFarmUpdate, + IFarmUpdateResolve, +}; + +use radroots_tangle_db_schema::farm_gcs_location::{ + IFarmGcsLocationCreate, + IFarmGcsLocationCreateResolve, + IFarmGcsLocationDelete, + IFarmGcsLocationDeleteResolve, + IFarmGcsLocationFindMany, + IFarmGcsLocationFindManyResolve, + IFarmGcsLocationFindOne, + IFarmGcsLocationFindOneResolve, + IFarmGcsLocationUpdate, + IFarmGcsLocationUpdateResolve, +}; + +use radroots_tangle_db_schema::farm_member::{ + IFarmMemberCreate, + IFarmMemberCreateResolve, + IFarmMemberDelete, + IFarmMemberDeleteResolve, + IFarmMemberFindMany, + IFarmMemberFindManyResolve, + IFarmMemberFindOne, + IFarmMemberFindOneResolve, + IFarmMemberUpdate, + IFarmMemberUpdateResolve, +}; + +use radroots_tangle_db_schema::farm_member_claim::{ + IFarmMemberClaimCreate, + IFarmMemberClaimCreateResolve, + IFarmMemberClaimDelete, + IFarmMemberClaimDeleteResolve, + IFarmMemberClaimFindMany, + IFarmMemberClaimFindManyResolve, + IFarmMemberClaimFindOne, + IFarmMemberClaimFindOneResolve, + IFarmMemberClaimUpdate, + IFarmMemberClaimUpdateResolve, +}; + +use radroots_tangle_db_schema::farm_tag::{ + IFarmTagCreate, + IFarmTagCreateResolve, + IFarmTagDelete, + IFarmTagDeleteResolve, + IFarmTagFindMany, + IFarmTagFindManyResolve, + IFarmTagFindOne, + IFarmTagFindOneResolve, + IFarmTagUpdate, + IFarmTagUpdateResolve, +}; + +use radroots_tangle_db_schema::gcs_location::{ + IGcsLocationCreate, + IGcsLocationCreateResolve, + IGcsLocationDelete, + IGcsLocationDeleteResolve, + IGcsLocationFindMany, + IGcsLocationFindManyResolve, + IGcsLocationFindOne, + IGcsLocationFindOneResolve, + IGcsLocationUpdate, + IGcsLocationUpdateResolve, +}; + +use radroots_tangle_db_schema::log_error::{ + ILogErrorCreate, + ILogErrorCreateResolve, + ILogErrorDelete, + ILogErrorDeleteResolve, + ILogErrorFindMany, + ILogErrorFindManyResolve, + ILogErrorFindOne, + ILogErrorFindOneResolve, + ILogErrorUpdate, + ILogErrorUpdateResolve, +}; + +use radroots_tangle_db_schema::media_image::{ + IMediaImageCreate, + IMediaImageCreateResolve, + IMediaImageDelete, + IMediaImageDeleteResolve, + IMediaImageFindMany, + IMediaImageFindManyResolve, + IMediaImageFindOne, + IMediaImageFindOneResolve, + IMediaImageUpdate, + IMediaImageUpdateResolve, +}; + +use radroots_tangle_db_schema::nostr_profile::{ + INostrProfileCreate, + INostrProfileCreateResolve, + INostrProfileDelete, + INostrProfileDeleteResolve, + INostrProfileFindMany, + INostrProfileFindManyResolve, + INostrProfileFindOne, + INostrProfileFindOneResolve, + INostrProfileUpdate, + INostrProfileUpdateResolve, +}; + +use radroots_tangle_db_schema::nostr_event_state::{ + INostrEventStateCreate, + INostrEventStateCreateResolve, + INostrEventStateDelete, + INostrEventStateDeleteResolve, + INostrEventStateFindMany, + INostrEventStateFindManyResolve, + INostrEventStateFindOne, + INostrEventStateFindOneResolve, + INostrEventStateUpdate, + INostrEventStateUpdateResolve, +}; + +use radroots_tangle_db_schema::nostr_relay::{ + INostrRelayCreate, + INostrRelayCreateResolve, + INostrRelayDelete, + INostrRelayDeleteResolve, + INostrRelayFindMany, + INostrRelayFindManyResolve, + INostrRelayFindOne, + INostrRelayFindOneResolve, + INostrRelayUpdate, + INostrRelayUpdateResolve, +}; + +use radroots_tangle_db_schema::trade_product::{ + ITradeProductCreate, + ITradeProductCreateResolve, + ITradeProductDelete, + ITradeProductDeleteResolve, + ITradeProductFindMany, + ITradeProductFindManyResolve, + ITradeProductFindOne, + ITradeProductFindOneResolve, + ITradeProductUpdate, + ITradeProductUpdateResolve, +}; + +use radroots_tangle_db_schema::plot::{ + IPlotCreate, + IPlotCreateResolve, + IPlotDelete, + IPlotDeleteResolve, + IPlotFindMany, + IPlotFindManyResolve, + IPlotFindOne, + IPlotFindOneResolve, + IPlotUpdate, + IPlotUpdateResolve, +}; + +use radroots_tangle_db_schema::plot_gcs_location::{ + IPlotGcsLocationCreate, + IPlotGcsLocationCreateResolve, + IPlotGcsLocationDelete, + IPlotGcsLocationDeleteResolve, + IPlotGcsLocationFindMany, + IPlotGcsLocationFindManyResolve, + IPlotGcsLocationFindOne, + IPlotGcsLocationFindOneResolve, + IPlotGcsLocationUpdate, + IPlotGcsLocationUpdateResolve, +}; + +use radroots_tangle_db_schema::plot_tag::{ + IPlotTagCreate, + IPlotTagCreateResolve, + IPlotTagDelete, + IPlotTagDeleteResolve, + IPlotTagFindMany, + IPlotTagFindManyResolve, + IPlotTagFindOne, + IPlotTagFindOneResolve, + IPlotTagUpdate, + IPlotTagUpdateResolve, +}; + +use radroots_tangle_db_schema::nostr_profile_relay::{ + INostrProfileRelayRelation, + INostrProfileRelayResolve, +}; + +use radroots_tangle_db_schema::trade_product_location::{ + ITradeProductLocationRelation, + ITradeProductLocationResolve, +}; + +use radroots_tangle_db_schema::trade_product_media::{ + ITradeProductMediaRelation, + ITradeProductMediaResolve, +}; + +pub mod backup; +pub mod migrations; +pub mod models; +pub use backup::{DatabaseBackup, MigrationBackup, SchemaEntry}; +pub use models::*; + +pub struct TangleSql<E: SqlExecutor> { + executor: E, +} + +impl<E: SqlExecutor> TangleSql<E> { + pub fn new(executor: E) -> Self { + Self { executor } + } + + pub fn executor(&self) -> &E { + &self.executor + } + + pub fn migrate_up(&self) -> Result<(), SqlError> { + crate::migrations::run_all_up(self.executor()) + } + + pub fn migrate_down(&self) -> Result<(), SqlError> { + crate::migrations::run_all_down(self.executor()) + } + + pub fn backup_database(&self) -> Result<DatabaseBackup, SqlError> { + crate::backup::export_database_backup(self.executor()) + } + + pub fn backup_database_json(&self) -> Result<String, SqlError> { + crate::backup::export_database_backup_json(self.executor()) + } + + pub fn restore_database(&self, backup: &DatabaseBackup) -> Result<(), SqlError> { + crate::backup::restore_database_backup(self.executor(), backup) + } + + pub fn restore_database_json(&self, backup_json: &str) -> Result<(), SqlError> { + crate::backup::restore_database_backup_json(self.executor(), backup_json) + } + + pub fn farm_create( + &self, + opts: &IFarmCreate, + ) -> Result<IFarmCreateResolve, IError<SqlError>> { + models::farm::create(self.executor(), opts) + } + + pub fn farm_find_many( + &self, + opts: &IFarmFindMany, + ) -> Result<IFarmFindManyResolve, IError<SqlError>> { + models::farm::find_many(self.executor(), opts) + } + + pub fn farm_find_one( + &self, + opts: &IFarmFindOne, + ) -> Result<IFarmFindOneResolve, IError<SqlError>> { + models::farm::find_one(self.executor(), opts) + } + + pub fn farm_update( + &self, + opts: &IFarmUpdate, + ) -> Result<IFarmUpdateResolve, IError<SqlError>> { + models::farm::update(self.executor(), opts) + } + + pub fn farm_delete( + &self, + opts: &IFarmDelete, + ) -> Result<IFarmDeleteResolve, IError<SqlError>> { + models::farm::delete(self.executor(), opts) + } + + pub fn plot_create( + &self, + opts: &IPlotCreate, + ) -> Result<IPlotCreateResolve, IError<SqlError>> { + models::plot::create(self.executor(), opts) + } + + pub fn plot_find_many( + &self, + opts: &IPlotFindMany, + ) -> Result<IPlotFindManyResolve, IError<SqlError>> { + models::plot::find_many(self.executor(), opts) + } + + pub fn plot_find_one( + &self, + opts: &IPlotFindOne, + ) -> Result<IPlotFindOneResolve, IError<SqlError>> { + models::plot::find_one(self.executor(), opts) + } + + pub fn plot_update( + &self, + opts: &IPlotUpdate, + ) -> Result<IPlotUpdateResolve, IError<SqlError>> { + models::plot::update(self.executor(), opts) + } + + pub fn plot_delete( + &self, + opts: &IPlotDelete, + ) -> Result<IPlotDeleteResolve, IError<SqlError>> { + models::plot::delete(self.executor(), opts) + } + + pub fn gcs_location_create( + &self, + opts: &IGcsLocationCreate, + ) -> Result<IGcsLocationCreateResolve, IError<SqlError>> { + models::gcs_location::create(self.executor(), opts) + } + + pub fn gcs_location_find_many( + &self, + opts: &IGcsLocationFindMany, + ) -> Result<IGcsLocationFindManyResolve, IError<SqlError>> { + models::gcs_location::find_many(self.executor(), opts) + } + + pub fn gcs_location_find_one( + &self, + opts: &IGcsLocationFindOne, + ) -> Result<IGcsLocationFindOneResolve, IError<SqlError>> { + models::gcs_location::find_one(self.executor(), opts) + } + + pub fn gcs_location_update( + &self, + opts: &IGcsLocationUpdate, + ) -> Result<IGcsLocationUpdateResolve, IError<SqlError>> { + models::gcs_location::update(self.executor(), opts) + } + + pub fn gcs_location_delete( + &self, + opts: &IGcsLocationDelete, + ) -> Result<IGcsLocationDeleteResolve, IError<SqlError>> { + models::gcs_location::delete(self.executor(), opts) + } + + pub fn farm_gcs_location_create( + &self, + opts: &IFarmGcsLocationCreate, + ) -> Result<IFarmGcsLocationCreateResolve, IError<SqlError>> { + models::farm_gcs_location::create(self.executor(), opts) + } + + pub fn farm_gcs_location_find_many( + &self, + opts: &IFarmGcsLocationFindMany, + ) -> Result<IFarmGcsLocationFindManyResolve, IError<SqlError>> { + models::farm_gcs_location::find_many(self.executor(), opts) + } + + pub fn farm_gcs_location_find_one( + &self, + opts: &IFarmGcsLocationFindOne, + ) -> Result<IFarmGcsLocationFindOneResolve, IError<SqlError>> { + models::farm_gcs_location::find_one(self.executor(), opts) + } + + pub fn farm_gcs_location_update( + &self, + opts: &IFarmGcsLocationUpdate, + ) -> Result<IFarmGcsLocationUpdateResolve, IError<SqlError>> { + models::farm_gcs_location::update(self.executor(), opts) + } + + pub fn farm_gcs_location_delete( + &self, + opts: &IFarmGcsLocationDelete, + ) -> Result<IFarmGcsLocationDeleteResolve, IError<SqlError>> { + models::farm_gcs_location::delete(self.executor(), opts) + } + + pub fn plot_gcs_location_create( + &self, + opts: &IPlotGcsLocationCreate, + ) -> Result<IPlotGcsLocationCreateResolve, IError<SqlError>> { + models::plot_gcs_location::create(self.executor(), opts) + } + + pub fn plot_gcs_location_find_many( + &self, + opts: &IPlotGcsLocationFindMany, + ) -> Result<IPlotGcsLocationFindManyResolve, IError<SqlError>> { + models::plot_gcs_location::find_many(self.executor(), opts) + } + + pub fn plot_gcs_location_find_one( + &self, + opts: &IPlotGcsLocationFindOne, + ) -> Result<IPlotGcsLocationFindOneResolve, IError<SqlError>> { + models::plot_gcs_location::find_one(self.executor(), opts) + } + + pub fn plot_gcs_location_update( + &self, + opts: &IPlotGcsLocationUpdate, + ) -> Result<IPlotGcsLocationUpdateResolve, IError<SqlError>> { + models::plot_gcs_location::update(self.executor(), opts) + } + + pub fn plot_gcs_location_delete( + &self, + opts: &IPlotGcsLocationDelete, + ) -> Result<IPlotGcsLocationDeleteResolve, IError<SqlError>> { + models::plot_gcs_location::delete(self.executor(), opts) + } + + pub fn farm_tag_create( + &self, + opts: &IFarmTagCreate, + ) -> Result<IFarmTagCreateResolve, IError<SqlError>> { + models::farm_tag::create(self.executor(), opts) + } + + pub fn farm_tag_find_many( + &self, + opts: &IFarmTagFindMany, + ) -> Result<IFarmTagFindManyResolve, IError<SqlError>> { + models::farm_tag::find_many(self.executor(), opts) + } + + pub fn farm_tag_find_one( + &self, + opts: &IFarmTagFindOne, + ) -> Result<IFarmTagFindOneResolve, IError<SqlError>> { + models::farm_tag::find_one(self.executor(), opts) + } + + pub fn farm_tag_update( + &self, + opts: &IFarmTagUpdate, + ) -> Result<IFarmTagUpdateResolve, IError<SqlError>> { + models::farm_tag::update(self.executor(), opts) + } + + pub fn farm_tag_delete( + &self, + opts: &IFarmTagDelete, + ) -> Result<IFarmTagDeleteResolve, IError<SqlError>> { + models::farm_tag::delete(self.executor(), opts) + } + + pub fn plot_tag_create( + &self, + opts: &IPlotTagCreate, + ) -> Result<IPlotTagCreateResolve, IError<SqlError>> { + models::plot_tag::create(self.executor(), opts) + } + + pub fn plot_tag_find_many( + &self, + opts: &IPlotTagFindMany, + ) -> Result<IPlotTagFindManyResolve, IError<SqlError>> { + models::plot_tag::find_many(self.executor(), opts) + } + + pub fn plot_tag_find_one( + &self, + opts: &IPlotTagFindOne, + ) -> Result<IPlotTagFindOneResolve, IError<SqlError>> { + models::plot_tag::find_one(self.executor(), opts) + } + + pub fn plot_tag_update( + &self, + opts: &IPlotTagUpdate, + ) -> Result<IPlotTagUpdateResolve, IError<SqlError>> { + models::plot_tag::update(self.executor(), opts) + } + + pub fn plot_tag_delete( + &self, + opts: &IPlotTagDelete, + ) -> Result<IPlotTagDeleteResolve, IError<SqlError>> { + models::plot_tag::delete(self.executor(), opts) + } + + pub fn farm_member_create( + &self, + opts: &IFarmMemberCreate, + ) -> Result<IFarmMemberCreateResolve, IError<SqlError>> { + models::farm_member::create(self.executor(), opts) + } + + pub fn farm_member_find_many( + &self, + opts: &IFarmMemberFindMany, + ) -> Result<IFarmMemberFindManyResolve, IError<SqlError>> { + models::farm_member::find_many(self.executor(), opts) + } + + pub fn farm_member_find_one( + &self, + opts: &IFarmMemberFindOne, + ) -> Result<IFarmMemberFindOneResolve, IError<SqlError>> { + models::farm_member::find_one(self.executor(), opts) + } + + pub fn farm_member_update( + &self, + opts: &IFarmMemberUpdate, + ) -> Result<IFarmMemberUpdateResolve, IError<SqlError>> { + models::farm_member::update(self.executor(), opts) + } + + pub fn farm_member_delete( + &self, + opts: &IFarmMemberDelete, + ) -> Result<IFarmMemberDeleteResolve, IError<SqlError>> { + models::farm_member::delete(self.executor(), opts) + } + + pub fn farm_member_claim_create( + &self, + opts: &IFarmMemberClaimCreate, + ) -> Result<IFarmMemberClaimCreateResolve, IError<SqlError>> { + models::farm_member_claim::create(self.executor(), opts) + } + + pub fn farm_member_claim_find_many( + &self, + opts: &IFarmMemberClaimFindMany, + ) -> Result<IFarmMemberClaimFindManyResolve, IError<SqlError>> { + models::farm_member_claim::find_many(self.executor(), opts) + } + + pub fn farm_member_claim_find_one( + &self, + opts: &IFarmMemberClaimFindOne, + ) -> Result<IFarmMemberClaimFindOneResolve, IError<SqlError>> { + models::farm_member_claim::find_one(self.executor(), opts) + } + + pub fn farm_member_claim_update( + &self, + opts: &IFarmMemberClaimUpdate, + ) -> Result<IFarmMemberClaimUpdateResolve, IError<SqlError>> { + models::farm_member_claim::update(self.executor(), opts) + } + + pub fn farm_member_claim_delete( + &self, + opts: &IFarmMemberClaimDelete, + ) -> Result<IFarmMemberClaimDeleteResolve, IError<SqlError>> { + models::farm_member_claim::delete(self.executor(), opts) + } + + pub fn log_error_create( + &self, + opts: &ILogErrorCreate, + ) -> Result<ILogErrorCreateResolve, IError<SqlError>> { + models::log_error::create(self.executor(), opts) + } + + pub fn log_error_find_many( + &self, + opts: &ILogErrorFindMany, + ) -> Result<ILogErrorFindManyResolve, IError<SqlError>> { + models::log_error::find_many(self.executor(), opts) + } + + pub fn log_error_find_one( + &self, + opts: &ILogErrorFindOne, + ) -> Result<ILogErrorFindOneResolve, IError<SqlError>> { + models::log_error::find_one(self.executor(), opts) + } + + pub fn log_error_update( + &self, + opts: &ILogErrorUpdate, + ) -> Result<ILogErrorUpdateResolve, IError<SqlError>> { + models::log_error::update(self.executor(), opts) + } + + pub fn log_error_delete( + &self, + opts: &ILogErrorDelete, + ) -> Result<ILogErrorDeleteResolve, IError<SqlError>> { + models::log_error::delete(self.executor(), opts) + } + + pub fn media_image_create( + &self, + opts: &IMediaImageCreate, + ) -> Result<IMediaImageCreateResolve, IError<SqlError>> { + models::media_image::create(self.executor(), opts) + } + + pub fn media_image_find_many( + &self, + opts: &IMediaImageFindMany, + ) -> Result<IMediaImageFindManyResolve, IError<SqlError>> { + models::media_image::find_many(self.executor(), opts) + } + + pub fn media_image_find_one( + &self, + opts: &IMediaImageFindOne, + ) -> Result<IMediaImageFindOneResolve, IError<SqlError>> { + models::media_image::find_one(self.executor(), opts) + } + + pub fn media_image_update( + &self, + opts: &IMediaImageUpdate, + ) -> Result<IMediaImageUpdateResolve, IError<SqlError>> { + models::media_image::update(self.executor(), opts) + } + + pub fn media_image_delete( + &self, + opts: &IMediaImageDelete, + ) -> Result<IMediaImageDeleteResolve, IError<SqlError>> { + models::media_image::delete(self.executor(), opts) + } + + pub fn nostr_profile_create( + &self, + opts: &INostrProfileCreate, + ) -> Result<INostrProfileCreateResolve, IError<SqlError>> { + models::nostr_profile::create(self.executor(), opts) + } + + pub fn nostr_profile_find_many( + &self, + opts: &INostrProfileFindMany, + ) -> Result<INostrProfileFindManyResolve, IError<SqlError>> { + models::nostr_profile::find_many(self.executor(), opts) + } + + pub fn nostr_profile_find_one( + &self, + opts: &INostrProfileFindOne, + ) -> Result<INostrProfileFindOneResolve, IError<SqlError>> { + models::nostr_profile::find_one(self.executor(), opts) + } + + pub fn nostr_profile_update( + &self, + opts: &INostrProfileUpdate, + ) -> Result<INostrProfileUpdateResolve, IError<SqlError>> { + models::nostr_profile::update(self.executor(), opts) + } + + pub fn nostr_profile_delete( + &self, + opts: &INostrProfileDelete, + ) -> Result<INostrProfileDeleteResolve, IError<SqlError>> { + models::nostr_profile::delete(self.executor(), opts) + } + + pub fn nostr_event_state_create( + &self, + opts: &INostrEventStateCreate, + ) -> Result<INostrEventStateCreateResolve, IError<SqlError>> { + models::nostr_event_state::create(self.executor(), opts) + } + + pub fn nostr_event_state_find_many( + &self, + opts: &INostrEventStateFindMany, + ) -> Result<INostrEventStateFindManyResolve, IError<SqlError>> { + models::nostr_event_state::find_many(self.executor(), opts) + } + + pub fn nostr_event_state_find_one( + &self, + opts: &INostrEventStateFindOne, + ) -> Result<INostrEventStateFindOneResolve, IError<SqlError>> { + models::nostr_event_state::find_one(self.executor(), opts) + } + + pub fn nostr_event_state_update( + &self, + opts: &INostrEventStateUpdate, + ) -> Result<INostrEventStateUpdateResolve, IError<SqlError>> { + models::nostr_event_state::update(self.executor(), opts) + } + + pub fn nostr_event_state_delete( + &self, + opts: &INostrEventStateDelete, + ) -> Result<INostrEventStateDeleteResolve, IError<SqlError>> { + models::nostr_event_state::delete(self.executor(), opts) + } + + pub fn nostr_relay_create( + &self, + opts: &INostrRelayCreate, + ) -> Result<INostrRelayCreateResolve, IError<SqlError>> { + models::nostr_relay::create(self.executor(), opts) + } + + pub fn nostr_relay_find_many( + &self, + opts: &INostrRelayFindMany, + ) -> Result<INostrRelayFindManyResolve, IError<SqlError>> { + models::nostr_relay::find_many(self.executor(), opts) + } + + pub fn nostr_relay_find_one( + &self, + opts: &INostrRelayFindOne, + ) -> Result<INostrRelayFindOneResolve, IError<SqlError>> { + models::nostr_relay::find_one(self.executor(), opts) + } + + pub fn nostr_relay_update( + &self, + opts: &INostrRelayUpdate, + ) -> Result<INostrRelayUpdateResolve, IError<SqlError>> { + models::nostr_relay::update(self.executor(), opts) + } + + pub fn nostr_relay_delete( + &self, + opts: &INostrRelayDelete, + ) -> Result<INostrRelayDeleteResolve, IError<SqlError>> { + models::nostr_relay::delete(self.executor(), opts) + } + + pub fn trade_product_create( + &self, + opts: &ITradeProductCreate, + ) -> Result<ITradeProductCreateResolve, IError<SqlError>> { + models::trade_product::create(self.executor(), opts) + } + + pub fn trade_product_find_many( + &self, + opts: &ITradeProductFindMany, + ) -> Result<ITradeProductFindManyResolve, IError<SqlError>> { + models::trade_product::find_many(self.executor(), opts) + } + + pub fn trade_product_find_one( + &self, + opts: &ITradeProductFindOne, + ) -> Result<ITradeProductFindOneResolve, IError<SqlError>> { + models::trade_product::find_one(self.executor(), opts) + } + + pub fn trade_product_update( + &self, + opts: &ITradeProductUpdate, + ) -> Result<ITradeProductUpdateResolve, IError<SqlError>> { + models::trade_product::update(self.executor(), opts) + } + + pub fn trade_product_delete( + &self, + opts: &ITradeProductDelete, + ) -> Result<ITradeProductDeleteResolve, IError<SqlError>> { + models::trade_product::delete(self.executor(), opts) + } + + pub fn nostr_profile_relay_set( + &self, + opts: &INostrProfileRelayRelation, + ) -> Result<INostrProfileRelayResolve, IError<SqlError>> { + models::nostr_profile_relay::set(self.executor(), opts) + } + + pub fn nostr_profile_relay_unset( + &self, + opts: &INostrProfileRelayRelation, + ) -> Result<INostrProfileRelayResolve, IError<SqlError>> { + models::nostr_profile_relay::unset(self.executor(), opts) + } + + pub fn trade_product_location_set( + &self, + opts: &ITradeProductLocationRelation, + ) -> Result<ITradeProductLocationResolve, IError<SqlError>> { + models::trade_product_location::set(self.executor(), opts) + } + + pub fn trade_product_location_unset( + &self, + opts: &ITradeProductLocationRelation, + ) -> Result<ITradeProductLocationResolve, IError<SqlError>> { + models::trade_product_location::unset(self.executor(), opts) + } + + pub fn trade_product_media_set( + &self, + opts: &ITradeProductMediaRelation, + ) -> Result<ITradeProductMediaResolve, IError<SqlError>> { + models::trade_product_media::set(self.executor(), opts) + } + + pub fn trade_product_media_unset( + &self, + opts: &ITradeProductMediaRelation, + ) -> Result<ITradeProductMediaResolve, IError<SqlError>> { + models::trade_product_media::unset(self.executor(), opts) + } + +} diff --git a/tangle-db/src/migrations.rs b/tangle-db/src/migrations.rs @@ -0,0 +1,115 @@ +use radroots_sql_core::SqlExecutor; +use radroots_sql_core::error::SqlError; +use radroots_sql_core::migrations::{Migration, migrations_run_all_down, migrations_run_all_up}; + +pub static MIGRATIONS: &[Migration] = &[ + Migration { + name: "0000_init", + up_sql: include_str!("../migrations/0000_init.up.sql"), + down_sql: include_str!("../migrations/0000_init.down.sql"), + }, + Migration { + name: "0001_log_error", + up_sql: include_str!("../migrations/0001_log_error.up.sql"), + down_sql: include_str!("../migrations/0001_log_error.down.sql"), + }, + Migration { + name: "0002_farm", + up_sql: include_str!("../migrations/0002_farm.up.sql"), + down_sql: include_str!("../migrations/0002_farm.down.sql"), + }, + Migration { + name: "0003_gcs_location", + up_sql: include_str!("../migrations/0003_gcs_location.up.sql"), + down_sql: include_str!("../migrations/0003_gcs_location.down.sql"), + }, + Migration { + name: "0004_trade_product", + up_sql: include_str!("../migrations/0004_trade_product.up.sql"), + down_sql: include_str!("../migrations/0004_trade_product.down.sql"), + }, + Migration { + name: "0005_nostr_profile", + up_sql: include_str!("../migrations/0005_nostr_profile.up.sql"), + down_sql: include_str!("../migrations/0005_nostr_profile.down.sql"), + }, + Migration { + name: "0006_nostr_relay", + up_sql: include_str!("../migrations/0006_nostr_relay.up.sql"), + down_sql: include_str!("../migrations/0006_nostr_relay.down.sql"), + }, + Migration { + name: "0007_media_image", + up_sql: include_str!("../migrations/0007_media_image.up.sql"), + down_sql: include_str!("../migrations/0007_media_image.down.sql"), + }, + Migration { + name: "0008_farm_gcs_location", + up_sql: include_str!("../migrations/0008_farm_gcs_location.up.sql"), + down_sql: include_str!("../migrations/0008_farm_gcs_location.down.sql"), + }, + Migration { + name: "0009_nostr_profile_relay", + up_sql: include_str!("../migrations/0009_nostr_profile_relay.up.sql"), + down_sql: include_str!("../migrations/0009_nostr_profile_relay.down.sql"), + }, + Migration { + name: "0010_trade_product_location", + up_sql: include_str!("../migrations/0010_trade_product_location.up.sql"), + down_sql: include_str!("../migrations/0010_trade_product_location.down.sql"), + }, + Migration { + name: "0011_trade_product_media", + up_sql: include_str!("../migrations/0011_trade_product_media.up.sql"), + down_sql: include_str!("../migrations/0011_trade_product_media.down.sql"), + }, + Migration { + name: "0012_plot", + up_sql: include_str!("../migrations/0012_plot.up.sql"), + down_sql: include_str!("../migrations/0012_plot.down.sql"), + }, + Migration { + name: "0013_plot_gcs_location", + up_sql: include_str!("../migrations/0013_plot_gcs_location.up.sql"), + down_sql: include_str!("../migrations/0013_plot_gcs_location.down.sql"), + }, + Migration { + name: "0014_farm_tag", + up_sql: include_str!("../migrations/0014_farm_tag.up.sql"), + down_sql: include_str!("../migrations/0014_farm_tag.down.sql"), + }, + Migration { + name: "0015_plot_tag", + up_sql: include_str!("../migrations/0015_plot_tag.up.sql"), + down_sql: include_str!("../migrations/0015_plot_tag.down.sql"), + }, + Migration { + name: "0016_farm_member", + up_sql: include_str!("../migrations/0016_farm_member.up.sql"), + down_sql: include_str!("../migrations/0016_farm_member.down.sql"), + }, + Migration { + name: "0017_farm_member_claim", + up_sql: include_str!("../migrations/0017_farm_member_claim.up.sql"), + down_sql: include_str!("../migrations/0017_farm_member_claim.down.sql"), + }, + Migration { + name: "0018_nostr_event_state", + up_sql: include_str!("../migrations/0018_nostr_event_state.up.sql"), + down_sql: include_str!("../migrations/0018_nostr_event_state.down.sql"), + }, +]; + +pub fn run_all_up<E>(executor: &E) -> Result<(), SqlError> +where + E: SqlExecutor, +{ + migrations_run_all_up(executor, MIGRATIONS) +} + +pub fn run_all_down<E>(executor: &E) -> Result<(), SqlError> +where + E: SqlExecutor, +{ + migrations_run_all_down(executor, MIGRATIONS) +} diff --git a/tangle-db/src/models/farm.rs b/tangle-db/src/models/farm.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::farm::{ + IFarmCreate, + IFarmCreateResolve, + IFarmDelete, + IFarmDeleteResolve, + IFarmFieldsFilter, + IFarmFindMany, + IFarmFindManyResolve, + IFarmFindOne, + IFarmFindOneResolve, + IFarmUpdate, + IFarmUpdateResolve, + Farm, + FarmFindManyRel, + FarmQueryBindValues, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "farm"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &IFarmCreate, +) -> Result<IFarmCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = FarmQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &IFarmFindOne, +) -> Result<IFarmFindOneResolve, IError<SqlError>> { + let result = match opts { + IFarmFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IFarmFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &IFarmFindMany, +) -> Result<IFarmFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<IFarmFieldsFilter>, +) -> Result<Vec<Farm>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<Farm> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &FarmQueryBindValues, +) -> Result<Option<Farm>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<Farm> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &FarmFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &FarmFindManyRel, +) -> Result<Option<Farm>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<Farm> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<Farm, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<Farm> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &IFarmUpdate, +) -> Result<IFarmUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &IFarmDelete, +) -> Result<IFarmDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + IFarmDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IFarmDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &FarmFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/farm_gcs_location.rs b/tangle-db/src/models/farm_gcs_location.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::farm_gcs_location::{ + FarmGcsLocation, + FarmGcsLocationFindManyRel, + FarmGcsLocationQueryBindValues, + IFarmGcsLocationCreate, + IFarmGcsLocationCreateResolve, + IFarmGcsLocationDelete, + IFarmGcsLocationDeleteResolve, + IFarmGcsLocationFieldsFilter, + IFarmGcsLocationFindMany, + IFarmGcsLocationFindManyResolve, + IFarmGcsLocationFindOne, + IFarmGcsLocationFindOneResolve, + IFarmGcsLocationUpdate, + IFarmGcsLocationUpdateResolve, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "farm_gcs_location"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &IFarmGcsLocationCreate, +) -> Result<IFarmGcsLocationCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = FarmGcsLocationQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &IFarmGcsLocationFindOne, +) -> Result<IFarmGcsLocationFindOneResolve, IError<SqlError>> { + let result = match opts { + IFarmGcsLocationFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IFarmGcsLocationFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &IFarmGcsLocationFindMany, +) -> Result<IFarmGcsLocationFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<IFarmGcsLocationFieldsFilter>, +) -> Result<Vec<FarmGcsLocation>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<FarmGcsLocation> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &FarmGcsLocationQueryBindValues, +) -> Result<Option<FarmGcsLocation>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmGcsLocation> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &FarmGcsLocationFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &FarmGcsLocationFindManyRel, +) -> Result<Option<FarmGcsLocation>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmGcsLocation> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmGcsLocation, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmGcsLocation> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &IFarmGcsLocationUpdate, +) -> Result<IFarmGcsLocationUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &IFarmGcsLocationDelete, +) -> Result<IFarmGcsLocationDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + IFarmGcsLocationDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IFarmGcsLocationDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &FarmGcsLocationFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/farm_member.rs b/tangle-db/src/models/farm_member.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::farm_member::{ + FarmMember, + FarmMemberFindManyRel, + FarmMemberQueryBindValues, + IFarmMemberCreate, + IFarmMemberCreateResolve, + IFarmMemberDelete, + IFarmMemberDeleteResolve, + IFarmMemberFieldsFilter, + IFarmMemberFindMany, + IFarmMemberFindManyResolve, + IFarmMemberFindOne, + IFarmMemberFindOneResolve, + IFarmMemberUpdate, + IFarmMemberUpdateResolve, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "farm_member"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &IFarmMemberCreate, +) -> Result<IFarmMemberCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = FarmMemberQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &IFarmMemberFindOne, +) -> Result<IFarmMemberFindOneResolve, IError<SqlError>> { + let result = match opts { + IFarmMemberFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IFarmMemberFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &IFarmMemberFindMany, +) -> Result<IFarmMemberFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<IFarmMemberFieldsFilter>, +) -> Result<Vec<FarmMember>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<FarmMember> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &FarmMemberQueryBindValues, +) -> Result<Option<FarmMember>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmMember> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &FarmMemberFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &FarmMemberFindManyRel, +) -> Result<Option<FarmMember>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmMember> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmMember, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmMember> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &IFarmMemberUpdate, +) -> Result<IFarmMemberUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &IFarmMemberDelete, +) -> Result<IFarmMemberDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + IFarmMemberDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IFarmMemberDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &FarmMemberFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/farm_member_claim.rs b/tangle-db/src/models/farm_member_claim.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::farm_member_claim::{ + FarmMemberClaim, + FarmMemberClaimFindManyRel, + FarmMemberClaimQueryBindValues, + IFarmMemberClaimCreate, + IFarmMemberClaimCreateResolve, + IFarmMemberClaimDelete, + IFarmMemberClaimDeleteResolve, + IFarmMemberClaimFieldsFilter, + IFarmMemberClaimFindMany, + IFarmMemberClaimFindManyResolve, + IFarmMemberClaimFindOne, + IFarmMemberClaimFindOneResolve, + IFarmMemberClaimUpdate, + IFarmMemberClaimUpdateResolve, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "farm_member_claim"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &IFarmMemberClaimCreate, +) -> Result<IFarmMemberClaimCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = FarmMemberClaimQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &IFarmMemberClaimFindOne, +) -> Result<IFarmMemberClaimFindOneResolve, IError<SqlError>> { + let result = match opts { + IFarmMemberClaimFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IFarmMemberClaimFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &IFarmMemberClaimFindMany, +) -> Result<IFarmMemberClaimFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<IFarmMemberClaimFieldsFilter>, +) -> Result<Vec<FarmMemberClaim>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<FarmMemberClaim> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &FarmMemberClaimQueryBindValues, +) -> Result<Option<FarmMemberClaim>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmMemberClaim> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &FarmMemberClaimFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &FarmMemberClaimFindManyRel, +) -> Result<Option<FarmMemberClaim>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmMemberClaim> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmMemberClaim, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmMemberClaim> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &IFarmMemberClaimUpdate, +) -> Result<IFarmMemberClaimUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &IFarmMemberClaimDelete, +) -> Result<IFarmMemberClaimDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + IFarmMemberClaimDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IFarmMemberClaimDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &FarmMemberClaimFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/farm_tag.rs b/tangle-db/src/models/farm_tag.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::farm_tag::{ + FarmTag, + FarmTagFindManyRel, + FarmTagQueryBindValues, + IFarmTagCreate, + IFarmTagCreateResolve, + IFarmTagDelete, + IFarmTagDeleteResolve, + IFarmTagFieldsFilter, + IFarmTagFindMany, + IFarmTagFindManyResolve, + IFarmTagFindOne, + IFarmTagFindOneResolve, + IFarmTagUpdate, + IFarmTagUpdateResolve, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "farm_tag"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &IFarmTagCreate, +) -> Result<IFarmTagCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = FarmTagQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &IFarmTagFindOne, +) -> Result<IFarmTagFindOneResolve, IError<SqlError>> { + let result = match opts { + IFarmTagFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IFarmTagFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &IFarmTagFindMany, +) -> Result<IFarmTagFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<IFarmTagFieldsFilter>, +) -> Result<Vec<FarmTag>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<FarmTag> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &FarmTagQueryBindValues, +) -> Result<Option<FarmTag>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmTag> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &FarmTagFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &FarmTagFindManyRel, +) -> Result<Option<FarmTag>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmTag> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmTag, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<FarmTag> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &IFarmTagUpdate, +) -> Result<IFarmTagUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &IFarmTagDelete, +) -> Result<IFarmTagDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + IFarmTagDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IFarmTagDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &FarmTagFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/gcs_location.rs b/tangle-db/src/models/gcs_location.rs @@ -0,0 +1,226 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::gcs_location::{ + GcsLocation, + GcsLocationFindManyRel, + GcsLocationQueryBindValues, + IGcsLocationCreate, + IGcsLocationCreateResolve, + IGcsLocationDelete, + IGcsLocationDeleteResolve, + IGcsLocationFieldsFilter, + IGcsLocationFindMany, + IGcsLocationFindManyResolve, + IGcsLocationFindOne, + IGcsLocationFindOneResolve, + IGcsLocationUpdate, + IGcsLocationUpdateResolve, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "gcs_location"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &IGcsLocationCreate, +) -> Result<IGcsLocationCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = GcsLocationQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &IGcsLocationFindOne, +) -> Result<IGcsLocationFindOneResolve, IError<SqlError>> { + let result = match opts { + IGcsLocationFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IGcsLocationFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &IGcsLocationFindMany, +) -> Result<IGcsLocationFindManyResolve, IError<SqlError>> { + let results = match opts { + IGcsLocationFindMany::Filter { filter } => find_many_filter(exec, filter)?, + IGcsLocationFindMany::Rel { rel } => find_many_by_rel(exec, rel)?, + }; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<IGcsLocationFieldsFilter>, +) -> Result<Vec<GcsLocation>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<GcsLocation> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &GcsLocationQueryBindValues, +) -> Result<Option<GcsLocation>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<GcsLocation> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &GcsLocationFindManyRel) -> (&'static str, Vec<Value>) { + match rel { + GcsLocationFindManyRel::OnTradeProduct(args) => ( + "SELECT gl.* FROM gcs_location gl JOIN trade_product_location tp_gl ON gl.id = tp_gl.tb_gl WHERE tp_gl.tb_tp = ?", + vec![Value::from(args.id.clone())], + ), + GcsLocationFindManyRel::OffTradeProduct(args) => ( + "SELECT gl.* FROM gcs_location gl WHERE NOT EXISTS (SELECT 1 FROM trade_product_location tp_gl WHERE tp_gl.tb_gl = gl.id AND tp_gl.tb_tp = ?)", + vec![Value::from(args.id.clone())], + ), + GcsLocationFindManyRel::OnFarm(args) => ( + "SELECT gl.* FROM gcs_location gl JOIN farm_gcs_location fgcs ON gl.id = fgcs.gcs_location_id WHERE fgcs.farm_id = ?", + vec![Value::from(args.id.clone())], + ), + GcsLocationFindManyRel::OffFarm(args) => ( + "SELECT gl.* FROM gcs_location gl WHERE NOT EXISTS (SELECT 1 FROM farm_gcs_location fgcs WHERE fgcs.gcs_location_id = gl.id AND fgcs.farm_id = ?)", + vec![Value::from(args.id.clone())], + ), + GcsLocationFindManyRel::OnPlot(args) => ( + "SELECT gl.* FROM gcs_location gl JOIN plot_gcs_location pgcs ON gl.id = pgcs.gcs_location_id WHERE pgcs.plot_id = ?", + vec![Value::from(args.id.clone())], + ), + GcsLocationFindManyRel::OffPlot(args) => ( + "SELECT gl.* FROM gcs_location gl WHERE NOT EXISTS (SELECT 1 FROM plot_gcs_location pgcs WHERE pgcs.gcs_location_id = gl.id AND pgcs.plot_id = ?)", + vec![Value::from(args.id.clone())], + ), + } +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &GcsLocationFindManyRel, +) -> Result<Option<GcsLocation>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<GcsLocation> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn find_many_by_rel<E: SqlExecutor>( + exec: &E, + rel: &GcsLocationFindManyRel, +) -> Result<Vec<GcsLocation>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql};"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<GcsLocation> = utils::parse_json(&json)?; + Ok(rows) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<GcsLocation, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<GcsLocation> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &IGcsLocationUpdate, +) -> Result<IGcsLocationUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &IGcsLocationDelete, +) -> Result<IGcsLocationDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + IGcsLocationDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IGcsLocationDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &GcsLocationFindManyRel) -> String { + match rel { + GcsLocationFindManyRel::OnTradeProduct(args) => format!("on_trade_product:{}", args.id.as_str()), + GcsLocationFindManyRel::OffTradeProduct(args) => format!("off_trade_product:{}", args.id.as_str()), + GcsLocationFindManyRel::OnFarm(args) => format!("on_farm:{}", args.id.as_str()), + GcsLocationFindManyRel::OffFarm(args) => format!("off_farm:{}", args.id.as_str()), + GcsLocationFindManyRel::OnPlot(args) => format!("on_plot:{}", args.id.as_str()), + GcsLocationFindManyRel::OffPlot(args) => format!("off_plot:{}", args.id.as_str()), + } +} diff --git a/tangle-db/src/models/log_error.rs b/tangle-db/src/models/log_error.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::log_error::{ + ILogErrorCreate, + ILogErrorCreateResolve, + ILogErrorDelete, + ILogErrorDeleteResolve, + ILogErrorFieldsFilter, + ILogErrorFindMany, + ILogErrorFindManyResolve, + ILogErrorFindOne, + ILogErrorFindOneResolve, + ILogErrorUpdate, + ILogErrorUpdateResolve, + LogError, + LogErrorFindManyRel, + LogErrorQueryBindValues, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "log_error"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &ILogErrorCreate, +) -> Result<ILogErrorCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = LogErrorQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &ILogErrorFindOne, +) -> Result<ILogErrorFindOneResolve, IError<SqlError>> { + let result = match opts { + ILogErrorFindOne::On(args) => find_one_by_on(exec, &args.on)?, + ILogErrorFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &ILogErrorFindMany, +) -> Result<ILogErrorFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<ILogErrorFieldsFilter>, +) -> Result<Vec<LogError>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<LogError> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &LogErrorQueryBindValues, +) -> Result<Option<LogError>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<LogError> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &LogErrorFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &LogErrorFindManyRel, +) -> Result<Option<LogError>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<LogError> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<LogError, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<LogError> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &ILogErrorUpdate, +) -> Result<ILogErrorUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &ILogErrorDelete, +) -> Result<ILogErrorDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + ILogErrorDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + ILogErrorDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &LogErrorFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/media_image.rs b/tangle-db/src/models/media_image.rs @@ -0,0 +1,206 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::media_image::{ + IMediaImageCreate, + IMediaImageCreateResolve, + IMediaImageDelete, + IMediaImageDeleteResolve, + IMediaImageFieldsFilter, + IMediaImageFindMany, + IMediaImageFindManyResolve, + IMediaImageFindOne, + IMediaImageFindOneResolve, + IMediaImageUpdate, + IMediaImageUpdateResolve, + MediaImage, + MediaImageFindManyRel, + MediaImageQueryBindValues, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "media_image"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &IMediaImageCreate, +) -> Result<IMediaImageCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = MediaImageQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &IMediaImageFindOne, +) -> Result<IMediaImageFindOneResolve, IError<SqlError>> { + let result = match opts { + IMediaImageFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IMediaImageFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &IMediaImageFindMany, +) -> Result<IMediaImageFindManyResolve, IError<SqlError>> { + let results = match opts { + IMediaImageFindMany::Filter { filter } => find_many_filter(exec, filter)?, + IMediaImageFindMany::Rel { rel } => find_many_by_rel(exec, rel)?, + }; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<IMediaImageFieldsFilter>, +) -> Result<Vec<MediaImage>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<MediaImage> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &MediaImageQueryBindValues, +) -> Result<Option<MediaImage>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &MediaImageFindManyRel) -> (&'static str, Vec<Value>) { + match rel { + MediaImageFindManyRel::OnTradeProduct(args) => ( + "SELECT mu.* FROM media_image mu JOIN trade_product_media tp_lg ON mu.id = tp_lg.tb_mu WHERE tp_lg.tb_tp = ?", + vec![Value::from(args.id.clone())], + ), + MediaImageFindManyRel::OffTradeProduct(args) => ( + "SELECT mu.* FROM media_image mu WHERE NOT EXISTS (SELECT 1 FROM trade_product_media tp_lg WHERE tp_lg.tb_mu = mu.id AND tp_lg.tb_tp = ?)", + vec![Value::from(args.id.clone())], + ), + } +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &MediaImageFindManyRel, +) -> Result<Option<MediaImage>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn find_many_by_rel<E: SqlExecutor>( + exec: &E, + rel: &MediaImageFindManyRel, +) -> Result<Vec<MediaImage>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql};"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<MediaImage> = utils::parse_json(&json)?; + Ok(rows) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<MediaImage, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &IMediaImageUpdate, +) -> Result<IMediaImageUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &IMediaImageDelete, +) -> Result<IMediaImageDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + IMediaImageDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IMediaImageDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &MediaImageFindManyRel) -> String { + match rel { + MediaImageFindManyRel::OnTradeProduct(args) => format!("on_trade_product:{}", args.id.as_str()), + MediaImageFindManyRel::OffTradeProduct(args) => format!("off_trade_product:{}", args.id.as_str()), + } +} diff --git a/tangle-db/src/models/mod.rs b/tangle-db/src/models/mod.rs @@ -0,0 +1,18 @@ +pub mod farm; +pub mod farm_gcs_location; +pub mod farm_member; +pub mod farm_member_claim; +pub mod farm_tag; +pub mod gcs_location; +pub mod log_error; +pub mod media_image; +pub mod nostr_event_state; +pub mod nostr_profile; +pub mod nostr_profile_relay; +pub mod nostr_relay; +pub mod plot; +pub mod plot_gcs_location; +pub mod plot_tag; +pub mod trade_product; +pub mod trade_product_location; +pub mod trade_product_media; diff --git a/tangle-db/src/models/nostr_event_state.rs b/tangle-db/src/models/nostr_event_state.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::nostr_event_state::{ + INostrEventStateCreate, + INostrEventStateCreateResolve, + INostrEventStateDelete, + INostrEventStateDeleteResolve, + INostrEventStateFieldsFilter, + INostrEventStateFindMany, + INostrEventStateFindManyResolve, + INostrEventStateFindOne, + INostrEventStateFindOneResolve, + INostrEventStateUpdate, + INostrEventStateUpdateResolve, + NostrEventState, + NostrEventStateFindManyRel, + NostrEventStateQueryBindValues, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "nostr_event_state"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &INostrEventStateCreate, +) -> Result<INostrEventStateCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = NostrEventStateQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &INostrEventStateFindOne, +) -> Result<INostrEventStateFindOneResolve, IError<SqlError>> { + let result = match opts { + INostrEventStateFindOne::On(args) => find_one_by_on(exec, &args.on)?, + INostrEventStateFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &INostrEventStateFindMany, +) -> Result<INostrEventStateFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<INostrEventStateFieldsFilter>, +) -> Result<Vec<NostrEventState>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<NostrEventState> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &NostrEventStateQueryBindValues, +) -> Result<Option<NostrEventState>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<NostrEventState> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &NostrEventStateFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &NostrEventStateFindManyRel, +) -> Result<Option<NostrEventState>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<NostrEventState> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrEventState, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<NostrEventState> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &INostrEventStateUpdate, +) -> Result<INostrEventStateUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &INostrEventStateDelete, +) -> Result<INostrEventStateDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + INostrEventStateDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + INostrEventStateDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &NostrEventStateFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/nostr_profile.rs b/tangle-db/src/models/nostr_profile.rs @@ -0,0 +1,206 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::nostr_profile::{ + INostrProfileCreate, + INostrProfileCreateResolve, + INostrProfileDelete, + INostrProfileDeleteResolve, + INostrProfileFieldsFilter, + INostrProfileFindMany, + INostrProfileFindManyResolve, + INostrProfileFindOne, + INostrProfileFindOneResolve, + INostrProfileUpdate, + INostrProfileUpdateResolve, + NostrProfile, + NostrProfileFindManyRel, + NostrProfileQueryBindValues, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "nostr_profile"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &INostrProfileCreate, +) -> Result<INostrProfileCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = NostrProfileQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &INostrProfileFindOne, +) -> Result<INostrProfileFindOneResolve, IError<SqlError>> { + let result = match opts { + INostrProfileFindOne::On(args) => find_one_by_on(exec, &args.on)?, + INostrProfileFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &INostrProfileFindMany, +) -> Result<INostrProfileFindManyResolve, IError<SqlError>> { + let results = match opts { + INostrProfileFindMany::Filter { filter } => find_many_filter(exec, filter)?, + INostrProfileFindMany::Rel { rel } => find_many_by_rel(exec, rel)?, + }; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<INostrProfileFieldsFilter>, +) -> Result<Vec<NostrProfile>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<NostrProfile> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &NostrProfileQueryBindValues, +) -> Result<Option<NostrProfile>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &NostrProfileFindManyRel) -> (&'static str, Vec<Value>) { + match rel { + NostrProfileFindManyRel::OnRelay(args) => ( + "SELECT pr.* FROM nostr_profile pr JOIN nostr_profile_relay pr_rl ON pr.id = pr_rl.tb_pr WHERE pr_rl.tb_rl = ?", + vec![Value::from(args.id.clone())], + ), + NostrProfileFindManyRel::OffRelay(args) => ( + "SELECT pr.* FROM nostr_profile pr WHERE NOT EXISTS (SELECT 1 FROM nostr_profile_relay pr_rl WHERE pr_rl.tb_pr = pr.id AND pr_rl.tb_rl = ?)", + vec![Value::from(args.id.clone())], + ), + } +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &NostrProfileFindManyRel, +) -> Result<Option<NostrProfile>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn find_many_by_rel<E: SqlExecutor>( + exec: &E, + rel: &NostrProfileFindManyRel, +) -> Result<Vec<NostrProfile>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql};"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<NostrProfile> = utils::parse_json(&json)?; + Ok(rows) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrProfile, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &INostrProfileUpdate, +) -> Result<INostrProfileUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &INostrProfileDelete, +) -> Result<INostrProfileDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + INostrProfileDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + INostrProfileDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &NostrProfileFindManyRel) -> String { + match rel { + NostrProfileFindManyRel::OnRelay(args) => format!("on_relay:{}", args.id.as_str()), + NostrProfileFindManyRel::OffRelay(args) => format!("off_relay:{}", args.id.as_str()), + } +} diff --git a/tangle-db/src/models/nostr_profile_relay.rs b/tangle-db/src/models/nostr_profile_relay.rs @@ -0,0 +1,40 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::nostr_profile_relay::{ + INostrProfileRelayRelation, + INostrProfileRelayResolve, +}; +use radroots_types::types::{IError, IResultPass}; +use serde_json::Value; + +const TABLE_NAME: &str = "nostr_profile_relay"; + +pub fn set<E: SqlExecutor>( + exec: &E, + opts: &INostrProfileRelayRelation, +) -> Result<INostrProfileRelayResolve, IError<SqlError>> { + let mut query_vals: Vec<Value> = Vec::with_capacity(2); + let (nostr_profile_column, nostr_profile_value) = opts.nostr_profile.to_filter_param(); + query_vals.push(nostr_profile_value); + let (nostr_relay_column, nostr_relay_value) = opts.nostr_relay.to_filter_param(); + query_vals.push(nostr_relay_value); + let query = format!("INSERT INTO {} (tb_pr, tb_rl) VALUES ((SELECT id FROM nostr_profile WHERE {} = ?), (SELECT id FROM nostr_relay WHERE {} = ?));", TABLE_NAME, nostr_profile_column, nostr_relay_column); + let params_json = utils::to_params_json(query_vals)?; + let _ = exec.exec(&query, ¶ms_json)?; + Ok(IResultPass { pass: true }) +} + +pub fn unset<E: SqlExecutor>( + exec: &E, + opts: &INostrProfileRelayRelation, +) -> Result<INostrProfileRelayResolve, IError<SqlError>> { + let mut query_vals: Vec<Value> = Vec::with_capacity(2); + let (nostr_profile_column, nostr_profile_value) = opts.nostr_profile.to_filter_param(); + query_vals.push(nostr_profile_value); + let (nostr_relay_column, nostr_relay_value) = opts.nostr_relay.to_filter_param(); + query_vals.push(nostr_relay_value); + let query = format!("DELETE FROM {} WHERE tb_pr = (SELECT id FROM nostr_profile WHERE {} = ?) AND tb_rl = (SELECT id FROM nostr_relay WHERE {} = ?);", TABLE_NAME, nostr_profile_column, nostr_relay_column); + let params_json = utils::to_params_json(query_vals)?; + let _ = exec.exec(&query, ¶ms_json)?; + Ok(IResultPass { pass: true }) +} diff --git a/tangle-db/src/models/nostr_relay.rs b/tangle-db/src/models/nostr_relay.rs @@ -0,0 +1,206 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::nostr_relay::{ + INostrRelayCreate, + INostrRelayCreateResolve, + INostrRelayDelete, + INostrRelayDeleteResolve, + INostrRelayFieldsFilter, + INostrRelayFindMany, + INostrRelayFindManyResolve, + INostrRelayFindOne, + INostrRelayFindOneResolve, + INostrRelayUpdate, + INostrRelayUpdateResolve, + NostrRelay, + NostrRelayFindManyRel, + NostrRelayQueryBindValues, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "nostr_relay"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &INostrRelayCreate, +) -> Result<INostrRelayCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = NostrRelayQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &INostrRelayFindOne, +) -> Result<INostrRelayFindOneResolve, IError<SqlError>> { + let result = match opts { + INostrRelayFindOne::On(args) => find_one_by_on(exec, &args.on)?, + INostrRelayFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &INostrRelayFindMany, +) -> Result<INostrRelayFindManyResolve, IError<SqlError>> { + let results = match opts { + INostrRelayFindMany::Filter { filter } => find_many_filter(exec, filter)?, + INostrRelayFindMany::Rel { rel } => find_many_by_rel(exec, rel)?, + }; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<INostrRelayFieldsFilter>, +) -> Result<Vec<NostrRelay>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<NostrRelay> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &NostrRelayQueryBindValues, +) -> Result<Option<NostrRelay>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &NostrRelayFindManyRel) -> (&'static str, Vec<Value>) { + match rel { + NostrRelayFindManyRel::OnProfile(args) => ( + "SELECT rl.* FROM nostr_relay rl JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key = ?", + vec![Value::from(args.public_key.clone())], + ), + NostrRelayFindManyRel::OffProfile(args) => ( + "SELECT rl.* FROM nostr_relay rl LEFT JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl LEFT JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key <> ?", + vec![Value::from(args.public_key.clone())], + ), + } +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &NostrRelayFindManyRel, +) -> Result<Option<NostrRelay>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn find_many_by_rel<E: SqlExecutor>( + exec: &E, + rel: &NostrRelayFindManyRel, +) -> Result<Vec<NostrRelay>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql};"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<NostrRelay> = utils::parse_json(&json)?; + Ok(rows) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrRelay, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &INostrRelayUpdate, +) -> Result<INostrRelayUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &INostrRelayDelete, +) -> Result<INostrRelayDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + INostrRelayDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + INostrRelayDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &NostrRelayFindManyRel) -> String { + match rel { + NostrRelayFindManyRel::OnProfile(args) => format!("on_profile:{}", args.public_key.as_str()), + NostrRelayFindManyRel::OffProfile(args) => format!("off_profile:{}", args.public_key.as_str()), + } +} diff --git a/tangle-db/src/models/plot.rs b/tangle-db/src/models/plot.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::plot::{ + IPlotCreate, + IPlotCreateResolve, + IPlotDelete, + IPlotDeleteResolve, + IPlotFieldsFilter, + IPlotFindMany, + IPlotFindManyResolve, + IPlotFindOne, + IPlotFindOneResolve, + IPlotUpdate, + IPlotUpdateResolve, + Plot, + PlotFindManyRel, + PlotQueryBindValues, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "plot"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &IPlotCreate, +) -> Result<IPlotCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = PlotQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &IPlotFindOne, +) -> Result<IPlotFindOneResolve, IError<SqlError>> { + let result = match opts { + IPlotFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IPlotFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &IPlotFindMany, +) -> Result<IPlotFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<IPlotFieldsFilter>, +) -> Result<Vec<Plot>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<Plot> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &PlotQueryBindValues, +) -> Result<Option<Plot>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<Plot> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &PlotFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &PlotFindManyRel, +) -> Result<Option<Plot>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<Plot> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<Plot, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<Plot> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &IPlotUpdate, +) -> Result<IPlotUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &IPlotDelete, +) -> Result<IPlotDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + IPlotDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IPlotDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &PlotFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/plot_gcs_location.rs b/tangle-db/src/models/plot_gcs_location.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::plot_gcs_location::{ + IPlotGcsLocationCreate, + IPlotGcsLocationCreateResolve, + IPlotGcsLocationDelete, + IPlotGcsLocationDeleteResolve, + IPlotGcsLocationFieldsFilter, + IPlotGcsLocationFindMany, + IPlotGcsLocationFindManyResolve, + IPlotGcsLocationFindOne, + IPlotGcsLocationFindOneResolve, + IPlotGcsLocationUpdate, + IPlotGcsLocationUpdateResolve, + PlotGcsLocation, + PlotGcsLocationFindManyRel, + PlotGcsLocationQueryBindValues, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "plot_gcs_location"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &IPlotGcsLocationCreate, +) -> Result<IPlotGcsLocationCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = PlotGcsLocationQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &IPlotGcsLocationFindOne, +) -> Result<IPlotGcsLocationFindOneResolve, IError<SqlError>> { + let result = match opts { + IPlotGcsLocationFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IPlotGcsLocationFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &IPlotGcsLocationFindMany, +) -> Result<IPlotGcsLocationFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<IPlotGcsLocationFieldsFilter>, +) -> Result<Vec<PlotGcsLocation>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<PlotGcsLocation> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &PlotGcsLocationQueryBindValues, +) -> Result<Option<PlotGcsLocation>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<PlotGcsLocation> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &PlotGcsLocationFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &PlotGcsLocationFindManyRel, +) -> Result<Option<PlotGcsLocation>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<PlotGcsLocation> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<PlotGcsLocation, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<PlotGcsLocation> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &IPlotGcsLocationUpdate, +) -> Result<IPlotGcsLocationUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &IPlotGcsLocationDelete, +) -> Result<IPlotGcsLocationDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + IPlotGcsLocationDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IPlotGcsLocationDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &PlotGcsLocationFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/plot_tag.rs b/tangle-db/src/models/plot_tag.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::plot_tag::{ + IPlotTagCreate, + IPlotTagCreateResolve, + IPlotTagDelete, + IPlotTagDeleteResolve, + IPlotTagFieldsFilter, + IPlotTagFindMany, + IPlotTagFindManyResolve, + IPlotTagFindOne, + IPlotTagFindOneResolve, + IPlotTagUpdate, + IPlotTagUpdateResolve, + PlotTag, + PlotTagFindManyRel, + PlotTagQueryBindValues, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "plot_tag"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &IPlotTagCreate, +) -> Result<IPlotTagCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = PlotTagQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &IPlotTagFindOne, +) -> Result<IPlotTagFindOneResolve, IError<SqlError>> { + let result = match opts { + IPlotTagFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IPlotTagFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &IPlotTagFindMany, +) -> Result<IPlotTagFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<IPlotTagFieldsFilter>, +) -> Result<Vec<PlotTag>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<PlotTag> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &PlotTagQueryBindValues, +) -> Result<Option<PlotTag>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<PlotTag> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &PlotTagFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &PlotTagFindManyRel, +) -> Result<Option<PlotTag>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<PlotTag> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<PlotTag, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<PlotTag> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &IPlotTagUpdate, +) -> Result<IPlotTagUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &IPlotTagDelete, +) -> Result<IPlotTagDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + IPlotTagDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IPlotTagDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &PlotTagFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/trade_product.rs b/tangle-db/src/models/trade_product.rs @@ -0,0 +1,179 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::trade_product::{ + ITradeProductCreate, + ITradeProductCreateResolve, + ITradeProductDelete, + ITradeProductDeleteResolve, + ITradeProductFieldsFilter, + ITradeProductFindMany, + ITradeProductFindManyResolve, + ITradeProductFindOne, + ITradeProductFindOneResolve, + ITradeProductUpdate, + ITradeProductUpdateResolve, + TradeProduct, + TradeProductFindManyRel, + TradeProductQueryBindValues, +}; +use radroots_types::types::{IError, IResult, IResultList}; +use serde_json::Value; + +const TABLE_NAME: &str = "trade_product"; + +pub fn create<E: SqlExecutor>( + exec: &E, + opts: &ITradeProductCreate, +) -> Result<ITradeProductCreateResolve, IError<SqlError>> { + let field_map = utils::to_object_map(opts)?; + let id = utils::uuidv4(); + let now = utils::time_created_on(); + let meta: [(&str, Value); 3] = [ + ("id", Value::from(id.clone())), + ("created_at", Value::from(now.clone())), + ("updated_at", Value::from(now.clone())), + ]; + let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); + let params_json = utils::to_params_json(bind_values)?; + let _ = exec.exec(&sql, ¶ms_json)?; + let on = TradeProductQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? + .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; + Ok(IResult { result }) +} + +pub fn find_one<E: SqlExecutor>( + exec: &E, + opts: &ITradeProductFindOne, +) -> Result<ITradeProductFindOneResolve, IError<SqlError>> { + let result = match opts { + ITradeProductFindOne::On(args) => find_one_by_on(exec, &args.on)?, + ITradeProductFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; + Ok(IResult { result }) +} + +pub fn find_many<E: SqlExecutor>( + exec: &E, + opts: &ITradeProductFindMany, +) -> Result<ITradeProductFindManyResolve, IError<SqlError>> { + let results = find_many_filter(exec, &opts.filter)?; + Ok(IResultList { results }) +} + +fn find_many_filter<E: SqlExecutor>( + exec: &E, + filter: &Option<ITradeProductFieldsFilter>, +) -> Result<Vec<TradeProduct>, IError<SqlError>> { + let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); + let params_json = utils::to_params_json(bind_values)?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let rows: Vec<TradeProduct> = utils::parse_json(&json)?; + Ok(rows) +} + +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &TradeProductQueryBindValues, +) -> Result<Option<TradeProduct>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &TradeProductFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &TradeProductFindManyRel, +) -> Result<Option<TradeProduct>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<TradeProduct, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); + let json = exec.query_raw(&sql, ¶ms_json)?; + let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; + rows.pop() + .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) +} + +pub fn update<E: SqlExecutor>( + exec: &E, + opts: &ITradeProductUpdate, +) -> Result<ITradeProductUpdateResolve, IError<SqlError>> { + let mut updates = utils::to_partial_object_map(&opts.fields)?; + if updates.is_empty() { + return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); + } + updates.insert( + String::from("updated_at"), + Value::from(utils::time_created_on()), + ); + let mut set_parts = Vec::with_capacity(updates.len()); + let mut bind_values = Vec::with_capacity(updates.len() + 1); + for (column, value) in updates { + set_parts.push(format!("{column} = ?")); + bind_values.push(utils::to_db_bind_value(&value)); + } + let id_for_lookup = match opts.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + model.id + } + }; + bind_values.push(Value::from(id_for_lookup.clone())); + let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); + let params_json = utils::to_params_json(bind_values)?; + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + let updated = select_by_id(exec, &id_for_lookup)?; + Ok(IResult { result: updated }) +} + +pub fn delete<E: SqlExecutor>( + exec: &E, + opts: &ITradeProductDelete, +) -> Result<ITradeProductDeleteResolve, IError<SqlError>> { + let id_for_lookup = match opts { + ITradeProductDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + ITradeProductDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; + model.id + } + }; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); + let outcome = exec.exec(&sql, ¶ms_json)?; + if outcome.changes == 0 { + return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); + } + Ok(IResult { result: id_for_lookup }) +} + +fn rel_lookup_key(rel: &TradeProductFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-db/src/models/trade_product_location.rs b/tangle-db/src/models/trade_product_location.rs @@ -0,0 +1,40 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::trade_product_location::{ + ITradeProductLocationRelation, + ITradeProductLocationResolve, +}; +use radroots_types::types::{IError, IResultPass}; +use serde_json::Value; + +const TABLE_NAME: &str = "trade_product_location"; + +pub fn set<E: SqlExecutor>( + exec: &E, + opts: &ITradeProductLocationRelation, +) -> Result<ITradeProductLocationResolve, IError<SqlError>> { + let mut query_vals: Vec<Value> = Vec::with_capacity(2); + let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); + query_vals.push(trade_product_value); + let (gcs_location_column, gcs_location_value) = opts.gcs_location.to_filter_param(); + query_vals.push(gcs_location_value); + let query = format!("INSERT INTO {} (tb_tp, tb_gl) VALUES ((SELECT id FROM trade_product WHERE {} = ?), (SELECT id FROM gcs_location WHERE {} = ?));", TABLE_NAME, trade_product_column, gcs_location_column); + let params_json = utils::to_params_json(query_vals)?; + let _ = exec.exec(&query, ¶ms_json)?; + Ok(IResultPass { pass: true }) +} + +pub fn unset<E: SqlExecutor>( + exec: &E, + opts: &ITradeProductLocationRelation, +) -> Result<ITradeProductLocationResolve, IError<SqlError>> { + let mut query_vals: Vec<Value> = Vec::with_capacity(2); + let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); + query_vals.push(trade_product_value); + let (gcs_location_column, gcs_location_value) = opts.gcs_location.to_filter_param(); + query_vals.push(gcs_location_value); + let query = format!("DELETE FROM {} WHERE tb_tp = (SELECT id FROM trade_product WHERE {} = ?) AND tb_gl = (SELECT id FROM gcs_location WHERE {} = ?);", TABLE_NAME, trade_product_column, gcs_location_column); + let params_json = utils::to_params_json(query_vals)?; + let _ = exec.exec(&query, ¶ms_json)?; + Ok(IResultPass { pass: true }) +} diff --git a/tangle-db/src/models/trade_product_media.rs b/tangle-db/src/models/trade_product_media.rs @@ -0,0 +1,40 @@ +use radroots_sql_core::error::SqlError; +use radroots_sql_core::{SqlExecutor, utils}; +use radroots_tangle_db_schema::trade_product_media::{ + ITradeProductMediaRelation, + ITradeProductMediaResolve, +}; +use radroots_types::types::{IError, IResultPass}; +use serde_json::Value; + +const TABLE_NAME: &str = "trade_product_media"; + +pub fn set<E: SqlExecutor>( + exec: &E, + opts: &ITradeProductMediaRelation, +) -> Result<ITradeProductMediaResolve, IError<SqlError>> { + let mut query_vals: Vec<Value> = Vec::with_capacity(2); + let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); + query_vals.push(trade_product_value); + let (media_image_column, media_image_value) = opts.media_image.to_filter_param(); + query_vals.push(media_image_value); + let query = format!("INSERT INTO {} (tb_tp, tb_mu) VALUES ((SELECT id FROM trade_product WHERE {} = ?), (SELECT id FROM media_image WHERE {} = ?));", TABLE_NAME, trade_product_column, media_image_column); + let params_json = utils::to_params_json(query_vals)?; + let _ = exec.exec(&query, ¶ms_json)?; + Ok(IResultPass { pass: true }) +} + +pub fn unset<E: SqlExecutor>( + exec: &E, + opts: &ITradeProductMediaRelation, +) -> Result<ITradeProductMediaResolve, IError<SqlError>> { + let mut query_vals: Vec<Value> = Vec::with_capacity(2); + let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); + query_vals.push(trade_product_value); + let (media_image_column, media_image_value) = opts.media_image.to_filter_param(); + query_vals.push(media_image_value); + let query = format!("DELETE FROM {} WHERE tb_tp = (SELECT id FROM trade_product WHERE {} = ?) AND tb_mu = (SELECT id FROM media_image WHERE {} = ?);", TABLE_NAME, trade_product_column, media_image_column); + let params_json = utils::to_params_json(query_vals)?; + let _ = exec.exec(&query, ¶ms_json)?; + Ok(IResultPass { pass: true }) +} diff --git a/tangle-events-wasm/Cargo.toml b/tangle-events-wasm/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "radroots-tangle-events-wasm" +version.workspace = true +edition.workspace = true +authors = ["Radroots Authors"] +rust-version.workspace = true +license.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +radroots-events = { workspace = true, default-features = false, features = ["serde"] } +radroots-sql-core = { workspace = true, features = ["web"] } +radroots-sql-wasm-core = { workspace = true } +radroots-tangle-events = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +serde-wasm-bindgen = { workspace = true } +wasm-bindgen = { workspace = true } diff --git a/tangle-events-wasm/src/lib.rs b/tangle-events-wasm/src/lib.rs @@ -0,0 +1,44 @@ +#![cfg(target_arch = "wasm32")] +#![forbid(unsafe_code)] + +use radroots_events::RadrootsNostrEvent; +use radroots_sql_core::WasmSqlExecutor; +use radroots_tangle_events::{ + radroots_tangle_ingest_event, + radroots_tangle_sync_all, + RadrootsTangleIngestOutcome, + RadrootsTangleSyncRequest, +}; +use wasm_bindgen::prelude::*; + +fn err_js<E: ToString>(err: E) -> JsValue { + JsValue::from_str(&err.to_string()) +} + +fn parse_request(request_json: &str) -> Result<RadrootsTangleSyncRequest, JsValue> { + serde_json::from_str(request_json).map_err(err_js) +} + +fn parse_event(event_json: &str) -> Result<RadrootsNostrEvent, JsValue> { + serde_json::from_str(event_json).map_err(err_js) +} + +#[wasm_bindgen(js_name = tangle_events_sync_all)] +pub fn tangle_events_sync_all(request_json: &str) -> Result<JsValue, JsValue> { + let request = parse_request(request_json)?; + let exec = WasmSqlExecutor::new(); + let bundle = radroots_tangle_sync_all(&exec, &request).map_err(err_js)?; + serde_wasm_bindgen::to_value(&bundle).map_err(err_js) +} + +#[wasm_bindgen(js_name = tangle_events_ingest_event)] +pub fn tangle_events_ingest_event(event_json: &str) -> Result<JsValue, JsValue> { + let event = parse_event(event_json)?; + let exec = WasmSqlExecutor::new(); + let outcome = radroots_tangle_ingest_event(&exec, &event).map_err(err_js)?; + let value = match outcome { + RadrootsTangleIngestOutcome::Applied => "applied", + RadrootsTangleIngestOutcome::Skipped => "skipped", + }; + Ok(JsValue::from_str(value)) +} diff --git a/tangle-events/Cargo.toml b/tangle-events/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "radroots-tangle-events" +version.workspace = true +edition.workspace = true +authors = ["Radroots Authors"] +rust-version.workspace = true +license.workspace = true + +[lib] +crate-type = ["rlib"] + +[features] +default = ["std"] +std = [ + "radroots-events/std", + "radroots-events-codec/std", + "dep:base64", + "dep:uuid", +] + +[dependencies] +radroots-events = { workspace = true, default-features = false, features = ["serde"] } +radroots-events-codec = { workspace = true, default-features = false, features = ["serde_json"] } +radroots-sql-core = { workspace = true } +radroots-tangle-db-schema = { workspace = true } +radroots-tangle-db = { workspace = true } +radroots-types = { workspace = true } +hex = { workspace = true } +serde = { workspace = true, default-features = false, features = ["alloc", "derive"] } +serde_json = { workspace = true, default-features = false, features = ["alloc"] } +sha2 = { workspace = true, default-features = false } +base64 = { workspace = true, optional = true } +uuid = { workspace = true, optional = true } + +[dev-dependencies] +radroots-sql-core = { workspace = true, features = ["native"] } +radroots-tangle-db = { workspace = true } +serde_json = { workspace = true } diff --git a/tangle-events/src/canonical.rs b/tangle-events/src/canonical.rs @@ -0,0 +1,43 @@ +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; + +use serde::Serialize; +use serde_json::{Map, Value}; + +use crate::error::RadrootsTangleEventsError; + +pub fn canonical_json_string<T: Serialize>( + value: &T, +) -> Result<String, RadrootsTangleEventsError> { + let value = serde_json::to_value(value).map_err(|_| { + RadrootsTangleEventsError::InvalidData("canonical json serialization failed".to_string()) + })?; + let canonical = canonicalize_value(value); + serde_json::to_string(&canonical).map_err(|_| { + RadrootsTangleEventsError::InvalidData("canonical json encoding failed".to_string()) + }) +} + +fn canonicalize_value(value: Value) -> Value { + match value { + Value::Object(map) => canonicalize_object(map), + Value::Array(values) => { + let values = values + .into_iter() + .map(canonicalize_value) + .collect::<Vec<_>>(); + Value::Array(values) + } + other => other, + } +} + +fn canonicalize_object(map: Map<String, Value>) -> Value { + let mut entries = map.into_iter().collect::<Vec<_>>(); + entries.sort_by(|a, b| a.0.cmp(&b.0)); + let mut ordered = Map::new(); + for (key, value) in entries { + ordered.insert(key, canonicalize_value(value)); + } + Value::Object(ordered) +} diff --git a/tangle-events/src/emit.rs b/tangle-events/src/emit.rs @@ -0,0 +1,776 @@ +#[cfg(not(feature = "std"))] +use alloc::{collections::BTreeMap, string::String, vec, vec::Vec}; +#[cfg(feature = "std")] +use std::collections::BTreeMap; + +use radroots_events::farm::{ + RadrootsFarm, + RadrootsFarmLocation, + RadrootsFarmRef, + RadrootsGcsLocation, + RadrootsGeoJsonPoint, + RadrootsGeoJsonPolygon, +}; +use radroots_events::kinds::{KIND_FARM, KIND_LIST_SET_GENERIC, KIND_PLOT}; +use radroots_events::plot::RadrootsPlot; +use radroots_events::profile::{ + radroots_profile_type_from_tag_value, + radroots_profile_type_tag_value, + RadrootsProfile, + RadrootsProfileType, + RADROOTS_PROFILE_TYPE_TAG_KEY, +}; +use radroots_events_codec::farm::encode as farm_encode; +use radroots_events_codec::farm::list_sets as farm_list_sets; +use radroots_events_codec::list_set::encode as list_set_encode; +use radroots_events_codec::plot::encode as plot_encode; +use radroots_events_codec::wire::WireEventParts; +use radroots_sql_core::SqlExecutor; +use radroots_tangle_db_schema::farm::{ + Farm, + IFarmFindMany, + IFarmFindOne, + IFarmFindOneArgs, + IFarmFieldsFilter, +}; +use radroots_tangle_db_schema::farm_gcs_location::{ + FarmGcsLocation, + IFarmGcsLocationFindMany, + IFarmGcsLocationFieldsFilter, +}; +use radroots_tangle_db_schema::farm_member::{ + FarmMember, + IFarmMemberFindMany, + IFarmMemberFieldsFilter, +}; +use radroots_tangle_db_schema::farm_member_claim::{ + FarmMemberClaim, + IFarmMemberClaimFindMany, + IFarmMemberClaimFieldsFilter, +}; +use radroots_tangle_db_schema::farm_tag::{IFarmTagFindMany, IFarmTagFieldsFilter}; +use radroots_tangle_db_schema::gcs_location::{ + GcsLocation, + IGcsLocationFindOne, + IGcsLocationFindOneArgs, + GcsLocationQueryBindValues, +}; +use radroots_tangle_db_schema::nostr_profile::{ + INostrProfileFindOne, + INostrProfileFindOneArgs, + NostrProfileQueryBindValues, +}; +use radroots_tangle_db_schema::plot::{Plot, IPlotFindMany, IPlotFieldsFilter}; +use radroots_tangle_db_schema::plot_gcs_location::{ + PlotGcsLocation, + IPlotGcsLocationFindMany, + IPlotGcsLocationFieldsFilter, +}; +use radroots_tangle_db_schema::plot_tag::{IPlotTagFindMany, IPlotTagFieldsFilter}; +use radroots_tangle_db::{ + farm, + farm_gcs_location, + farm_member, + farm_member_claim, + farm_tag, + gcs_location, + nostr_profile, + plot, + plot_gcs_location, + plot_tag, +}; +use serde_json::Value; + +use crate::error::RadrootsTangleEventsError; +use crate::canonical::canonical_json_string; +use crate::geo::{geojson_point_from_lat_lng, geojson_polygon_circle_wgs84}; +use crate::types::{ + RADROOTS_TANGLE_TRANSFER_VERSION, + RadrootsTangleEventDraft, + RadrootsTangleFarmSelector, + RadrootsTangleSyncBundle, + RadrootsTangleSyncOptions, + RadrootsTangleSyncRequest, +}; + +const ROLE_PRIMARY: &str = "primary"; +const ROLE_MEMBER: &str = "member"; +const ROLE_OWNER: &str = "owner"; +const ROLE_WORKER: &str = "worker"; + +pub fn radroots_tangle_sync_all<E: SqlExecutor>( + exec: &E, + request: &RadrootsTangleSyncRequest, +) -> Result<RadrootsTangleSyncBundle, RadrootsTangleEventsError> { + radroots_tangle_sync_all_with_options(exec, &request.farm, request.options.as_ref()) +} + +pub fn radroots_tangle_sync_all_with_options<E: SqlExecutor>( + exec: &E, + farm_selector: &RadrootsTangleFarmSelector, + options: Option<&RadrootsTangleSyncOptions>, +) -> Result<RadrootsTangleSyncBundle, RadrootsTangleEventsError> { + let farm = resolve_farm(exec, farm_selector)?; + let include_profiles = options + .and_then(|opt| opt.include_profiles) + .unwrap_or(true); + let include_list_sets = options + .and_then(|opt| opt.include_list_sets) + .unwrap_or(true); + let include_claims = options + .and_then(|opt| opt.include_membership_claims) + .unwrap_or(true); + + let mut events = Vec::new(); + + if include_profiles { + let profiles = radroots_tangle_profile_events(exec, &farm)?; + events.extend(profiles); + } + + events.push(radroots_tangle_farm_event(exec, &farm)?); + + let plots = radroots_tangle_plot_events(exec, &farm)?; + events.extend(plots); + + if include_list_sets { + let list_sets = radroots_tangle_list_set_events(exec, &farm)?; + events.extend(list_sets); + } + + if include_claims { + let claims = radroots_tangle_membership_claim_events(exec, &farm.pubkey)?; + events.extend(claims); + } + + Ok(RadrootsTangleSyncBundle { + version: RADROOTS_TANGLE_TRANSFER_VERSION, + events, + }) +} + +pub fn radroots_tangle_profile_events<E: SqlExecutor>( + exec: &E, + farm: &Farm, +) -> Result<Vec<RadrootsTangleEventDraft>, RadrootsTangleEventsError> { + let mut pubkeys = collect_profile_pubkeys(exec, farm)?; + pubkeys.sort(); + pubkeys.dedup(); + + let mut events = Vec::new(); + for pubkey in pubkeys { + if let Some(profile) = load_profile(exec, &pubkey)? { + events.push(profile_event(&pubkey, profile)?); + } + } + Ok(events) +} + +pub fn radroots_tangle_farm_event<E: SqlExecutor>( + exec: &E, + farm: &Farm, +) -> Result<RadrootsTangleEventDraft, RadrootsTangleEventsError> { + let tags = collect_farm_tags(exec, &farm.id)?; + let location = load_farm_location(exec, farm)?; + let farm_event = RadrootsFarm { + d_tag: farm.d_tag.clone(), + name: farm.name.clone(), + about: farm.about.clone(), + website: farm.website.clone(), + picture: farm.picture.clone(), + banner: farm.banner.clone(), + location, + tags: if tags.is_empty() { None } else { Some(tags) }, + }; + let tags = farm_encode::farm_build_tags(&farm_event)?; + let content = canonical_json_string(&farm_event)?; + let parts = WireEventParts { + kind: KIND_FARM, + content, + tags, + }; + Ok(parts_to_draft(&farm.pubkey, parts)) +} + +pub fn radroots_tangle_plot_events<E: SqlExecutor>( + exec: &E, + farm: &Farm, +) -> Result<Vec<RadrootsTangleEventDraft>, RadrootsTangleEventsError> { + let plots = load_plots(exec, &farm.id)?; + let mut events = Vec::new(); + for plot_row in plots { + let tags = collect_plot_tags(exec, &plot_row.id)?; + let location = load_plot_location(exec, &plot_row)?; + let plot_event = RadrootsPlot { + d_tag: plot_row.d_tag.clone(), + farm: RadrootsFarmRef { + pubkey: farm.pubkey.clone(), + d_tag: farm.d_tag.clone(), + }, + name: plot_row.name.clone(), + about: plot_row.about.clone(), + location, + tags: if tags.is_empty() { None } else { Some(tags) }, + }; + let tags = plot_encode::plot_build_tags(&plot_event)?; + let content = canonical_json_string(&plot_event)?; + let parts = WireEventParts { + kind: KIND_PLOT, + content, + tags, + }; + events.push(parts_to_draft(&farm.pubkey, parts)); + } + Ok(events) +} + +pub fn radroots_tangle_list_set_events<E: SqlExecutor>( + exec: &E, + farm: &Farm, +) -> Result<Vec<RadrootsTangleEventDraft>, RadrootsTangleEventsError> { + let members = load_farm_members(exec, &farm.id)?; + let plots = load_plots(exec, &farm.id)?; + + let members_list = farm_list_sets::farm_members_list_set( + &farm.d_tag, + role_pubkeys(&members, ROLE_MEMBER), + )?; + let owners_list = farm_list_sets::farm_owners_list_set( + &farm.d_tag, + role_pubkeys(&members, ROLE_OWNER), + )?; + let workers_list = farm_list_sets::farm_workers_list_set( + &farm.d_tag, + role_pubkeys(&members, ROLE_WORKER), + )?; + + let plot_ids = sorted_plot_ids(&plots); + let plots_list = farm_list_sets::farm_plots_list_set( + &farm.d_tag, + &farm.pubkey, + plot_ids, + )?; + + let list_sets = [members_list, owners_list, workers_list, plots_list]; + let mut events = Vec::new(); + for list_set in list_sets { + let parts = list_set_encode::to_wire_parts_with_kind(&list_set, KIND_LIST_SET_GENERIC)?; + events.push(parts_to_draft(&farm.pubkey, parts)); + } + Ok(events) +} + +pub fn radroots_tangle_membership_claim_events<E: SqlExecutor>( + exec: &E, + farm_pubkey: &str, +) -> Result<Vec<RadrootsTangleEventDraft>, RadrootsTangleEventsError> { + let claims = load_member_claims(exec, farm_pubkey)?; + let mut by_member: BTreeMap<String, Vec<String>> = BTreeMap::new(); + for claim in claims { + by_member + .entry(claim.member_pubkey.clone()) + .or_default() + .push(claim.farm_pubkey.clone()); + } + + let mut events = Vec::new(); + for (member_pubkey, _) in by_member.iter() { + let all_claims = load_member_claims_for_member(exec, member_pubkey)?; + let mut farm_pubkeys = all_claims + .into_iter() + .map(|claim| claim.farm_pubkey) + .collect::<Vec<String>>(); + farm_pubkeys.sort(); + farm_pubkeys.dedup(); + let list_set = farm_list_sets::member_of_farms_list_set(farm_pubkeys)?; + let parts = list_set_encode::to_wire_parts_with_kind(&list_set, KIND_LIST_SET_GENERIC)?; + events.push(parts_to_draft(member_pubkey, parts)); + } + + Ok(events) +} + +fn resolve_farm<E: SqlExecutor>( + exec: &E, + selector: &RadrootsTangleFarmSelector, +) -> Result<Farm, RadrootsTangleEventsError> { + if let Some(id) = selector.id.as_ref().filter(|v| !v.trim().is_empty()) { + let result = farm::find_one( + exec, + &IFarmFindOne::On(IFarmFindOneArgs { + on: radroots_tangle_db_schema::farm::FarmQueryBindValues::Id { id: id.clone() }, + }), + )?; + return result + .result + .ok_or_else(|| RadrootsTangleEventsError::InvalidSelector(format!("farm not found: {id}"))); + } + + let d_tag = selector.d_tag.as_ref().map(|v| v.trim()).filter(|v| !v.is_empty()); + let pubkey = selector.pubkey.as_ref().map(|v| v.trim()).filter(|v| !v.is_empty()); + + let (d_tag, pubkey) = match (d_tag, pubkey) { + (Some(d_tag), Some(pubkey)) => (d_tag, pubkey), + _ => { + return Err(RadrootsTangleEventsError::InvalidSelector( + "farm selector requires id or (d_tag + pubkey)".to_string(), + )) + } + }; + + let filter = IFarmFieldsFilter { + id: None, + created_at: None, + updated_at: None, + d_tag: Some(d_tag.to_string()), + pubkey: Some(pubkey.to_string()), + name: None, + about: None, + website: None, + picture: None, + banner: None, + location_primary: None, + location_city: None, + location_region: None, + location_country: None, + }; + let result = farm::find_many(exec, &IFarmFindMany { filter: Some(filter) })?; + if result.results.len() == 1 { + return Ok(result.results.into_iter().next().expect("farm result")); + } + Err(RadrootsTangleEventsError::InvalidSelector( + "farm selector did not resolve to a single farm".to_string(), + )) +} + +fn collect_farm_tags<E: SqlExecutor>( + exec: &E, + farm_id: &str, +) -> Result<Vec<String>, RadrootsTangleEventsError> { + let filter = IFarmTagFieldsFilter { + id: None, + created_at: None, + updated_at: None, + farm_id: Some(farm_id.to_string()), + tag: None, + }; + let result = farm_tag::find_many(exec, &IFarmTagFindMany { filter: Some(filter) })?; + let mut tags = result.results.into_iter().map(|row| row.tag).collect::<Vec<_>>(); + tags.sort(); + tags.dedup(); + Ok(tags) +} + +fn collect_plot_tags<E: SqlExecutor>( + exec: &E, + plot_id: &str, +) -> Result<Vec<String>, RadrootsTangleEventsError> { + let filter = IPlotTagFieldsFilter { + id: None, + created_at: None, + updated_at: None, + plot_id: Some(plot_id.to_string()), + tag: None, + }; + let result = plot_tag::find_many(exec, &IPlotTagFindMany { filter: Some(filter) })?; + let mut tags = result.results.into_iter().map(|row| row.tag).collect::<Vec<_>>(); + tags.sort(); + tags.dedup(); + Ok(tags) +} + +fn load_farm_members<E: SqlExecutor>( + exec: &E, + farm_id: &str, +) -> Result<Vec<FarmMember>, RadrootsTangleEventsError> { + let filter = IFarmMemberFieldsFilter { + id: None, + created_at: None, + updated_at: None, + farm_id: Some(farm_id.to_string()), + member_pubkey: None, + role: None, + }; + let result = farm_member::find_many(exec, &IFarmMemberFindMany { filter: Some(filter) })?; + Ok(result.results) +} + +fn role_pubkeys(members: &[FarmMember], role: &str) -> Vec<String> { + let mut values = members + .iter() + .filter(|member| member.role == role) + .map(|member| member.member_pubkey.clone()) + .collect::<Vec<_>>(); + values.sort(); + values.dedup(); + values +} + +fn sorted_plot_ids(plots: &[Plot]) -> Vec<String> { + let mut ids = plots.iter().map(|plot| plot.d_tag.clone()).collect::<Vec<_>>(); + ids.sort(); + ids.dedup(); + ids +} + +fn load_plots<E: SqlExecutor>(exec: &E, farm_id: &str) -> Result<Vec<Plot>, RadrootsTangleEventsError> { + let filter = IPlotFieldsFilter { + id: None, + created_at: None, + updated_at: None, + d_tag: None, + farm_id: Some(farm_id.to_string()), + name: None, + about: None, + location_primary: None, + location_city: None, + location_region: None, + location_country: None, + }; + let result = plot::find_many(exec, &IPlotFindMany { filter: Some(filter) })?; + let mut plots = result.results; + plots.sort_by(|a, b| a.d_tag.cmp(&b.d_tag)); + Ok(plots) +} + +fn load_farm_location<E: SqlExecutor>( + exec: &E, + farm: &Farm, +) -> Result<Option<RadrootsFarmLocation>, RadrootsTangleEventsError> { + let location = load_gcs_location_for_farm(exec, &farm.id)?; + Ok(location.map(|gcs| RadrootsFarmLocation { + primary: farm.location_primary.clone(), + city: farm.location_city.clone(), + region: farm.location_region.clone(), + country: farm.location_country.clone(), + gcs, + })) +} + +fn load_plot_location<E: SqlExecutor>( + exec: &E, + plot: &Plot, +) -> Result<Option<radroots_events::plot::RadrootsPlotLocation>, RadrootsTangleEventsError> { + let location = load_gcs_location_for_plot(exec, &plot.id)?; + Ok(location.map(|gcs| radroots_events::plot::RadrootsPlotLocation { + primary: plot.location_primary.clone(), + city: plot.location_city.clone(), + region: plot.location_region.clone(), + country: plot.location_country.clone(), + gcs, + })) +} + +fn load_gcs_location_for_farm<E: SqlExecutor>( + exec: &E, + farm_id: &str, +) -> Result<Option<RadrootsGcsLocation>, RadrootsTangleEventsError> { + let primary = load_relation_by_role( + exec, + farm_id, + ROLE_PRIMARY, + RelationType::Farm, + )?; + match primary { + Some(gcs) => Ok(Some(gcs)), + None => load_relation_by_role(exec, farm_id, "", RelationType::Farm), + } +} + +fn load_gcs_location_for_plot<E: SqlExecutor>( + exec: &E, + plot_id: &str, +) -> Result<Option<RadrootsGcsLocation>, RadrootsTangleEventsError> { + let primary = load_relation_by_role( + exec, + plot_id, + ROLE_PRIMARY, + RelationType::Plot, + )?; + match primary { + Some(gcs) => Ok(Some(gcs)), + None => load_relation_by_role(exec, plot_id, "", RelationType::Plot), + } +} + +enum RelationType { + Farm, + Plot, +} + +fn load_relation_by_role<E: SqlExecutor>( + exec: &E, + id: &str, + role: &str, + relation: RelationType, +) -> Result<Option<RadrootsGcsLocation>, RadrootsTangleEventsError> { + let mut rels = match relation { + RelationType::Farm => { + let filter = IFarmGcsLocationFieldsFilter { + id: None, + created_at: None, + updated_at: None, + farm_id: Some(id.to_string()), + gcs_location_id: None, + role: if role.is_empty() { None } else { Some(role.to_string()) }, + }; + let result = farm_gcs_location::find_many( + exec, + &IFarmGcsLocationFindMany { filter: Some(filter) }, + )?; + result.results.into_iter().map(RelationRow::Farm).collect::<Vec<_>>() + } + RelationType::Plot => { + let filter = IPlotGcsLocationFieldsFilter { + id: None, + created_at: None, + updated_at: None, + plot_id: Some(id.to_string()), + gcs_location_id: None, + role: if role.is_empty() { None } else { Some(role.to_string()) }, + }; + let result = plot_gcs_location::find_many( + exec, + &IPlotGcsLocationFindMany { filter: Some(filter) }, + )?; + result.results.into_iter().map(RelationRow::Plot).collect::<Vec<_>>() + } + }; + + if rels.is_empty() { + return Ok(None); + } + + rels.sort_by(|a, b| { + let rank = location_role_rank(a.role()).cmp(&location_role_rank(b.role())); + rank.then_with(|| a.gcs_location_id().cmp(b.gcs_location_id())) + }); + let gcs_id = rels[0].gcs_location_id().to_string(); + let gcs = gcs_location::find_one( + exec, + &IGcsLocationFindOne::On(IGcsLocationFindOneArgs { + on: GcsLocationQueryBindValues::Id { id: gcs_id }, + }), + )? + .result + .ok_or_else(|| RadrootsTangleEventsError::InvalidData("gcs_location not found".to_string()))?; + Ok(Some(gcs_location_to_event(&gcs)?)) +} + +enum RelationRow { + Farm(FarmGcsLocation), + Plot(PlotGcsLocation), +} + +impl RelationRow { + fn gcs_location_id(&self) -> &str { + match self { + Self::Farm(row) => row.gcs_location_id.as_str(), + Self::Plot(row) => row.gcs_location_id.as_str(), + } + } + + fn role(&self) -> &str { + match self { + Self::Farm(row) => row.role.as_str(), + Self::Plot(row) => row.role.as_str(), + } + } +} + +fn location_role_rank(role: &str) -> u8 { + if role == ROLE_PRIMARY { + 0 + } else { + 1 + } +} + +fn gcs_location_to_event(gcs: &GcsLocation) -> Result<RadrootsGcsLocation, RadrootsTangleEventsError> { + let point = parse_point(&gcs.point, gcs.lat, gcs.lng); + let polygon = parse_polygon(&gcs.polygon, gcs.lat, gcs.lng); + Ok(RadrootsGcsLocation { + lat: gcs.lat, + lng: gcs.lng, + geohash: gcs.geohash.clone(), + point, + polygon, + accuracy: gcs.accuracy, + altitude: gcs.altitude, + tag_0: gcs.tag_0.clone(), + label: gcs.label.clone(), + area: gcs.area, + elevation: gcs.elevation, + soil: gcs.soil.clone(), + climate: gcs.climate.clone(), + gc_id: gcs.gc_id.clone(), + gc_name: gcs.gc_name.clone(), + gc_admin1_id: gcs.gc_admin1_id.clone(), + gc_admin1_name: gcs.gc_admin1_name.clone(), + gc_country_id: gcs.gc_country_id.clone(), + gc_country_name: gcs.gc_country_name.clone(), + }) +} + +fn parse_point(value: &str, lat: f64, lng: f64) -> RadrootsGeoJsonPoint { + if !value.trim().is_empty() { + if let Ok(parsed) = serde_json::from_str::<RadrootsGeoJsonPoint>(value) { + return parsed; + } + } + geojson_point_from_lat_lng(lat, lng) +} + +fn parse_polygon(value: &str, lat: f64, lng: f64) -> RadrootsGeoJsonPolygon { + if !value.trim().is_empty() { + if let Ok(parsed) = serde_json::from_str::<RadrootsGeoJsonPolygon>(value) { + if !parsed.coordinates.is_empty() && !parsed.coordinates[0].is_empty() { + return parsed; + } + } + } + geojson_polygon_circle_wgs84(lat, lng, 100.0, 64) +} + +fn load_profile<E: SqlExecutor>( + exec: &E, + pubkey: &str, +) -> Result<Option<radroots_tangle_db_schema::nostr_profile::NostrProfile>, RadrootsTangleEventsError> { + let result = nostr_profile::find_one( + exec, + &INostrProfileFindOne::On(INostrProfileFindOneArgs { + on: NostrProfileQueryBindValues::PublicKey { + public_key: pubkey.to_string(), + }, + }), + )?; + Ok(result.result) +} + +fn profile_event( + pubkey: &str, + profile: radroots_tangle_db_schema::nostr_profile::NostrProfile, +) -> Result<RadrootsTangleEventDraft, RadrootsTangleEventsError> { + let profile_type = match profile.profile_type.as_str() { + "individual" => Some(RadrootsProfileType::Individual), + "farm" => Some(RadrootsProfileType::Farm), + other => radroots_profile_type_from_tag_value(other), + }; + let profile_event = RadrootsProfile { + name: profile.name, + display_name: profile.display_name, + nip05: profile.nip05, + about: profile.about, + website: profile.website, + picture: profile.picture, + banner: profile.banner, + lud06: profile.lud06, + lud16: profile.lud16, + bot: None, + }; + let content = serialize_profile_content(&profile_event)?; + let mut tags = Vec::new(); + if let Some(profile_type) = profile_type { + let mut tag = Vec::with_capacity(2); + tag.push(RADROOTS_PROFILE_TYPE_TAG_KEY.to_string()); + tag.push(radroots_profile_type_tag_value(profile_type).to_string()); + tags.push(tag); + } + Ok(RadrootsTangleEventDraft { + kind: radroots_events::kinds::KIND_PROFILE, + author: pubkey.to_string(), + content, + tags, + }) +} + +fn serialize_profile_content(profile: &RadrootsProfile) -> Result<String, RadrootsTangleEventsError> { + let mut obj = serde_json::Map::new(); + obj.insert("name".to_string(), Value::from(profile.name.clone())); + if let Some(value) = profile.display_name.as_ref() { + obj.insert("display_name".to_string(), Value::from(value.clone())); + } + if let Some(value) = profile.nip05.as_ref() { + obj.insert("nip05".to_string(), Value::from(value.clone())); + } + if let Some(value) = profile.about.as_ref() { + obj.insert("about".to_string(), Value::from(value.clone())); + } + if let Some(value) = profile.website.as_ref() { + obj.insert("website".to_string(), Value::from(value.clone())); + } + if let Some(value) = profile.picture.as_ref() { + obj.insert("picture".to_string(), Value::from(value.clone())); + } + if let Some(value) = profile.banner.as_ref() { + obj.insert("banner".to_string(), Value::from(value.clone())); + } + if let Some(value) = profile.lud06.as_ref() { + obj.insert("lud06".to_string(), Value::from(value.clone())); + } + if let Some(value) = profile.lud16.as_ref() { + obj.insert("lud16".to_string(), Value::from(value.clone())); + } + canonical_json_string(&Value::Object(obj)) +} + +fn collect_member_pubkeys<E: SqlExecutor>( + exec: &E, + farm_id: &str, +) -> Result<Vec<String>, RadrootsTangleEventsError> { + let members = load_farm_members(exec, farm_id)?; + let mut pubkeys = members.into_iter().map(|row| row.member_pubkey).collect::<Vec<_>>(); + pubkeys.sort(); + pubkeys.dedup(); + Ok(pubkeys) +} + +fn collect_profile_pubkeys<E: SqlExecutor>( + exec: &E, + farm: &Farm, +) -> Result<Vec<String>, RadrootsTangleEventsError> { + let mut pubkeys = collect_member_pubkeys(exec, &farm.id)?; + let claims = load_member_claims(exec, &farm.pubkey)?; + pubkeys.extend(claims.into_iter().map(|claim| claim.member_pubkey)); + pubkeys.push(farm.pubkey.clone()); + Ok(pubkeys) +} + +fn load_member_claims<E: SqlExecutor>( + exec: &E, + farm_pubkey: &str, +) -> Result<Vec<FarmMemberClaim>, RadrootsTangleEventsError> { + let filter = IFarmMemberClaimFieldsFilter { + id: None, + created_at: None, + updated_at: None, + member_pubkey: None, + farm_pubkey: Some(farm_pubkey.to_string()), + }; + let result = farm_member_claim::find_many(exec, &IFarmMemberClaimFindMany { filter: Some(filter) })?; + Ok(result.results) +} + +fn load_member_claims_for_member<E: SqlExecutor>( + exec: &E, + member_pubkey: &str, +) -> Result<Vec<FarmMemberClaim>, RadrootsTangleEventsError> { + let filter = IFarmMemberClaimFieldsFilter { + id: None, + created_at: None, + updated_at: None, + member_pubkey: Some(member_pubkey.to_string()), + farm_pubkey: None, + }; + let result = farm_member_claim::find_many(exec, &IFarmMemberClaimFindMany { filter: Some(filter) })?; + Ok(result.results) +} + +fn parts_to_draft(author: &str, parts: WireEventParts) -> RadrootsTangleEventDraft { + RadrootsTangleEventDraft { + kind: parts.kind, + author: author.to_string(), + content: parts.content, + tags: parts.tags, + } +} diff --git a/tangle-events/src/error.rs b/tangle-events/src/error.rs @@ -0,0 +1,55 @@ +#[cfg(not(feature = "std"))] +use alloc::string::String; + +use core::fmt; + +use radroots_events_codec::error::{EventEncodeError, EventParseError}; +use radroots_sql_core::error::SqlError; +use radroots_types::types::IError; + +pub enum RadrootsTangleEventsError { + Sql(IError<SqlError>), + Encode(EventEncodeError), + Parse(EventParseError), + InvalidSelector(String), + InvalidData(String), +} + +impl fmt::Debug for RadrootsTangleEventsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for RadrootsTangleEventsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Sql(err) => write!(f, "tangle_events.sql: {}", err.err.to_string()), + Self::Encode(err) => write!(f, "tangle_events.encode: {err}"), + Self::Parse(err) => write!(f, "tangle_events.parse: {err}"), + Self::InvalidSelector(msg) => write!(f, "tangle_events.selector: {msg}"), + Self::InvalidData(msg) => write!(f, "tangle_events.data: {msg}"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for RadrootsTangleEventsError {} + +impl From<IError<SqlError>> for RadrootsTangleEventsError { + fn from(err: IError<SqlError>) -> Self { + Self::Sql(err) + } +} + +impl From<EventEncodeError> for RadrootsTangleEventsError { + fn from(err: EventEncodeError) -> Self { + Self::Encode(err) + } +} + +impl From<EventParseError> for RadrootsTangleEventsError { + fn from(err: EventParseError) -> Self { + Self::Parse(err) + } +} diff --git a/tangle-events/src/geo.rs b/tangle-events/src/geo.rs @@ -0,0 +1,67 @@ +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; + +use radroots_events::farm::{RadrootsGeoJsonPoint, RadrootsGeoJsonPolygon}; + +const EARTH_RADIUS_M: f64 = 6_378_137.0; + +pub fn geojson_point_from_lat_lng(lat: f64, lng: f64) -> RadrootsGeoJsonPoint { + RadrootsGeoJsonPoint { + r#type: String::from("Point"), + coordinates: [lng, lat], + } +} + +pub fn geojson_polygon_circle_wgs84( + lat: f64, + lng: f64, + radius_m: f64, + steps: usize, +) -> RadrootsGeoJsonPolygon { + let steps = if steps < 3 { 3 } else { steps }; + let lat1 = lat.to_radians(); + let lng1 = lng.to_radians(); + let angular = radius_m / EARTH_RADIUS_M; + let sin_lat1 = lat1.sin(); + let cos_lat1 = lat1.cos(); + let sin_ang = angular.sin(); + let cos_ang = angular.cos(); + + let mut ring = Vec::with_capacity(steps + 1); + for idx in 0..=steps { + let bearing = (idx as f64) * core::f64::consts::TAU / (steps as f64); + let sin_bearing = bearing.sin(); + let cos_bearing = bearing.cos(); + + let sin_lat2 = sin_lat1 * cos_ang + cos_lat1 * sin_ang * cos_bearing; + let lat2 = sin_lat2.asin(); + let y = sin_bearing * sin_ang * cos_lat1; + let x = cos_ang - sin_lat1 * sin_lat2; + let lng2 = lng1 + y.atan2(x); + + let lat_deg = round_coord(lat2.to_degrees()); + let lng_deg = round_coord(normalize_lng(lng2.to_degrees())); + ring.push([lng_deg, lat_deg]); + } + + RadrootsGeoJsonPolygon { + r#type: String::from("Polygon"), + coordinates: vec![ring], + } +} + +fn round_coord(value: f64) -> f64 { + let scale = 1_000_000.0; + (value * scale).round() / scale +} + +fn normalize_lng(value: f64) -> f64 { + let mut lng = value; + while lng > 180.0 { + lng -= 360.0; + } + while lng < -180.0 { + lng += 360.0; + } + lng +} diff --git a/tangle-events/src/ingest.rs b/tangle-events/src/ingest.rs @@ -0,0 +1,992 @@ +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; + +#[cfg(feature = "std")] +use base64::engine::general_purpose::URL_SAFE_NO_PAD; +#[cfg(feature = "std")] +use base64::Engine; + +use radroots_events::kinds::{ + is_nip51_list_set_kind, + KIND_FARM, + KIND_PLOT, + KIND_PROFILE, +}; +use radroots_events::RadrootsNostrEvent; +use radroots_events_codec::farm::decode as farm_decode; +use radroots_events_codec::list_set::decode as list_set_decode; +use radroots_events_codec::plot::decode as plot_decode; +use radroots_events_codec::profile::decode as profile_decode; +use radroots_sql_core::SqlExecutor; +use radroots_tangle_db_schema::farm::{ + FarmQueryBindValues, + IFarmFields, + IFarmFieldsFilter, + IFarmFindMany, + IFarmUpdate, + IFarmFieldsPartial, +}; +use radroots_tangle_db_schema::farm_gcs_location::{ + IFarmGcsLocationFields, + IFarmGcsLocationFindMany, + IFarmGcsLocationFieldsFilter, + IFarmGcsLocationDelete, + IFarmGcsLocationFindOneArgs, + FarmGcsLocationQueryBindValues, +}; +use radroots_tangle_db_schema::farm_member::{ + IFarmMemberFields, + IFarmMemberFindMany, + IFarmMemberFieldsFilter, + IFarmMemberDelete, + IFarmMemberFindOneArgs, + FarmMemberQueryBindValues, +}; +use radroots_tangle_db_schema::farm_member_claim::{ + IFarmMemberClaimFields, + IFarmMemberClaimFindMany, + IFarmMemberClaimFieldsFilter, + IFarmMemberClaimDelete, + IFarmMemberClaimFindOneArgs, + FarmMemberClaimQueryBindValues, +}; +use radroots_tangle_db_schema::farm_tag::{ + IFarmTagFields, + IFarmTagFindMany, + IFarmTagFieldsFilter, + IFarmTagDelete, + IFarmTagFindOneArgs, + FarmTagQueryBindValues, +}; +use radroots_tangle_db_schema::gcs_location::{ + IGcsLocationFields, +}; +use radroots_tangle_db_schema::nostr_event_state::{ + INostrEventStateFields, + INostrEventStateFindOne, + INostrEventStateFindOneArgs, + INostrEventStateUpdate, + INostrEventStateFieldsPartial, + NostrEventStateQueryBindValues, +}; +use radroots_tangle_db_schema::nostr_profile::{ + INostrProfileFields, + INostrProfileFindOne, + INostrProfileFindOneArgs, + INostrProfileUpdate, + INostrProfileFieldsPartial, + NostrProfileQueryBindValues, +}; +use radroots_tangle_db_schema::plot::{ + IPlotFields, + IPlotFieldsFilter, + IPlotFindMany, + IPlotUpdate, + PlotQueryBindValues, + IPlotFieldsPartial, +}; +use radroots_tangle_db_schema::plot_gcs_location::{ + IPlotGcsLocationFields, + IPlotGcsLocationFindMany, + IPlotGcsLocationFieldsFilter, + IPlotGcsLocationDelete, + IPlotGcsLocationFindOneArgs, + PlotGcsLocationQueryBindValues, +}; +use radroots_tangle_db_schema::plot_tag::{ + IPlotTagFields, + IPlotTagFindMany, + IPlotTagFieldsFilter, + IPlotTagDelete, + IPlotTagFindOneArgs, + PlotTagQueryBindValues, +}; +use radroots_tangle_db::{ + farm, + farm_gcs_location, + farm_member, + farm_member_claim, + farm_tag, + gcs_location, + nostr_event_state, + nostr_profile, + plot, + plot_gcs_location, + plot_tag, +}; +use serde_json::Value; +use sha2::{Digest, Sha256}; + +use crate::error::RadrootsTangleEventsError; +const ROLE_PRIMARY: &str = "primary"; +const ROLE_MEMBER: &str = "member"; +const ROLE_OWNER: &str = "owner"; +const ROLE_WORKER: &str = "worker"; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RadrootsTangleIngestOutcome { + Applied, + Skipped, +} + +pub trait RadrootsTangleIdFactory { + fn new_d_tag(&self) -> String; +} + +#[cfg(feature = "std")] +pub struct RadrootsTangleDefaultIdFactory; + +#[cfg(feature = "std")] +impl RadrootsTangleIdFactory for RadrootsTangleDefaultIdFactory { + fn new_d_tag(&self) -> String { + let uuid = uuid::Uuid::now_v7(); + let bytes = uuid.as_bytes(); + URL_SAFE_NO_PAD.encode(bytes) + } +} + +#[cfg(feature = "std")] +pub fn radroots_tangle_ingest_event<E: SqlExecutor>( + exec: &E, + event: &RadrootsNostrEvent, +) -> Result<RadrootsTangleIngestOutcome, RadrootsTangleEventsError> { + radroots_tangle_ingest_event_with_factory(exec, event, &RadrootsTangleDefaultIdFactory) +} + +pub fn radroots_tangle_ingest_event_with_factory<E: SqlExecutor, F: RadrootsTangleIdFactory>( + exec: &E, + event: &RadrootsNostrEvent, + factory: &F, +) -> Result<RadrootsTangleIngestOutcome, RadrootsTangleEventsError> { + exec.begin().map_err(|e| RadrootsTangleEventsError::from(radroots_types::types::IError::from(e)))?; + + let outcome = match ingest_event_inner(exec, event, factory) { + Ok(outcome) => { + exec.commit().map_err(|e| RadrootsTangleEventsError::from(radroots_types::types::IError::from(e)))?; + Ok(outcome) + } + Err(err) => { + let _ = exec.rollback(); + Err(err) + } + }; + + outcome +} + +fn ingest_event_inner<E: SqlExecutor, F: RadrootsTangleIdFactory>( + exec: &E, + event: &RadrootsNostrEvent, + factory: &F, +) -> Result<RadrootsTangleIngestOutcome, RadrootsTangleEventsError> { + match event.kind { + KIND_PROFILE => ingest_profile_event(exec, event), + KIND_FARM => ingest_farm_event(exec, event, factory), + KIND_PLOT => ingest_plot_event(exec, event, factory), + kind if is_nip51_list_set_kind(kind) => ingest_list_set_event(exec, event), + _ => Err(RadrootsTangleEventsError::InvalidData(format!( + "unsupported kind {}", + event.kind + ))), + } +} + +fn ingest_profile_event<E: SqlExecutor>( + exec: &E, + event: &RadrootsNostrEvent, +) -> Result<RadrootsTangleIngestOutcome, RadrootsTangleEventsError> { + let metadata = profile_decode::metadata_from_event( + event.id.clone(), + event.author.clone(), + event.created_at, + event.kind, + event.content.clone(), + event.tags.clone(), + )?; + let profile_type = metadata + .profile_type + .ok_or_else(|| RadrootsTangleEventsError::InvalidData("profile_type required".to_string()))?; + + let d_tag = "".to_string(); + let decision = event_state_decision(exec, event, &d_tag)?; + if !decision.apply { + return Ok(RadrootsTangleIngestOutcome::Skipped); + } + + let profile_type = match profile_type { + radroots_events::profile::RadrootsProfileType::Individual => "individual", + radroots_events::profile::RadrootsProfileType::Farm => "farm", + }; + + let existing = nostr_profile::find_one( + exec, + &INostrProfileFindOne::On(INostrProfileFindOneArgs { + on: NostrProfileQueryBindValues::PublicKey { + public_key: metadata.author.clone(), + }, + }), + )? + .result; + + match existing { + Some(profile) => { + let fields = INostrProfileFieldsPartial { + public_key: None, + profile_type: Some(Value::from(profile_type)), + name: Some(Value::from(metadata.profile.name)), + display_name: to_value_opt(metadata.profile.display_name), + about: to_value_opt(metadata.profile.about), + website: to_value_opt(metadata.profile.website), + picture: to_value_opt(metadata.profile.picture), + banner: to_value_opt(metadata.profile.banner), + nip05: to_value_opt(metadata.profile.nip05), + lud06: to_value_opt(metadata.profile.lud06), + lud16: to_value_opt(metadata.profile.lud16), + }; + let _ = nostr_profile::update( + exec, + &INostrProfileUpdate { + on: NostrProfileQueryBindValues::Id { id: profile.id }, + fields, + }, + )?; + } + None => { + let fields = INostrProfileFields { + public_key: metadata.author.clone(), + profile_type: profile_type.to_string(), + name: metadata.profile.name, + display_name: metadata.profile.display_name, + about: metadata.profile.about, + website: metadata.profile.website, + picture: metadata.profile.picture, + banner: metadata.profile.banner, + nip05: metadata.profile.nip05, + lud06: metadata.profile.lud06, + lud16: metadata.profile.lud16, + }; + let _ = nostr_profile::create(exec, &fields)?; + } + } + + radroots_tangle_ingest_event_state(exec, event, &d_tag, &decision.content_hash)?; + Ok(RadrootsTangleIngestOutcome::Applied) +} + +fn ingest_farm_event<E: SqlExecutor, F: RadrootsTangleIdFactory>( + exec: &E, + event: &RadrootsNostrEvent, + factory: &F, +) -> Result<RadrootsTangleIngestOutcome, RadrootsTangleEventsError> { + let farm = farm_decode::farm_from_event(event.kind, &event.tags, &event.content)?; + let decision = event_state_decision(exec, event, &farm.d_tag)?; + if !decision.apply { + return Ok(RadrootsTangleIngestOutcome::Skipped); + } + + let filter = IFarmFieldsFilter { + id: None, + created_at: None, + updated_at: None, + d_tag: Some(farm.d_tag.clone()), + pubkey: Some(event.author.clone()), + name: None, + about: None, + website: None, + picture: None, + banner: None, + location_primary: None, + location_city: None, + location_region: None, + location_country: None, + }; + let existing = farm::find_many(exec, &IFarmFindMany { filter: Some(filter) })?; + let location = farm.location.clone(); + let (location_primary, location_city, location_region, location_country) = + unpack_farm_location_strings(location.as_ref()); + let farm_id = if let Some(row) = existing.results.get(0) { + let fields = IFarmFieldsPartial { + d_tag: Some(Value::from(farm.d_tag.clone())), + pubkey: Some(Value::from(event.author.clone())), + name: Some(Value::from(farm.name.clone())), + about: to_value_opt(farm.about.clone()), + website: to_value_opt(farm.website.clone()), + picture: to_value_opt(farm.picture.clone()), + banner: to_value_opt(farm.banner.clone()), + location_primary: to_value_opt(location_primary), + location_city: to_value_opt(location_city), + location_region: to_value_opt(location_region), + location_country: to_value_opt(location_country), + }; + let _ = farm::update( + exec, + &IFarmUpdate { + on: FarmQueryBindValues::Id { id: row.id.clone() }, + fields, + }, + )?; + row.id.clone() + } else { + let fields = IFarmFields { + d_tag: farm.d_tag.clone(), + pubkey: event.author.clone(), + name: farm.name.clone(), + about: farm.about.clone(), + website: farm.website.clone(), + picture: farm.picture.clone(), + banner: farm.banner.clone(), + location_primary, + location_city, + location_region, + location_country, + }; + farm::create(exec, &fields)?.result.id + }; + + upsert_farm_tags(exec, &farm_id, farm.tags)?; + upsert_farm_location(exec, &farm_id, location, factory)?; + + radroots_tangle_ingest_event_state(exec, event, &farm.d_tag, &decision.content_hash)?; + Ok(RadrootsTangleIngestOutcome::Applied) +} + +fn ingest_plot_event<E: SqlExecutor, F: RadrootsTangleIdFactory>( + exec: &E, + event: &RadrootsNostrEvent, + factory: &F, +) -> Result<RadrootsTangleIngestOutcome, RadrootsTangleEventsError> { + let plot = plot_decode::plot_from_event(event.kind, &event.tags, &event.content)?; + let decision = event_state_decision(exec, event, &plot.d_tag)?; + if !decision.apply { + return Ok(RadrootsTangleIngestOutcome::Skipped); + } + + let farm = find_farm_by_ref(exec, &plot.farm.pubkey, &plot.farm.d_tag)?; + let filter = IPlotFieldsFilter { + id: None, + created_at: None, + updated_at: None, + d_tag: Some(plot.d_tag.clone()), + farm_id: Some(farm.id.clone()), + name: None, + about: None, + location_primary: None, + location_city: None, + location_region: None, + location_country: None, + }; + let existing = plot::find_many(exec, &IPlotFindMany { filter: Some(filter) })?; + let location = plot.location.clone(); + let (location_primary, location_city, location_region, location_country) = + unpack_plot_location_strings(location.as_ref()); + let plot_id = if let Some(row) = existing.results.get(0) { + let fields = IPlotFieldsPartial { + d_tag: Some(Value::from(plot.d_tag.clone())), + farm_id: Some(Value::from(farm.id.clone())), + name: Some(Value::from(plot.name.clone())), + about: to_value_opt(plot.about.clone()), + location_primary: to_value_opt(location_primary), + location_city: to_value_opt(location_city), + location_region: to_value_opt(location_region), + location_country: to_value_opt(location_country), + }; + let _ = plot::update( + exec, + &IPlotUpdate { + on: PlotQueryBindValues::Id { id: row.id.clone() }, + fields, + }, + )?; + row.id.clone() + } else { + let fields = IPlotFields { + d_tag: plot.d_tag.clone(), + farm_id: farm.id.clone(), + name: plot.name.clone(), + about: plot.about.clone(), + location_primary, + location_city, + location_region, + location_country, + }; + plot::create(exec, &fields)?.result.id + }; + + upsert_plot_tags(exec, &plot_id, plot.tags)?; + upsert_plot_location(exec, &plot_id, location, factory)?; + + radroots_tangle_ingest_event_state(exec, event, &plot.d_tag, &decision.content_hash)?; + Ok(RadrootsTangleIngestOutcome::Applied) +} + +fn ingest_list_set_event<E: SqlExecutor>( + exec: &E, + event: &RadrootsNostrEvent, +) -> Result<RadrootsTangleIngestOutcome, RadrootsTangleEventsError> { + if event.kind != radroots_events::kinds::KIND_LIST_SET_GENERIC { + return Ok(RadrootsTangleIngestOutcome::Skipped); + } + let list_set = list_set_decode::list_set_from_tags(event.kind, event.content.clone(), &event.tags)?; + + if list_set.title.is_some() || list_set.description.is_some() || list_set.image.is_some() { + return Err(RadrootsTangleEventsError::InvalidData( + "domain:farm list sets must omit metadata".to_string(), + )); + } + if !list_set.content.is_empty() { + return Err(RadrootsTangleEventsError::InvalidData( + "domain:farm list sets must not include content".to_string(), + )); + } + + let d_tag = list_set.d_tag.clone(); + let decision = event_state_decision(exec, event, &d_tag)?; + if !decision.apply { + return Ok(RadrootsTangleIngestOutcome::Skipped); + } + + if d_tag == "member_of.farms" { + ensure_list_set_entries_tag(&list_set, "p", "member_of.farms")?; + upsert_member_claims(exec, &event.author, &list_set)?; + radroots_tangle_ingest_event_state(exec, event, &d_tag, &decision.content_hash)?; + return Ok(RadrootsTangleIngestOutcome::Applied); + } + + if let Some((farm_d_tag, role)) = parse_farm_list_set_d_tag(&d_tag) { + if role == ListSetRole::Plots { + ensure_list_set_entries_tag(&list_set, "a", "farm plots")?; + radroots_tangle_ingest_event_state(exec, event, &d_tag, &decision.content_hash)?; + return Ok(RadrootsTangleIngestOutcome::Applied); + } + ensure_list_set_entries_tag(&list_set, "p", "farm members")?; + let farm = find_farm_by_ref(exec, &event.author, &farm_d_tag)?; + upsert_farm_members(exec, &farm.id, role, &list_set)?; + radroots_tangle_ingest_event_state(exec, event, &d_tag, &decision.content_hash)?; + return Ok(RadrootsTangleIngestOutcome::Applied); + } + + Err(RadrootsTangleEventsError::InvalidData( + "unsupported list set d_tag".to_string(), + )) +} + +pub fn radroots_tangle_ingest_event_state<E: SqlExecutor>( + exec: &E, + event: &RadrootsNostrEvent, + d_tag: &str, + content_hash: &str, +) -> Result<(), RadrootsTangleEventsError> { + let key = event_state_key(event.kind, &event.author, d_tag); + let existing = nostr_event_state::find_one( + exec, + &INostrEventStateFindOne::On(INostrEventStateFindOneArgs { + on: NostrEventStateQueryBindValues::Key { key: key.clone() }, + }), + )? + .result; + + match existing { + Some(state) => { + let fields = INostrEventStateFieldsPartial { + key: None, + kind: None, + pubkey: None, + d_tag: None, + last_event_id: Some(Value::from(event.id.clone())), + last_created_at: Some(Value::from(event.created_at)), + content_hash: Some(Value::from(content_hash.to_string())), + }; + let _ = nostr_event_state::update( + exec, + &INostrEventStateUpdate { + on: NostrEventStateQueryBindValues::Id { id: state.id }, + fields, + }, + )?; + } + None => { + let fields = INostrEventStateFields { + key, + kind: event.kind, + pubkey: event.author.clone(), + d_tag: d_tag.to_string(), + last_event_id: event.id.clone(), + last_created_at: event.created_at, + content_hash: content_hash.to_string(), + }; + let _ = nostr_event_state::create(exec, &fields)?; + } + } + + Ok(()) +} + +fn event_state_decision<E: SqlExecutor>( + exec: &E, + event: &RadrootsNostrEvent, + d_tag: &str, +) -> Result<EventStateDecision, RadrootsTangleEventsError> { + let key = event_state_key(event.kind, &event.author, d_tag); + let content_hash = hash_content(&event.content, &event.tags)?; + let existing = nostr_event_state::find_one( + exec, + &INostrEventStateFindOne::On(INostrEventStateFindOneArgs { + on: NostrEventStateQueryBindValues::Key { key }, + }), + )? + .result; + + if let Some(state) = existing { + if event.created_at < state.last_created_at { + return Ok(EventStateDecision { apply: false, content_hash }); + } + if event.created_at == state.last_created_at && content_hash == state.content_hash { + return Ok(EventStateDecision { apply: false, content_hash }); + } + } + + Ok(EventStateDecision { apply: true, content_hash }) +} + +fn event_state_key(kind: u32, pubkey: &str, d_tag: &str) -> String { + format!("{kind}:{pubkey}:{d_tag}") +} + +fn hash_content(content: &str, tags: &[Vec<String>]) -> Result<String, RadrootsTangleEventsError> { + let tags_json = serde_json::to_string(tags) + .map_err(|_| RadrootsTangleEventsError::InvalidData("tags serialization failed".to_string()))?; + let mut hasher = Sha256::new(); + hasher.update(content.as_bytes()); + hasher.update(tags_json.as_bytes()); + Ok(hex::encode(hasher.finalize())) +} + +fn find_farm_by_ref<E: SqlExecutor>( + exec: &E, + pubkey: &str, + d_tag: &str, +) -> Result<radroots_tangle_db_schema::farm::Farm, RadrootsTangleEventsError> { + let filter = IFarmFieldsFilter { + id: None, + created_at: None, + updated_at: None, + d_tag: Some(d_tag.to_string()), + pubkey: Some(pubkey.to_string()), + name: None, + about: None, + website: None, + picture: None, + banner: None, + location_primary: None, + location_city: None, + location_region: None, + location_country: None, + }; + let result = farm::find_many(exec, &IFarmFindMany { filter: Some(filter) })?; + result + .results + .into_iter() + .next() + .ok_or_else(|| RadrootsTangleEventsError::InvalidData("farm not found".to_string())) +} + +fn upsert_farm_tags<E: SqlExecutor>( + exec: &E, + farm_id: &str, + tags: Option<Vec<String>>, +) -> Result<(), RadrootsTangleEventsError> { + let existing = farm_tag::find_many( + exec, + &IFarmTagFindMany { + filter: Some(IFarmTagFieldsFilter { + id: None, + created_at: None, + updated_at: None, + farm_id: Some(farm_id.to_string()), + tag: None, + }), + }, + )?; + for row in existing.results { + let _ = farm_tag::delete( + exec, + &IFarmTagDelete::On(IFarmTagFindOneArgs { + on: FarmTagQueryBindValues::Id { id: row.id }, + }), + )?; + } + + let mut tags = tags.unwrap_or_default(); + tags.sort(); + tags.dedup(); + for tag in tags { + if tag.trim().is_empty() { + continue; + } + let fields = IFarmTagFields { + farm_id: farm_id.to_string(), + tag, + }; + let _ = farm_tag::create(exec, &fields)?; + } + Ok(()) +} + +fn upsert_plot_tags<E: SqlExecutor>( + exec: &E, + plot_id: &str, + tags: Option<Vec<String>>, +) -> Result<(), RadrootsTangleEventsError> { + let existing = plot_tag::find_many( + exec, + &IPlotTagFindMany { + filter: Some(IPlotTagFieldsFilter { + id: None, + created_at: None, + updated_at: None, + plot_id: Some(plot_id.to_string()), + tag: None, + }), + }, + )?; + for row in existing.results { + let _ = plot_tag::delete( + exec, + &IPlotTagDelete::On(IPlotTagFindOneArgs { + on: PlotTagQueryBindValues::Id { id: row.id }, + }), + )?; + } + + let mut tags = tags.unwrap_or_default(); + tags.sort(); + tags.dedup(); + for tag in tags { + if tag.trim().is_empty() { + continue; + } + let fields = IPlotTagFields { + plot_id: plot_id.to_string(), + tag, + }; + let _ = plot_tag::create(exec, &fields)?; + } + Ok(()) +} + +fn upsert_farm_location<E: SqlExecutor, F: RadrootsTangleIdFactory>( + exec: &E, + farm_id: &str, + location: Option<radroots_events::farm::RadrootsFarmLocation>, + factory: &F, +) -> Result<(), RadrootsTangleEventsError> { + clear_farm_locations(exec, farm_id)?; + if let Some(location) = location { + let gcs_id = create_gcs_location(exec, location.gcs, factory)?; + let fields = IFarmGcsLocationFields { + farm_id: farm_id.to_string(), + gcs_location_id: gcs_id, + role: ROLE_PRIMARY.to_string(), + }; + let _ = farm_gcs_location::create(exec, &fields)?; + } + Ok(()) +} + +fn upsert_plot_location<E: SqlExecutor, F: RadrootsTangleIdFactory>( + exec: &E, + plot_id: &str, + location: Option<radroots_events::plot::RadrootsPlotLocation>, + factory: &F, +) -> Result<(), RadrootsTangleEventsError> { + clear_plot_locations(exec, plot_id)?; + if let Some(location) = location { + let gcs_id = create_gcs_location(exec, location.gcs, factory)?; + let fields = IPlotGcsLocationFields { + plot_id: plot_id.to_string(), + gcs_location_id: gcs_id, + role: ROLE_PRIMARY.to_string(), + }; + let _ = plot_gcs_location::create(exec, &fields)?; + } + Ok(()) +} + +fn clear_farm_locations<E: SqlExecutor>( + exec: &E, + farm_id: &str, +) -> Result<(), RadrootsTangleEventsError> { + let existing = farm_gcs_location::find_many( + exec, + &IFarmGcsLocationFindMany { + filter: Some(IFarmGcsLocationFieldsFilter { + id: None, + created_at: None, + updated_at: None, + farm_id: Some(farm_id.to_string()), + gcs_location_id: None, + role: None, + }), + }, + )?; + for row in existing.results { + let _ = farm_gcs_location::delete( + exec, + &IFarmGcsLocationDelete::On(IFarmGcsLocationFindOneArgs { + on: FarmGcsLocationQueryBindValues::Id { id: row.id }, + }), + )?; + } + Ok(()) +} + +fn clear_plot_locations<E: SqlExecutor>( + exec: &E, + plot_id: &str, +) -> Result<(), RadrootsTangleEventsError> { + let existing = plot_gcs_location::find_many( + exec, + &IPlotGcsLocationFindMany { + filter: Some(IPlotGcsLocationFieldsFilter { + id: None, + created_at: None, + updated_at: None, + plot_id: Some(plot_id.to_string()), + gcs_location_id: None, + role: None, + }), + }, + )?; + for row in existing.results { + let _ = plot_gcs_location::delete( + exec, + &IPlotGcsLocationDelete::On(IPlotGcsLocationFindOneArgs { + on: PlotGcsLocationQueryBindValues::Id { id: row.id }, + }), + )?; + } + Ok(()) +} + +fn create_gcs_location<E: SqlExecutor, F: RadrootsTangleIdFactory>( + exec: &E, + gcs: radroots_events::farm::RadrootsGcsLocation, + factory: &F, +) -> Result<String, RadrootsTangleEventsError> { + let d_tag = factory.new_d_tag(); + let point = serde_json::to_string(&gcs.point) + .map_err(|_| RadrootsTangleEventsError::InvalidData("gcs.point".to_string()))?; + let polygon = serde_json::to_string(&gcs.polygon) + .map_err(|_| RadrootsTangleEventsError::InvalidData("gcs.polygon".to_string()))?; + + let fields = IGcsLocationFields { + d_tag, + lat: gcs.lat, + lng: gcs.lng, + geohash: gcs.geohash, + point, + polygon, + accuracy: gcs.accuracy, + altitude: gcs.altitude, + tag_0: gcs.tag_0, + label: gcs.label, + area: gcs.area, + elevation: gcs.elevation, + soil: gcs.soil, + climate: gcs.climate, + gc_id: gcs.gc_id, + gc_name: gcs.gc_name, + gc_admin1_id: gcs.gc_admin1_id, + gc_admin1_name: gcs.gc_admin1_name, + gc_country_id: gcs.gc_country_id, + gc_country_name: gcs.gc_country_name, + }; + let result = gcs_location::create(exec, &fields)?; + Ok(result.result.id) +} + +fn upsert_farm_members<E: SqlExecutor>( + exec: &E, + farm_id: &str, + role: ListSetRole, + list_set: &radroots_events::list_set::RadrootsListSet, +) -> Result<(), RadrootsTangleEventsError> { + let role_value = match role { + ListSetRole::Members => ROLE_MEMBER, + ListSetRole::Owners => ROLE_OWNER, + ListSetRole::Workers => ROLE_WORKER, + ListSetRole::Plots => return Ok(()), + }; + let existing = farm_member::find_many( + exec, + &IFarmMemberFindMany { + filter: Some(IFarmMemberFieldsFilter { + id: None, + created_at: None, + updated_at: None, + farm_id: Some(farm_id.to_string()), + member_pubkey: None, + role: Some(role_value.to_string()), + }), + }, + )?; + for row in existing.results { + let _ = farm_member::delete( + exec, + &IFarmMemberDelete::On(IFarmMemberFindOneArgs { + on: FarmMemberQueryBindValues::Id { id: row.id }, + }), + )?; + } + + let mut entries = list_set + .entries + .iter() + .filter(|entry| entry.tag == "p") + .filter_map(|entry| entry.values.get(0)) + .map(|value| value.to_string()) + .collect::<Vec<_>>(); + entries.sort(); + entries.dedup(); + + for pubkey in entries { + let fields = IFarmMemberFields { + farm_id: farm_id.to_string(), + member_pubkey: pubkey, + role: role_value.to_string(), + }; + let _ = farm_member::create(exec, &fields)?; + } + Ok(()) +} + +fn upsert_member_claims<E: SqlExecutor>( + exec: &E, + member_pubkey: &str, + list_set: &radroots_events::list_set::RadrootsListSet, +) -> Result<(), RadrootsTangleEventsError> { + let existing = farm_member_claim::find_many( + exec, + &IFarmMemberClaimFindMany { + filter: Some(IFarmMemberClaimFieldsFilter { + id: None, + created_at: None, + updated_at: None, + member_pubkey: Some(member_pubkey.to_string()), + farm_pubkey: None, + }), + }, + )?; + for row in existing.results { + let _ = farm_member_claim::delete( + exec, + &IFarmMemberClaimDelete::On(IFarmMemberClaimFindOneArgs { + on: FarmMemberClaimQueryBindValues::Id { id: row.id }, + }), + )?; + } + + let mut entries = list_set + .entries + .iter() + .filter(|entry| entry.tag == "p") + .filter_map(|entry| entry.values.get(0)) + .map(|value| value.to_string()) + .collect::<Vec<_>>(); + entries.sort(); + entries.dedup(); + + for farm_pubkey in entries { + let fields = IFarmMemberClaimFields { + member_pubkey: member_pubkey.to_string(), + farm_pubkey, + }; + let _ = farm_member_claim::create(exec, &fields)?; + } + Ok(()) +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum ListSetRole { + Members, + Owners, + Workers, + Plots, +} + +fn unpack_farm_location_strings( + location: Option<&radroots_events::farm::RadrootsFarmLocation>, +) -> (Option<String>, Option<String>, Option<String>, Option<String>) { + match location { + Some(location) => ( + location.primary.clone(), + location.city.clone(), + location.region.clone(), + location.country.clone(), + ), + None => (None, None, None, None), + } +} + +fn unpack_plot_location_strings( + location: Option<&radroots_events::plot::RadrootsPlotLocation>, +) -> (Option<String>, Option<String>, Option<String>, Option<String>) { + match location { + Some(location) => ( + location.primary.clone(), + location.city.clone(), + location.region.clone(), + location.country.clone(), + ), + None => (None, None, None, None), + } +} + +fn ensure_list_set_entries_tag( + list_set: &radroots_events::list_set::RadrootsListSet, + expected: &str, + label: &str, +) -> Result<(), RadrootsTangleEventsError> { + for entry in list_set.entries.iter() { + if entry.tag != expected { + return Err(RadrootsTangleEventsError::InvalidData(format!( + "domain:farm list set {label} must only include {expected} tags" + ))); + } + if entry.values.get(0).map(|v| v.trim().is_empty()).unwrap_or(true) { + return Err(RadrootsTangleEventsError::InvalidData(format!( + "domain:farm list set {label} contains empty entries" + ))); + } + } + Ok(()) +} + +fn parse_farm_list_set_d_tag(d_tag: &str) -> Option<(String, ListSetRole)> { + let mut parts = d_tag.splitn(3, ':'); + if parts.next()? != "farm" { + return None; + } + let farm_d_tag = parts.next()?.to_string(); + let suffix = parts.next()?; + let role = match suffix { + "members" => ListSetRole::Members, + "members.owners" => ListSetRole::Owners, + "members.workers" => ListSetRole::Workers, + "plots" => ListSetRole::Plots, + _ => return None, + }; + Some((farm_d_tag, role)) +} + +fn to_value_opt(value: Option<String>) -> Option<Value> { + Some(match value { + Some(value) => Value::from(value), + None => Value::Null, + }) +} + +struct EventStateDecision { + apply: bool, + content_hash: String, +} diff --git a/tangle-events/src/lib.rs b/tangle-events/src/lib.rs @@ -0,0 +1,43 @@ +#![forbid(unsafe_code)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc; + +pub mod error; +mod canonical; +mod geo; +pub mod emit; +pub mod ingest; +pub mod types; + +pub use error::RadrootsTangleEventsError; +pub use emit::{ + radroots_tangle_sync_all, + radroots_tangle_sync_all_with_options, + radroots_tangle_farm_event, + radroots_tangle_list_set_events, + radroots_tangle_membership_claim_events, + radroots_tangle_plot_events, + radroots_tangle_profile_events, +}; +pub use ingest::{ + radroots_tangle_ingest_event_with_factory, + radroots_tangle_ingest_event_state, + RadrootsTangleIngestOutcome, + RadrootsTangleIdFactory, +}; +pub use types::{ + RADROOTS_TANGLE_TRANSFER_VERSION, + RadrootsTangleEventDraft, + RadrootsTangleFarmSelector, + RadrootsTangleSyncBundle, + RadrootsTangleSyncOptions, + RadrootsTangleSyncRequest, +}; + +#[cfg(feature = "std")] +pub use ingest::{radroots_tangle_ingest_event, RadrootsTangleDefaultIdFactory}; + +#[cfg(test)] +mod tests; diff --git a/tangle-events/src/tests.rs b/tangle-events/src/tests.rs @@ -0,0 +1,238 @@ +use radroots_events::farm::{RadrootsGeoJsonPoint, RadrootsGeoJsonPolygon}; +use radroots_events::kinds::{KIND_FARM, KIND_LIST_SET_GENERIC, KIND_PLOT, KIND_PROFILE}; +use radroots_sql_core::SqliteExecutor; +use radroots_sql_core::error::SqlError; +use crate::{ + radroots_tangle_sync_all, + RadrootsTangleFarmSelector, + RadrootsTangleSyncRequest, + RADROOTS_TANGLE_TRANSFER_VERSION, +}; +use radroots_types::types::IError; +use radroots_tangle_db_schema::farm::IFarmFields; +use radroots_tangle_db_schema::farm_gcs_location::IFarmGcsLocationFields; +use radroots_tangle_db_schema::farm_member::IFarmMemberFields; +use radroots_tangle_db_schema::farm_member_claim::IFarmMemberClaimFields; +use radroots_tangle_db_schema::farm_tag::IFarmTagFields; +use radroots_tangle_db_schema::gcs_location::IGcsLocationFields; +use radroots_tangle_db_schema::nostr_profile::INostrProfileFields; +use radroots_tangle_db_schema::plot::IPlotFields; +use radroots_tangle_db_schema::plot_gcs_location::IPlotGcsLocationFields; +use radroots_tangle_db_schema::plot_tag::IPlotTagFields; +use radroots_tangle_db::{ + farm, + farm_gcs_location, + farm_member, + farm_member_claim, + farm_tag, + gcs_location, + migrations, + nostr_profile, + plot, + plot_gcs_location, + plot_tag, +}; + +fn unwrap_sql<T>(result: Result<T, IError<SqlError>>, label: &str) -> T { + match result { + Ok(value) => value, + Err(err) => panic!("{label}: {}", err.err), + } +} + +#[test] +fn sync_all_emits_expected_order() { + let exec = SqliteExecutor::open_memory().expect("exec"); + migrations::run_all_up(&exec).expect("migrations"); + + let farm_pubkey = "f".repeat(64); + let farm_fields = IFarmFields { + d_tag: "farm_d".to_string(), + pubkey: farm_pubkey.clone(), + name: "Green Farm".to_string(), + about: Some("About".to_string()), + website: None, + picture: None, + banner: None, + location_primary: None, + location_city: None, + location_region: None, + location_country: None, + }; + let farm_row = unwrap_sql(farm::create(&exec, &farm_fields), "farm").result; + + let gcs_point = RadrootsGeoJsonPoint { + r#type: "Point".to_string(), + coordinates: [-122.4, 37.7], + }; + let gcs_polygon = RadrootsGeoJsonPolygon { + r#type: "Polygon".to_string(), + coordinates: vec![vec![ + [-122.4, 37.7], + [-122.4, 37.701], + [-122.401, 37.701], + [-122.4, 37.7], + ]], + }; + let gcs_fields = IGcsLocationFields { + d_tag: "gcs_d".to_string(), + lat: 37.7, + lng: -122.4, + geohash: "9q8yy".to_string(), + point: serde_json::to_string(&gcs_point).expect("point"), + polygon: serde_json::to_string(&gcs_polygon).expect("polygon"), + accuracy: None, + altitude: None, + tag_0: None, + label: None, + area: None, + elevation: None, + soil: None, + climate: None, + gc_id: None, + gc_name: None, + gc_admin1_id: None, + gc_admin1_name: None, + gc_country_id: None, + gc_country_name: None, + }; + let gcs_row = unwrap_sql(gcs_location::create(&exec, &gcs_fields), "gcs").result; + + let farm_gcs_fields = IFarmGcsLocationFields { + farm_id: farm_row.id.clone(), + gcs_location_id: gcs_row.id.clone(), + role: "primary".to_string(), + }; + let _ = unwrap_sql( + farm_gcs_location::create(&exec, &farm_gcs_fields), + "farm_gcs", + ); + + let plot_fields = IPlotFields { + d_tag: "plot_d".to_string(), + farm_id: farm_row.id.clone(), + name: "Plot A".to_string(), + about: None, + location_primary: None, + location_city: None, + location_region: None, + location_country: None, + }; + let plot_row = unwrap_sql(plot::create(&exec, &plot_fields), "plot").result; + + let plot_gcs_fields = IPlotGcsLocationFields { + plot_id: plot_row.id.clone(), + gcs_location_id: gcs_row.id.clone(), + role: "primary".to_string(), + }; + let _ = unwrap_sql( + plot_gcs_location::create(&exec, &plot_gcs_fields), + "plot_gcs", + ); + + let _ = unwrap_sql( + farm_tag::create( + &exec, + &IFarmTagFields { + farm_id: farm_row.id.clone(), + tag: "coffee".to_string(), + }, + ), + "farm_tag", + ); + + let _ = unwrap_sql( + plot_tag::create( + &exec, + &IPlotTagFields { + plot_id: plot_row.id.clone(), + tag: "orchard".to_string(), + }, + ), + "plot_tag", + ); + + let owner_pubkey = "o".repeat(64); + let _ = unwrap_sql( + farm_member::create( + &exec, + &IFarmMemberFields { + farm_id: farm_row.id.clone(), + member_pubkey: owner_pubkey.clone(), + role: "owner".to_string(), + }, + ), + "farm_member", + ); + + let _ = unwrap_sql( + farm_member_claim::create( + &exec, + &IFarmMemberClaimFields { + member_pubkey: owner_pubkey.clone(), + farm_pubkey: farm_pubkey.clone(), + }, + ), + "farm_member_claim", + ); + + let _ = unwrap_sql( + nostr_profile::create( + &exec, + &INostrProfileFields { + public_key: farm_pubkey.clone(), + profile_type: "farm".to_string(), + name: "Farm Profile".to_string(), + display_name: None, + about: None, + website: None, + picture: None, + banner: None, + nip05: None, + lud06: None, + lud16: None, + }, + ), + "farm_profile", + ); + + let _ = unwrap_sql( + nostr_profile::create( + &exec, + &INostrProfileFields { + public_key: owner_pubkey.clone(), + profile_type: "individual".to_string(), + name: "Owner".to_string(), + display_name: None, + about: None, + website: None, + picture: None, + banner: None, + nip05: None, + lud06: None, + lud16: None, + }, + ), + "owner_profile", + ); + + let request = RadrootsTangleSyncRequest { + farm: RadrootsTangleFarmSelector { + id: Some(farm_row.id.clone()), + d_tag: None, + pubkey: None, + }, + options: None, + }; + let bundle = radroots_tangle_sync_all(&exec, &request).expect("sync"); + + assert_eq!(bundle.version, RADROOTS_TANGLE_TRANSFER_VERSION); + assert_eq!(bundle.events.len(), 9); + let kinds = bundle.events.iter().map(|event| event.kind).collect::<Vec<_>>(); + assert_eq!(kinds[0], KIND_PROFILE); + assert_eq!(kinds[1], KIND_PROFILE); + assert_eq!(kinds[2], KIND_FARM); + assert_eq!(kinds[3], KIND_PLOT); + assert!(kinds[4..8].iter().all(|kind| *kind == KIND_LIST_SET_GENERIC)); + assert_eq!(kinds[8], KIND_LIST_SET_GENERIC); +} diff --git a/tangle-events/src/types.rs b/tangle-events/src/types.rs @@ -0,0 +1,40 @@ +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; + +use serde::{Deserialize, Serialize}; + +pub const RADROOTS_TANGLE_TRANSFER_VERSION: u32 = 1; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RadrootsTangleEventDraft { + pub kind: u32, + pub author: String, + pub content: String, + pub tags: Vec<Vec<String>>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RadrootsTangleSyncBundle { + pub version: u32, + pub events: Vec<RadrootsTangleEventDraft>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RadrootsTangleFarmSelector { + pub id: Option<String>, + pub d_tag: Option<String>, + pub pubkey: Option<String>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RadrootsTangleSyncOptions { + pub include_profiles: Option<bool>, + pub include_list_sets: Option<bool>, + pub include_membership_claims: Option<bool>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RadrootsTangleSyncRequest { + pub farm: RadrootsTangleFarmSelector, + pub options: Option<RadrootsTangleSyncOptions>, +} diff --git a/tangle-schema/Cargo.toml b/tangle-schema/Cargo.toml @@ -1,21 +0,0 @@ -[package] -name = "radroots-tangle-schema" -version.workspace = true -edition.workspace = true -authors = ["Radroots Authors"] -rust-version.workspace = true -license.workspace = true -build = "build.rs" - -[lib] -crate-type = ["rlib"] - -[features] -default = [] -ts-rs = ["dep:ts-rs"] - -[dependencies] -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -ts-rs = { workspace = true, optional = true } -radroots-types = { workspace = true } diff --git a/tangle-schema/bindings/ts/package.json b/tangle-schema/bindings/ts/package.json @@ -1,41 +0,0 @@ -{ - "name": "@radroots/tangle-schema-bindings", - "version": "1.0.0", - "private": true, - "license": "AGPLv3", - "type": "module", - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", - "types": "./dist/types/index.d.ts", - "exports": { - ".": { - "types": "./dist/types/index.d.ts", - "import": "./dist/esm/index.js", - "require": "./dist/cjs/index.js" - } - }, - "files": [ - "dist" - ], - "sideEffects": false, - "scripts": { - "build:esm": "tsc -p tsconfig.esm.json", - "build:cjs": "tsc -p tsconfig.cjs.json", - "build": "npm run build:esm && npm run build:cjs", - "prebuild": "npm run clean && npm run prepend-imports", - "prepend-imports": "bash -c 'f=./src/types.ts; line=\"import type { IResult, IResultList, IResultPass } from \\\"@radroots/types-bindings\\\";\"; grep -qxF \"$line\" \"$f\" || (echo -e \"$line\\n\\n$(cat $f)\" > \"$f\")'", - "clean": "rimraf dist", - "dev": "npm run watch", - "watch": "tsc -w" - }, - "devDependencies": { - "@radroots/tsconfig": "workspace:*", - "rimraf": "^6.0.1" - }, - "dependencies": { - "@radroots/types-bindings": "workspace:*" - }, - "publishConfig": { - "access": "public" - } -} -\ No newline at end of file diff --git a/tangle-schema/bindings/ts/src/types.ts b/tangle-schema/bindings/ts/src/types.ts @@ -1,279 +0,0 @@ -import type { IResult, IResultList, IResultPass } from "@radroots/types-bindings"; - -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -export type Farm = { id: string, created_at: string, updated_at: string, name: string, area: string | null, area_unit: string | null, title: string | null, description: string | null, }; - -export type FarmFindManyRel = never; - -export type FarmQueryBindValues = { id: string, }; - -export type IFarmCreate = IFarmFields; - -export type IFarmCreateResolve = IResult<Farm>; - -export type IFarmDelete = IFarmFindOne; - -export type IFarmDeleteResolve = IResult<string>; - -export type IFarmFields = { name: string, area?: string | null, area_unit?: string | null, title?: string | null, description?: string | null, }; - -export type IFarmFieldsFilter = { id?: string, created_at?: string, updated_at?: string, name?: string, area?: string, area_unit?: string, title?: string, description?: string, }; - -export type IFarmFieldsPartial = { name?: string | null, area?: string | null, area_unit?: string | null, title?: string | null, description?: string | null, }; - -export type IFarmFindMany = { filter: IFarmFieldsFilter | null, }; - -export type IFarmFindManyResolve = IResultList<Farm>; - -export type IFarmFindOne = IFarmFindOneArgs | IFarmFindOneRelArgs; - -export type IFarmFindOneArgs = { on: FarmQueryBindValues, }; - -export type IFarmFindOneRelArgs = { rel: FarmFindManyRel, }; - -export type IFarmFindOneResolve = IResult<Farm>; - -export type IFarmLocationRelation = { farm: FarmQueryBindValues, location_gcs: LocationGcsQueryBindValues, }; - -export type IFarmLocationResolve = IResultPass; - -export type IFarmUpdate = { on: FarmQueryBindValues, fields: IFarmFieldsPartial, }; - -export type IFarmUpdateResolve = IResult<Farm>; - -export type ILocationGcsCreate = ILocationGcsFields; - -export type ILocationGcsCreateResolve = IResult<LocationGcs>; - -export type ILocationGcsDelete = ILocationGcsFindOne; - -export type ILocationGcsDeleteResolve = IResult<string>; - -export type ILocationGcsFields = { lat: number, lng: number, geohash: string, tag_0?: string | null, label?: string | null, area?: number | null, elevation?: number | null, soil?: string | null, climate?: string | null, gc_id?: string | null, gc_name?: string | null, gc_admin1_id?: string | null, gc_admin1_name?: string | null, gc_country_id?: string | null, gc_country_name?: string | null, }; - -export type ILocationGcsFieldsFilter = { id?: string, created_at?: string, updated_at?: string, lat?: number, lng?: number, geohash?: string, tag_0?: string, label?: string, area?: number, elevation?: number, soil?: string, climate?: string, gc_id?: string, gc_name?: string, gc_admin1_id?: string, gc_admin1_name?: string, gc_country_id?: string, gc_country_name?: string, }; - -export type ILocationGcsFieldsPartial = { lat?: number | null, lng?: number | null, geohash?: string | null, tag_0?: string | null, label?: string | null, area?: number | null, elevation?: number | null, soil?: string | null, climate?: string | null, gc_id?: string | null, gc_name?: string | null, gc_admin1_id?: string | null, gc_admin1_name?: string | null, gc_country_id?: string | null, gc_country_name?: string | null, }; - -export type ILocationGcsFindMany = { filter: ILocationGcsFieldsFilter | null, } | { rel: LocationGcsFindManyRel, }; - -export type ILocationGcsFindManyResolve = IResultList<LocationGcs>; - -export type ILocationGcsFindOne = ILocationGcsFindOneArgs | ILocationGcsFindOneRelArgs; - -export type ILocationGcsFindOneArgs = { on: LocationGcsQueryBindValues, }; - -export type ILocationGcsFindOneRelArgs = { rel: LocationGcsFindManyRel, }; - -export type ILocationGcsFindOneResolve = IResult<LocationGcs>; - -export type ILocationGcsUpdate = { on: LocationGcsQueryBindValues, fields: ILocationGcsFieldsPartial, }; - -export type ILocationGcsUpdateResolve = IResult<LocationGcs>; - -export type ILogErrorCreate = ILogErrorFields; - -export type ILogErrorCreateResolve = IResult<LogError>; - -export type ILogErrorDelete = ILogErrorFindOne; - -export type ILogErrorDeleteResolve = IResult<string>; - -export type ILogErrorFields = { error: string, message: string, stack_trace?: string | null, cause?: string | null, app_system: string, app_version: string, nostr_pubkey: string, data?: string | null, }; - -export type ILogErrorFieldsFilter = { id?: string, created_at?: string, updated_at?: string, error?: string, message?: string, stack_trace?: string, cause?: string, app_system?: string, app_version?: string, nostr_pubkey?: string, data?: string, }; - -export type ILogErrorFieldsPartial = { error?: string | null, message?: string | null, stack_trace?: string | null, cause?: string | null, app_system?: string | null, app_version?: string | null, nostr_pubkey?: string | null, data?: string | null, }; - -export type ILogErrorFindMany = { filter: ILogErrorFieldsFilter | null, }; - -export type ILogErrorFindManyResolve = IResultList<LogError>; - -export type ILogErrorFindOne = ILogErrorFindOneArgs | ILogErrorFindOneRelArgs; - -export type ILogErrorFindOneArgs = { on: LogErrorQueryBindValues, }; - -export type ILogErrorFindOneRelArgs = { rel: LogErrorFindManyRel, }; - -export type ILogErrorFindOneResolve = IResult<LogError>; - -export type ILogErrorUpdate = { on: LogErrorQueryBindValues, fields: ILogErrorFieldsPartial, }; - -export type ILogErrorUpdateResolve = IResult<LogError>; - -export type IMediaImageCreate = IMediaImageFields; - -export type IMediaImageCreateResolve = IResult<MediaImage>; - -export type IMediaImageDelete = IMediaImageFindOne; - -export type IMediaImageDeleteResolve = IResult<string>; - -export type IMediaImageFields = { file_path: string, mime_type: string, res_base: string, res_path: string, label?: string | null, description?: string | null, }; - -export type IMediaImageFieldsFilter = { id?: string, created_at?: string, updated_at?: string, file_path?: string, mime_type?: string, res_base?: string, res_path?: string, label?: string, description?: string, }; - -export type IMediaImageFieldsPartial = { file_path?: string | null, mime_type?: string | null, res_base?: string | null, res_path?: string | null, label?: string | null, description?: string | null, }; - -export type IMediaImageFindMany = { filter: IMediaImageFieldsFilter | null, } | { rel: MediaImageFindManyRel, }; - -export type IMediaImageFindManyResolve = IResultList<MediaImage>; - -export type IMediaImageFindOne = IMediaImageFindOneArgs | IMediaImageFindOneRelArgs; - -export type IMediaImageFindOneArgs = { on: MediaImageQueryBindValues, }; - -export type IMediaImageFindOneRelArgs = { rel: MediaImageFindManyRel, }; - -export type IMediaImageFindOneResolve = IResult<MediaImage>; - -export type IMediaImageUpdate = { on: MediaImageQueryBindValues, fields: IMediaImageFieldsPartial, }; - -export type IMediaImageUpdateResolve = IResult<MediaImage>; - -export type INostrProfileCreate = INostrProfileFields; - -export type INostrProfileCreateResolve = IResult<NostrProfile>; - -export type INostrProfileDelete = INostrProfileFindOne; - -export type INostrProfileDeleteResolve = IResult<string>; - -export type INostrProfileFields = { public_key: string, name: string, display_name?: string | null, about?: string | null, website?: string | null, picture?: string | null, banner?: string | null, nip05?: string | null, lud06?: string | null, lud16?: string | null, }; - -export type INostrProfileFieldsFilter = { id?: string, created_at?: string, updated_at?: string, public_key?: string, name?: string, display_name?: string, about?: string, website?: string, picture?: string, banner?: string, nip05?: string, lud06?: string, lud16?: string, }; - -export type INostrProfileFieldsPartial = { public_key?: string | null, name?: string | null, display_name?: string | null, about?: string | null, website?: string | null, picture?: string | null, banner?: string | null, nip05?: string | null, lud06?: string | null, lud16?: string | null, }; - -export type INostrProfileFindMany = { filter: INostrProfileFieldsFilter | null, } | { rel: NostrProfileFindManyRel, }; - -export type INostrProfileFindManyResolve = IResultList<NostrProfile>; - -export type INostrProfileFindOne = INostrProfileFindOneArgs | INostrProfileFindOneRelArgs; - -export type INostrProfileFindOneArgs = { on: NostrProfileQueryBindValues, }; - -export type INostrProfileFindOneRelArgs = { rel: NostrProfileFindManyRel, }; - -export type INostrProfileFindOneResolve = IResult<NostrProfile>; - -export type INostrProfileRelayRelation = { nostr_profile: NostrProfileQueryBindValues, nostr_relay: NostrRelayQueryBindValues, }; - -export type INostrProfileRelayResolve = IResultPass; - -export type INostrProfileUpdate = { on: NostrProfileQueryBindValues, fields: INostrProfileFieldsPartial, }; - -export type INostrProfileUpdateResolve = IResult<NostrProfile>; - -export type INostrRelayCreate = INostrRelayFields; - -export type INostrRelayCreateResolve = IResult<NostrRelay>; - -export type INostrRelayDelete = INostrRelayFindOne; - -export type INostrRelayDeleteResolve = IResult<string>; - -export type INostrRelayFields = { url: string, relay_id?: string | null, name?: string | null, description?: string | null, pubkey?: string | null, contact?: string | null, supported_nips?: string | null, software?: string | null, version?: string | null, data?: string | null, }; - -export type INostrRelayFieldsFilter = { id?: string, created_at?: string, updated_at?: string, url?: string, relay_id?: string, name?: string, description?: string, pubkey?: string, contact?: string, supported_nips?: string, software?: string, version?: string, data?: string, }; - -export type INostrRelayFieldsPartial = { url?: string | null, relay_id?: string | null, name?: string | null, description?: string | null, pubkey?: string | null, contact?: string | null, supported_nips?: string | null, software?: string | null, version?: string | null, data?: string | null, }; - -export type INostrRelayFindMany = { filter: INostrRelayFieldsFilter | null, } | { rel: NostrRelayFindManyRel, }; - -export type INostrRelayFindManyResolve = IResultList<NostrRelay>; - -export type INostrRelayFindOne = INostrRelayFindOneArgs | INostrRelayFindOneRelArgs; - -export type INostrRelayFindOneArgs = { on: NostrRelayQueryBindValues, }; - -export type INostrRelayFindOneRelArgs = { rel: NostrRelayFindManyRel, }; - -export type INostrRelayFindOneResolve = IResult<NostrRelay>; - -export type INostrRelayUpdate = { on: NostrRelayQueryBindValues, fields: INostrRelayFieldsPartial, }; - -export type INostrRelayUpdateResolve = IResult<NostrRelay>; - -export type ITradeProductCreate = ITradeProductFields; - -export type ITradeProductCreateResolve = IResult<TradeProduct>; - -export type ITradeProductDelete = ITradeProductFindOne; - -export type ITradeProductDeleteResolve = IResult<string>; - -export type ITradeProductFields = { key: string, category: string, title: string, summary: string, process: string, lot: string, profile: string, year: bigint, qty_amt: bigint, qty_unit: string, qty_label?: string | null, qty_avail?: number | null, price_amt: number, price_currency: string, price_qty_amt: number, price_qty_unit: string, notes?: string | null, }; - -export type ITradeProductFieldsFilter = { id?: string, created_at?: string, updated_at?: string, key?: string, category?: string, title?: string, summary?: string, process?: string, lot?: string, profile?: string, year?: bigint, qty_amt?: bigint, qty_unit?: string, qty_label?: string, qty_avail?: bigint, price_amt?: number, price_currency?: string, price_qty_amt?: number, price_qty_unit?: string, notes?: string, }; - -export type ITradeProductFieldsPartial = { key?: string | null, category?: string | null, title?: string | null, summary?: string | null, process?: string | null, lot?: string | null, profile?: string | null, year?: number | null, qty_amt?: number | null, qty_unit?: string | null, qty_label?: string | null, qty_avail?: number | null, price_amt?: number | null, price_currency?: string | null, price_qty_amt?: number | null, price_qty_unit?: string | null, notes?: string | null, }; - -export type ITradeProductFindMany = { filter: ITradeProductFieldsFilter | null, }; - -export type ITradeProductFindManyResolve = IResultList<TradeProduct>; - -export type ITradeProductFindOne = ITradeProductFindOneArgs | ITradeProductFindOneRelArgs; - -export type ITradeProductFindOneArgs = { on: TradeProductQueryBindValues, }; - -export type ITradeProductFindOneRelArgs = { rel: TradeProductFindManyRel, }; - -export type ITradeProductFindOneResolve = IResult<TradeProduct>; - -export type ITradeProductLocationRelation = { trade_product: TradeProductQueryBindValues, location_gcs: LocationGcsQueryBindValues, }; - -export type ITradeProductLocationResolve = IResultPass; - -export type ITradeProductMediaRelation = { trade_product: TradeProductQueryBindValues, media_image: MediaImageQueryBindValues, }; - -export type ITradeProductMediaResolve = IResultPass; - -export type ITradeProductUpdate = { on: TradeProductQueryBindValues, fields: ITradeProductFieldsPartial, }; - -export type ITradeProductUpdateResolve = IResult<TradeProduct>; - -export type LocationGcs = { id: string, created_at: string, updated_at: string, lat: number, lng: number, geohash: string, tag_0: string | null, label: string | null, area: number | null, elevation: number | null, soil: string | null, climate: string | null, gc_id: string | null, gc_name: string | null, gc_admin1_id: string | null, gc_admin1_name: string | null, gc_country_id: string | null, gc_country_name: string | null, }; - -export type LocationGcsFindManyRel = { "on_trade_product": LocationGcsTradeProductArgs } | { "off_trade_product": LocationGcsTradeProductArgs } | { "on_farm": LocationGcsTradeProductArgs } | { "off_farm": LocationGcsTradeProductArgs }; - -export type LocationGcsQueryBindValues = { id: string, } | { geohash: string, }; - -export type LocationGcsTradeProductArgs = { id: string, }; - -export type LogError = { id: string, created_at: string, updated_at: string, error: string, message: string, stack_trace: string | null, cause: string | null, app_system: string, app_version: string, nostr_pubkey: string, data: string | null, }; - -export type LogErrorFindManyRel = never; - -export type LogErrorQueryBindValues = { id: string, } | { nostr_pubkey: string, }; - -export type MediaImage = { id: string, created_at: string, updated_at: string, file_path: string, mime_type: string, res_base: string, res_path: string, label: string | null, description: string | null, }; - -export type MediaImageFindManyRel = { "on_trade_product": MediaImageTradeProductArgs } | { "off_trade_product": MediaImageTradeProductArgs }; - -export type MediaImageQueryBindValues = { id: string, } | { file_path: string, }; - -export type MediaImageTradeProductArgs = { id: string, }; - -export type NostrProfile = { id: string, created_at: string, updated_at: string, public_key: string, name: string, display_name: string | null, about: string | null, website: string | null, picture: string | null, banner: string | null, nip05: string | null, lud06: string | null, lud16: string | null, }; - -export type NostrProfileFindManyRel = { "on_relay": NostrProfileRelayArgs } | { "off_relay": NostrProfileRelayArgs }; - -export type NostrProfileQueryBindValues = { id: string, } | { public_key: string, }; - -export type NostrProfileRelayArgs = { id: string, }; - -export type NostrRelay = { id: string, created_at: string, updated_at: string, url: string, relay_id: string | null, name: string | null, description: string | null, pubkey: string | null, contact: string | null, supported_nips: string | null, software: string | null, version: string | null, data: string | null, }; - -export type NostrRelayFindManyRel = { "on_profile": NostrRelayProfileArgs } | { "off_profile": NostrRelayProfileArgs }; - -export type NostrRelayProfileArgs = { public_key: string, }; - -export type NostrRelayQueryBindValues = { id: string, } | { url: string, }; - -export type TradeProduct = { id: string, created_at: string, updated_at: string, key: string, category: string, title: string, summary: string, process: string, lot: string, profile: string, year: bigint, qty_amt: bigint, qty_unit: string, qty_label: string | null, qty_avail: bigint | null, price_amt: number, price_currency: string, price_qty_amt: number, price_qty_unit: string, notes: string | null, }; - -export type TradeProductFindManyRel = never; - -export type TradeProductQueryBindValues = { id: string, }; diff --git a/tangle-schema/src/models/farm.rs b/tangle-schema/src/models/farm.rs @@ -1,232 +0,0 @@ -use radroots_types::types::{IResult, IResultList}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -#[cfg(feature = "ts-rs")] -use ts_rs::TS; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Serialize, Deserialize)] -pub struct Farm { - pub id: String, - pub created_at: String, - pub updated_at: String, - pub name: String, - pub area: Option<String>, - pub area_unit: Option<String>, - pub title: Option<String>, - pub description: Option<String>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct IFarmFields { - pub name: String, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub area: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub area_unit: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub title: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub description: Option<String>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct IFarmFieldsPartial { - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub name: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub area: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub area_unit: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub title: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub description: Option<serde_json::Value>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct IFarmFieldsFilter { - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub id: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub created_at: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub updated_at: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub name: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub area: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub area_unit: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub title: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub description: Option<String>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum FarmQueryBindValues { - Id { id: String }, -} -impl FarmQueryBindValues { - pub fn to_filter_param(&self) -> (&'static str, Value) { - match self { - Self::Id { id } => ("id", Value::from(id.clone())), - } - } - - pub fn primary_key(&self) -> Option<String> { - match self { - Self::Id { id } => Some(id.clone()), - } - } - - pub fn lookup_key(&self) -> String { - match self { - Self::Id { id } => id.clone(), - } - } -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub enum FarmFindManyRel { - -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "IFarmCreate", - type = "IFarmFields" - ) -)] -pub struct IFarmCreateTs; -pub type IFarmCreate = IFarmFields; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "IFarmCreateResolve", - type = "IResult<Farm>" - ) -)] -pub struct IFarmCreateResolveTs; -pub type IFarmCreateResolve = IResult<Farm>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Deserialize, Serialize)] -pub struct IFarmFindOneArgs { - pub on: FarmQueryBindValues, -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Deserialize, Serialize)] -pub struct IFarmFindOneRelArgs { - pub rel: FarmFindManyRel, -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts(export, export_to = "types.ts", rename = "IFarmFindOne") -)] -#[derive(Deserialize, Serialize)] -#[serde(untagged)] -pub enum IFarmFindOne { - On(IFarmFindOneArgs), - Rel(IFarmFindOneRelArgs), -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "IFarmFindOneResolve", - type = "IResult<Farm>" - ) -)] -pub struct IFarmFindOneResolveTs; -pub type IFarmFindOneResolve = IResult<Option<Farm>>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts(export, export_to = "types.ts", rename = "IFarmFindMany") -)] -#[derive(Deserialize, Serialize)] -pub struct IFarmFindManyArgs { - pub filter: Option<IFarmFieldsFilter>, -} -pub type IFarmFindMany = IFarmFindManyArgs; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "IFarmFindManyResolve", - type = "IResultList<Farm>" - ) -)] -pub struct IFarmFindManyResolveTs; -pub type IFarmFindManyResolve = IResultList<Farm>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "IFarmDelete", - type = "IFarmFindOne" - ) -)] -pub struct IFarmDeleteTs; -pub type IFarmDelete = IFarmFindOne; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "IFarmDeleteResolve", - type = "IResult<string>" - ) -)] -pub struct IFarmDeleteResolveTs; -pub type IFarmDeleteResolve = IResult<String>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts(export, export_to = "types.ts", rename = "IFarmUpdate") -)] -#[derive(Deserialize, Serialize)] -pub struct IFarmUpdateArgs { - pub on: FarmQueryBindValues, - pub fields: IFarmFieldsPartial, -} -pub type IFarmUpdate = IFarmUpdateArgs; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "IFarmUpdateResolve", - type = "IResult<Farm>" - ) -)] -pub struct IFarmUpdateResolveTs; -pub type IFarmUpdateResolve = IResult<Farm>; -\ No newline at end of file diff --git a/tangle-schema/src/models/farm_location.rs b/tangle-schema/src/models/farm_location.rs @@ -1,27 +0,0 @@ -use radroots_types::types::IResultPass; -use serde::{Deserialize, Serialize}; -#[cfg(feature = "ts-rs")] -use ts_rs::TS; -use crate::farm::FarmQueryBindValues; -use crate::location_gcs::LocationGcsQueryBindValues; - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct IFarmLocationRelation { - pub farm: FarmQueryBindValues, - pub location_gcs: LocationGcsQueryBindValues, -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "IFarmLocationResolve", - type = "IResultPass" - ) -)] -pub struct IFarmLocationResolveTs; -pub type IFarmLocationResolve = IResultPass; diff --git a/tangle-schema/src/models/location_gcs.rs b/tangle-schema/src/models/location_gcs.rs @@ -1,323 +0,0 @@ -use radroots_types::types::{IResult, IResultList}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -#[cfg(feature = "ts-rs")] -use ts_rs::TS; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Serialize, Deserialize)] -pub struct LocationGcs { - pub id: String, - pub created_at: String, - pub updated_at: String, - pub lat: f64, - pub lng: f64, - pub geohash: String, - pub tag_0: Option<String>, - pub label: Option<String>, - pub area: Option<f64>, - pub elevation: Option<u32>, - pub soil: Option<String>, - pub climate: Option<String>, - pub gc_id: Option<String>, - pub gc_name: Option<String>, - pub gc_admin1_id: Option<String>, - pub gc_admin1_name: Option<String>, - pub gc_country_id: Option<String>, - pub gc_country_name: Option<String>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct ILocationGcsFields { - pub lat: f64, - pub lng: f64, - pub geohash: String, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub tag_0: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub label: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] - pub area: Option<f64>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] - pub elevation: Option<u32>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub soil: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub climate: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_id: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_name: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_admin1_id: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_admin1_name: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_country_id: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_country_name: Option<String>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct ILocationGcsFieldsPartial { - #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] - pub lat: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] - pub lng: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub geohash: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub tag_0: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub label: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] - pub area: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "number | null"))] - pub elevation: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub soil: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub climate: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_id: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_name: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_admin1_id: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_admin1_name: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_country_id: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub gc_country_name: Option<serde_json::Value>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct ILocationGcsFieldsFilter { - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub id: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub created_at: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub updated_at: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub lat: Option<f64>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub lng: Option<f64>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub geohash: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub tag_0: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub label: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub area: Option<f64>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub elevation: Option<u32>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub soil: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub climate: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub gc_id: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub gc_name: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub gc_admin1_id: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub gc_admin1_name: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub gc_country_id: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub gc_country_name: Option<String>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum LocationGcsQueryBindValues { - Id { id: String }, - Geohash { geohash: String }, -} -impl LocationGcsQueryBindValues { - pub fn to_filter_param(&self) -> (&'static str, Value) { - match self { - Self::Id { id } => ("id", Value::from(id.clone())), - Self::Geohash { geohash } => ("geohash", Value::from(geohash.clone())), - } - } - - pub fn primary_key(&self) -> Option<String> { - match self { - Self::Id { id } => Some(id.clone()), - _ => None, - } - } - - pub fn lookup_key(&self) -> String { - match self { - Self::Id { id } => id.clone(), - Self::Geohash { geohash } => geohash.clone(), - } - } -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct LocationGcsTradeProductArgs { - pub id: String, -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub enum LocationGcsFindManyRel { - #[serde(rename = "on_trade_product")] - OnTradeProduct(LocationGcsTradeProductArgs), - #[serde(rename = "off_trade_product")] - OffTradeProduct(LocationGcsTradeProductArgs), - #[serde(rename = "on_farm")] - OnFarm(LocationGcsTradeProductArgs), - #[serde(rename = "off_farm")] - OffFarm(LocationGcsTradeProductArgs), -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "ILocationGcsCreate", - type = "ILocationGcsFields" - ) -)] -pub struct ILocationGcsCreateTs; -pub type ILocationGcsCreate = ILocationGcsFields; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "ILocationGcsCreateResolve", - type = "IResult<LocationGcs>" - ) -)] -pub struct ILocationGcsCreateResolveTs; -pub type ILocationGcsCreateResolve = IResult<LocationGcs>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Deserialize, Serialize)] -pub struct ILocationGcsFindOneArgs { - pub on: LocationGcsQueryBindValues, -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Deserialize, Serialize)] -pub struct ILocationGcsFindOneRelArgs { - pub rel: LocationGcsFindManyRel, -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts(export, export_to = "types.ts", rename = "ILocationGcsFindOne") -)] -#[derive(Deserialize, Serialize)] -#[serde(untagged)] -pub enum ILocationGcsFindOne { - On(ILocationGcsFindOneArgs), - Rel(ILocationGcsFindOneRelArgs), -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "ILocationGcsFindOneResolve", - type = "IResult<LocationGcs>" - ) -)] -pub struct ILocationGcsFindOneResolveTs; -pub type ILocationGcsFindOneResolve = IResult<Option<LocationGcs>>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts(export, export_to = "types.ts", rename = "ILocationGcsFindMany") -)] -#[derive(Deserialize, Serialize)] -#[serde(untagged)] -pub enum ILocationGcsFindMany { - Filter { - filter: Option<ILocationGcsFieldsFilter>, - }, - Rel { - rel: LocationGcsFindManyRel, - }, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "ILocationGcsFindManyResolve", - type = "IResultList<LocationGcs>" - ) -)] -pub struct ILocationGcsFindManyResolveTs; -pub type ILocationGcsFindManyResolve = IResultList<LocationGcs>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "ILocationGcsDelete", - type = "ILocationGcsFindOne" - ) -)] -pub struct ILocationGcsDeleteTs; -pub type ILocationGcsDelete = ILocationGcsFindOne; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "ILocationGcsDeleteResolve", - type = "IResult<string>" - ) -)] -pub struct ILocationGcsDeleteResolveTs; -pub type ILocationGcsDeleteResolve = IResult<String>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts(export, export_to = "types.ts", rename = "ILocationGcsUpdate") -)] -#[derive(Deserialize, Serialize)] -pub struct ILocationGcsUpdateArgs { - pub on: LocationGcsQueryBindValues, - pub fields: ILocationGcsFieldsPartial, -} -pub type ILocationGcsUpdate = ILocationGcsUpdateArgs; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "ILocationGcsUpdateResolve", - type = "IResult<LocationGcs>" - ) -)] -pub struct ILocationGcsUpdateResolveTs; -pub type ILocationGcsUpdateResolve = IResult<LocationGcs>; -\ No newline at end of file diff --git a/tangle-schema/src/models/mod.rs b/tangle-schema/src/models/mod.rs @@ -1,11 +0,0 @@ -pub mod farm; -pub mod farm_location; -pub mod location_gcs; -pub mod log_error; -pub mod media_image; -pub mod nostr_profile; -pub mod nostr_profile_relay; -pub mod nostr_relay; -pub mod trade_product; -pub mod trade_product_location; -pub mod trade_product_media; diff --git a/tangle-schema/src/models/nostr_profile.rs b/tangle-schema/src/models/nostr_profile.rs @@ -1,285 +0,0 @@ -use radroots_types::types::{IResult, IResultList}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -#[cfg(feature = "ts-rs")] -use ts_rs::TS; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Serialize, Deserialize)] -pub struct NostrProfile { - pub id: String, - pub created_at: String, - pub updated_at: String, - pub public_key: String, - pub name: String, - pub display_name: Option<String>, - pub about: Option<String>, - pub website: Option<String>, - pub picture: Option<String>, - pub banner: Option<String>, - pub nip05: Option<String>, - pub lud06: Option<String>, - pub lud16: Option<String>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct INostrProfileFields { - pub public_key: String, - pub name: String, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub display_name: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub about: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub website: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub picture: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub banner: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub nip05: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub lud06: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub lud16: Option<String>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct INostrProfileFieldsPartial { - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub public_key: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub name: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub display_name: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub about: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub website: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub picture: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub banner: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub nip05: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub lud06: Option<serde_json::Value>, - #[cfg_attr(feature = "ts-rs", ts(optional, type = "string | null"))] - pub lud16: Option<serde_json::Value>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct INostrProfileFieldsFilter { - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub id: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub created_at: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub updated_at: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub public_key: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub name: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub display_name: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub about: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub website: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub picture: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub banner: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub nip05: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub lud06: Option<String>, - #[cfg_attr(feature = "ts-rs", ts(optional))] - pub lud16: Option<String>, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum NostrProfileQueryBindValues { - Id { id: String }, - PublicKey { public_key: String }, -} -impl NostrProfileQueryBindValues { - pub fn to_filter_param(&self) -> (&'static str, Value) { - match self { - Self::Id { id } => ("id", Value::from(id.clone())), - Self::PublicKey { public_key } => ("public_key", Value::from(public_key.clone())), - } - } - - pub fn primary_key(&self) -> Option<String> { - match self { - Self::Id { id } => Some(id.clone()), - _ => None, - } - } - - pub fn lookup_key(&self) -> String { - match self { - Self::Id { id } => id.clone(), - Self::PublicKey { public_key } => public_key.clone(), - } - } -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct NostrProfileRelayArgs { - pub id: String, -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub enum NostrProfileFindManyRel { - #[serde(rename = "on_relay")] - OnRelay(NostrProfileRelayArgs), - #[serde(rename = "off_relay")] - OffRelay(NostrProfileRelayArgs), -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "INostrProfileCreate", - type = "INostrProfileFields" - ) -)] -pub struct INostrProfileCreateTs; -pub type INostrProfileCreate = INostrProfileFields; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "INostrProfileCreateResolve", - type = "IResult<NostrProfile>" - ) -)] -pub struct INostrProfileCreateResolveTs; -pub type INostrProfileCreateResolve = IResult<NostrProfile>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Deserialize, Serialize)] -pub struct INostrProfileFindOneArgs { - pub on: NostrProfileQueryBindValues, -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Deserialize, Serialize)] -pub struct INostrProfileFindOneRelArgs { - pub rel: NostrProfileFindManyRel, -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts(export, export_to = "types.ts", rename = "INostrProfileFindOne") -)] -#[derive(Deserialize, Serialize)] -#[serde(untagged)] -pub enum INostrProfileFindOne { - On(INostrProfileFindOneArgs), - Rel(INostrProfileFindOneRelArgs), -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "INostrProfileFindOneResolve", - type = "IResult<NostrProfile>" - ) -)] -pub struct INostrProfileFindOneResolveTs; -pub type INostrProfileFindOneResolve = IResult<Option<NostrProfile>>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts(export, export_to = "types.ts", rename = "INostrProfileFindMany") -)] -#[derive(Deserialize, Serialize)] -#[serde(untagged)] -pub enum INostrProfileFindMany { - Filter { - filter: Option<INostrProfileFieldsFilter>, - }, - Rel { - rel: NostrProfileFindManyRel, - }, -} -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "INostrProfileFindManyResolve", - type = "IResultList<NostrProfile>" - ) -)] -pub struct INostrProfileFindManyResolveTs; -pub type INostrProfileFindManyResolve = IResultList<NostrProfile>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "INostrProfileDelete", - type = "INostrProfileFindOne" - ) -)] -pub struct INostrProfileDeleteTs; -pub type INostrProfileDelete = INostrProfileFindOne; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "INostrProfileDeleteResolve", - type = "IResult<string>" - ) -)] -pub struct INostrProfileDeleteResolveTs; -pub type INostrProfileDeleteResolve = IResult<String>; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts(export, export_to = "types.ts", rename = "INostrProfileUpdate") -)] -#[derive(Deserialize, Serialize)] -pub struct INostrProfileUpdateArgs { - pub on: NostrProfileQueryBindValues, - pub fields: INostrProfileFieldsPartial, -} -pub type INostrProfileUpdate = INostrProfileUpdateArgs; -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "INostrProfileUpdateResolve", - type = "IResult<NostrProfile>" - ) -)] -pub struct INostrProfileUpdateResolveTs; -pub type INostrProfileUpdateResolve = IResult<NostrProfile>; -\ No newline at end of file diff --git a/tangle-schema/src/models/trade_product_location.rs b/tangle-schema/src/models/trade_product_location.rs @@ -1,27 +0,0 @@ -use radroots_types::types::IResultPass; -use serde::{Deserialize, Serialize}; -#[cfg(feature = "ts-rs")] -use ts_rs::TS; -use crate::trade_product::TradeProductQueryBindValues; -use crate::location_gcs::LocationGcsQueryBindValues; - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] -#[derive(Clone, Deserialize, Serialize)] -pub struct ITradeProductLocationRelation { - pub trade_product: TradeProductQueryBindValues, - pub location_gcs: LocationGcsQueryBindValues, -} - -#[cfg_attr(feature = "ts-rs", derive(TS))] -#[cfg_attr( - feature = "ts-rs", - ts( - export, - export_to = "types.ts", - rename = "ITradeProductLocationResolve", - type = "IResultPass" - ) -)] -pub struct ITradeProductLocationResolveTs; -pub type ITradeProductLocationResolve = IResultPass; diff --git a/tangle-sql-wasm/Cargo.toml b/tangle-sql-wasm/Cargo.toml @@ -1,20 +0,0 @@ -[package] -name = "radroots-tangle-sql-wasm" -version.workspace = true -edition.workspace = true -authors = ["Radroots Authors"] -rust-version.workspace = true -license.workspace = true - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -radroots-sql-core = { workspace = true, features = ["web"] } -radroots-sql-wasm-core = { workspace = true } -radroots-tangle-sql = { workspace = true } -radroots-tangle-schema = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -serde-wasm-bindgen = { workspace = true } -wasm-bindgen = { workspace = true } diff --git a/tangle-sql-wasm/pkg/package.json b/tangle-sql-wasm/pkg/package.json @@ -1,19 +0,0 @@ -{ - "name": "@radroots/tangle-sql-wasm", - "version": "0.1.0", - "private": true, - "type": "module", - "files": [ - "dist" - ], - "main": "./dist/radroots_tangle_sql_wasm.js", - "types": "./dist/radroots_tangle_sql_wasm.d.ts", - "exports": { - ".": { - "types": "./dist/radroots_tangle_sql_wasm.d.ts", - "import": "./dist/radroots_tangle_sql_wasm.js", - "default": "./dist/radroots_tangle_sql_wasm.js" - } - }, - "sideEffects": false -} -\ No newline at end of file diff --git a/tangle-sql-wasm/src/lib.rs b/tangle-sql-wasm/src/lib.rs @@ -1,493 +0,0 @@ -#![cfg(target_arch = "wasm32")] - -use radroots_sql_core::WasmSqlExecutor; -use radroots_sql_wasm_core::{err_js, parse_json}; -use radroots_tangle_sql::migrations; -use wasm_bindgen::prelude::*; - -use radroots_tangle_schema::farm::{ - IFarmCreate, - IFarmDelete, - IFarmFindMany, - IFarmFindOne, - IFarmUpdate, -}; - -use radroots_tangle_schema::location_gcs::{ - ILocationGcsCreate, - ILocationGcsDelete, - ILocationGcsFindMany, - ILocationGcsFindOne, - ILocationGcsUpdate, -}; - -use radroots_tangle_schema::log_error::{ - ILogErrorCreate, - ILogErrorDelete, - ILogErrorFindMany, - ILogErrorFindOne, - ILogErrorUpdate, -}; - -use radroots_tangle_schema::media_image::{ - IMediaImageCreate, - IMediaImageDelete, - IMediaImageFindMany, - IMediaImageFindOne, - IMediaImageUpdate, -}; - -use radroots_tangle_schema::nostr_profile::{ - INostrProfileCreate, - INostrProfileDelete, - INostrProfileFindMany, - INostrProfileFindOne, - INostrProfileUpdate, -}; - -use radroots_tangle_schema::nostr_relay::{ - INostrRelayCreate, - INostrRelayDelete, - INostrRelayFindMany, - INostrRelayFindOne, - INostrRelayUpdate, -}; - -use radroots_tangle_schema::trade_product::{ - ITradeProductCreate, - ITradeProductDelete, - ITradeProductFindMany, - ITradeProductFindOne, - ITradeProductUpdate, -}; - -use radroots_tangle_schema::farm_location::{ - IFarmLocationRelation, -}; - -use radroots_tangle_schema::nostr_profile_relay::{ - INostrProfileRelayRelation, -}; - -use radroots_tangle_schema::trade_product_location::{ - ITradeProductLocationRelation, -}; - -use radroots_tangle_schema::trade_product_media::{ - ITradeProductMediaRelation, -}; - -pub mod utils; -pub use utils::*; - -#[wasm_bindgen(js_name = tangle_db_run_migrations)] -pub fn tangle_db_run_migrations() -> Result<(), JsValue> { - let exec = WasmSqlExecutor::new(); - migrations::run_all_up(&exec).map_err(err_js) -} - -#[wasm_bindgen(js_name = tangle_db_reset_database)] -pub fn tangle_db_reset_database() -> Result<(), JsValue> { - let exec = WasmSqlExecutor::new(); - migrations::run_all_down(&exec).map_err(err_js) -} - -#[wasm_bindgen(js_name = tangle_db_export_backup)] -pub fn tangle_db_export_backup() -> Result<JsValue, JsValue> { - let exec = WasmSqlExecutor::new(); - let dump = radroots_tangle_sql::backup::export_database_backup(&exec).map_err(err_js)?; - value_to_js(dump) -} - -#[wasm_bindgen(js_name = tangle_db_import_backup)] -pub fn tangle_db_import_backup(dump_json: &str) -> Result<(), JsValue> { - let exec = WasmSqlExecutor::new(); - radroots_tangle_sql::backup::restore_database_backup_json(&exec, dump_json).map_err(err_js) -} - -#[wasm_bindgen(js_name = tangle_db_farm_create)] -pub fn tangle_db_farm_create(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IFarmCreate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::farm::create(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_farm_find_one)] -pub fn tangle_db_farm_find_one(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IFarmFindOne = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::farm::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_farm_find_many)] -pub fn tangle_db_farm_find_many(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IFarmFindMany = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::farm::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_farm_update)] -pub fn tangle_db_farm_update(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IFarmUpdate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::farm::update(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_farm_delete)] -pub fn tangle_db_farm_delete(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IFarmDelete = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::farm::delete(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_location_gcs_create)] -pub fn tangle_db_location_gcs_create(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ILocationGcsCreate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::location_gcs::create(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_location_gcs_find_one)] -pub fn tangle_db_location_gcs_find_one(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ILocationGcsFindOne = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::location_gcs::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_location_gcs_find_many)] -pub fn tangle_db_location_gcs_find_many(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ILocationGcsFindMany = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::location_gcs::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_location_gcs_update)] -pub fn tangle_db_location_gcs_update(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ILocationGcsUpdate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::location_gcs::update(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_location_gcs_delete)] -pub fn tangle_db_location_gcs_delete(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ILocationGcsDelete = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::location_gcs::delete(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_log_error_create)] -pub fn tangle_db_log_error_create(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ILogErrorCreate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::log_error::create(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_log_error_find_one)] -pub fn tangle_db_log_error_find_one(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ILogErrorFindOne = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::log_error::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_log_error_find_many)] -pub fn tangle_db_log_error_find_many(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ILogErrorFindMany = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::log_error::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_log_error_update)] -pub fn tangle_db_log_error_update(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ILogErrorUpdate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::log_error::update(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_log_error_delete)] -pub fn tangle_db_log_error_delete(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ILogErrorDelete = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::log_error::delete(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_media_image_create)] -pub fn tangle_db_media_image_create(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IMediaImageCreate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::media_image::create(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_media_image_find_one)] -pub fn tangle_db_media_image_find_one(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IMediaImageFindOne = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::media_image::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_media_image_find_many)] -pub fn tangle_db_media_image_find_many(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IMediaImageFindMany = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::media_image::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_media_image_update)] -pub fn tangle_db_media_image_update(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IMediaImageUpdate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::media_image::update(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_media_image_delete)] -pub fn tangle_db_media_image_delete(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IMediaImageDelete = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::media_image::delete(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_profile_create)] -pub fn tangle_db_nostr_profile_create(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrProfileCreate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_profile::create(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_profile_find_one)] -pub fn tangle_db_nostr_profile_find_one(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrProfileFindOne = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_profile::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_profile_find_many)] -pub fn tangle_db_nostr_profile_find_many(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrProfileFindMany = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_profile::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_profile_update)] -pub fn tangle_db_nostr_profile_update(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrProfileUpdate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_profile::update(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_profile_delete)] -pub fn tangle_db_nostr_profile_delete(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrProfileDelete = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_profile::delete(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_relay_create)] -pub fn tangle_db_nostr_relay_create(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrRelayCreate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_relay::create(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_relay_find_one)] -pub fn tangle_db_nostr_relay_find_one(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrRelayFindOne = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_relay::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_relay_find_many)] -pub fn tangle_db_nostr_relay_find_many(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrRelayFindMany = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_relay::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_relay_update)] -pub fn tangle_db_nostr_relay_update(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrRelayUpdate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_relay::update(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_relay_delete)] -pub fn tangle_db_nostr_relay_delete(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrRelayDelete = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_relay::delete(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_trade_product_create)] -pub fn tangle_db_trade_product_create(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ITradeProductCreate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::trade_product::create(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_trade_product_find_one)] -pub fn tangle_db_trade_product_find_one(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ITradeProductFindOne = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::trade_product::find_one(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_trade_product_find_many)] -pub fn tangle_db_trade_product_find_many(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ITradeProductFindMany = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::trade_product::find_many(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_trade_product_update)] -pub fn tangle_db_trade_product_update(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ITradeProductUpdate = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::trade_product::update(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_trade_product_delete)] -pub fn tangle_db_trade_product_delete(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ITradeProductDelete = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::trade_product::delete(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_farm_location_set)] -pub fn tangle_db_farm_location_set(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IFarmLocationRelation = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::farm_location::set(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_farm_location_unset)] -pub fn tangle_db_farm_location_unset(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: IFarmLocationRelation = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::farm_location::unset(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_profile_relay_set)] -pub fn tangle_db_nostr_profile_relay_set(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrProfileRelayRelation = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_profile_relay::set(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_nostr_profile_relay_unset)] -pub fn tangle_db_nostr_profile_relay_unset(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: INostrProfileRelayRelation = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::nostr_profile_relay::unset(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_trade_product_location_set)] -pub fn tangle_db_trade_product_location_set(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ITradeProductLocationRelation = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::trade_product_location::set(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_trade_product_location_unset)] -pub fn tangle_db_trade_product_location_unset(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ITradeProductLocationRelation = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::trade_product_location::unset(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_trade_product_media_set)] -pub fn tangle_db_trade_product_media_set(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ITradeProductMediaRelation = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::trade_product_media::set(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} - -#[wasm_bindgen(js_name = tangle_db_trade_product_media_unset)] -pub fn tangle_db_trade_product_media_unset(opts_json: &str) -> Result<JsValue, JsValue> { - let opts: ITradeProductMediaRelation = parse_json(opts_json).map_err(err_js)?; - let exec = WasmSqlExecutor::new(); - let out = - radroots_tangle_sql::trade_product_media::unset(&exec, &opts).map_err(|e| err_js(e.err))?; - value_to_js(out) -} diff --git a/tangle-sql/Cargo.toml b/tangle-sql/Cargo.toml @@ -1,23 +0,0 @@ -[package] -name = "radroots-tangle-sql" -version.workspace = true -edition.workspace = true -authors = ["Radroots Authors"] -rust-version.workspace = true -license.workspace = true - -[lib] -crate-type = ["rlib"] - -[features] -default = [] -web = ["radroots-sql-core/web"] -native = ["radroots-sql-core/native"] -embedded = ["radroots-sql-core/embedded"] - -[dependencies] -radroots-sql-core = { workspace = true } -radroots-tangle-schema = { workspace = true } -radroots-types = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } diff --git a/tangle-sql/migrations/0002_farm.up.sql b/tangle-sql/migrations/0002_farm.up.sql @@ -1,10 +0,0 @@ -CREATE TABLE IF NOT EXISTS farm ( - id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), - created_at DATETIME NOT NULL CHECK(length(created_at) = 24), - updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), - name TEXT NOT NULL, - area TEXT, - area_unit TEXT, - title TEXT, - description TEXT -); -\ No newline at end of file diff --git a/tangle-sql/migrations/0003_location_gcs.down.sql b/tangle-sql/migrations/0003_location_gcs.down.sql @@ -1 +0,0 @@ -DROP TABLE IF EXISTS location_gcs; -\ No newline at end of file diff --git a/tangle-sql/migrations/0003_location_gcs.up.sql b/tangle-sql/migrations/0003_location_gcs.up.sql @@ -1,20 +0,0 @@ -CREATE TABLE IF NOT EXISTS location_gcs ( - id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), - created_at DATETIME NOT NULL CHECK(length(created_at) = 24), - updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), - lat REAL NOT NULL, - lng REAL NOT NULL, - geohash TEXT, - tag_0 TEXT, - label TEXT, - area REAL, - elevation INTEGER, - soil TEXT, - climate TEXT, - gc_id TEXT, - gc_name TEXT, - gc_admin1_id TEXT, - gc_admin1_name TEXT, - gc_country_id TEXT, - gc_country_name TEXT -); -\ No newline at end of file diff --git a/tangle-sql/migrations/0005_nostr_profile.up.sql b/tangle-sql/migrations/0005_nostr_profile.up.sql @@ -1,15 +0,0 @@ -CREATE TABLE IF NOT EXISTS nostr_profile ( - id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), - created_at DATETIME NOT NULL CHECK(length(created_at) = 24), - updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), - public_key CHAR(64) NOT NULL CHECK(length(public_key) = 64), - name TEXT NOT NULL, - display_name TEXT, - about TEXT, - website TEXT, - picture TEXT, - banner TEXT, - nip05 TEXT, - lud06 TEXT, - lud16 TEXT -); -\ No newline at end of file diff --git a/tangle-sql/migrations/0008_farm_location.down.sql b/tangle-sql/migrations/0008_farm_location.down.sql @@ -1 +0,0 @@ -DROP TABLE IF EXISTS farm_location; -\ No newline at end of file diff --git a/tangle-sql/migrations/0008_farm_location.up.sql b/tangle-sql/migrations/0008_farm_location.up.sql @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS farm_location ( - tb_farm CHAR(36), - tb_lg CHAR(36), - FOREIGN KEY (tb_farm) REFERENCES farm(id) ON DELETE CASCADE, - FOREIGN KEY (tb_lg) REFERENCES location_gcs(id) ON DELETE CASCADE, - PRIMARY KEY (tb_farm, tb_lg) -); -\ No newline at end of file diff --git a/tangle-sql/migrations/0010_trade_product_location.up.sql b/tangle-sql/migrations/0010_trade_product_location.up.sql @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS trade_product_location ( - tb_tp CHAR(36), - tb_lg CHAR(36), - FOREIGN KEY (tb_tp) REFERENCES trade_product(id) ON DELETE CASCADE, - FOREIGN KEY (tb_lg) REFERENCES location_gcs(id) ON DELETE CASCADE, - PRIMARY KEY (tb_tp, tb_lg) -); -\ No newline at end of file diff --git a/tangle-sql/src/backup.rs b/tangle-sql/src/backup.rs @@ -1,290 +0,0 @@ -use radroots_sql_core::{SqlExecutor, error::SqlError, utils}; -use serde::{Deserialize, Serialize}; -use serde_json::{Map, Value}; -use std::collections::{BTreeMap, HashMap}; - -pub const DATABASE_BACKUP_VERSION: &str = "1.0.0"; -pub const TANGLE_SQL_VERSION: &str = env!("CARGO_PKG_VERSION"); - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SchemaEntry { - pub object_type: String, - pub name: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub table_name: Option<String>, - #[serde(skip_serializing_if = "Option::is_none")] - pub sql: Option<String>, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TableData { - pub name: String, - pub rows: Vec<Map<String, Value>>, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MigrationBackup { - pub name: String, - pub up_sql: String, - pub down_sql: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DatabaseBackup { - pub format_version: String, - pub tangle_sql_version: String, - pub schema: Vec<SchemaEntry>, - pub migrations: Vec<MigrationBackup>, - pub data: Vec<TableData>, -} - -pub fn export_database_backup<E: SqlExecutor>(executor: &E) -> Result<DatabaseBackup, SqlError> { - let schema = load_schema(executor)?; - let data = read_tables_for_backup(executor, &schema)?; - let migrations = crate::migrations::MIGRATIONS - .iter() - .map(|m| MigrationBackup { - name: m.name.to_string(), - up_sql: m.up_sql.to_string(), - down_sql: m.down_sql.to_string(), - }) - .collect(); - Ok(DatabaseBackup { - format_version: DATABASE_BACKUP_VERSION.to_string(), - tangle_sql_version: TANGLE_SQL_VERSION.to_string(), - schema, - migrations, - data, - }) -} - -pub fn export_database_backup_json<E: SqlExecutor>(executor: &E) -> Result<String, SqlError> { - let backup = export_database_backup(executor)?; - serde_json::to_string(&backup).map_err(SqlError::from) -} - -pub fn restore_database_backup<E: SqlExecutor>( - executor: &E, - backup: &DatabaseBackup, -) -> Result<(), SqlError> { - validate_backup_version(backup)?; - executor.exec("PRAGMA foreign_keys = OFF;", "[]")?; - executor.begin()?; - let result = (|| { - drop_existing_objects(executor)?; - create_schema_from_backup(executor, &backup.schema)?; - insert_rows_from_backup(executor, backup)?; - Ok(()) - })(); - - match result { - Ok(()) => { - executor.commit()?; - let _ = executor.exec("PRAGMA foreign_keys = ON;", "[]")?; - Ok(()) - } - Err(err) => { - let _ = executor.rollback(); - let _ = executor.exec("PRAGMA foreign_keys = ON;", "[]"); - Err(err) - } - } -} - -pub fn restore_database_backup_json<E: SqlExecutor>( - executor: &E, - backup_json: &str, -) -> Result<(), SqlError> { - let backup: DatabaseBackup = serde_json::from_str(backup_json).map_err(SqlError::from)?; - restore_database_backup(executor, &backup) -} - -fn drop_existing_objects<E: SqlExecutor>(executor: &E) -> Result<(), SqlError> { - #[derive(Deserialize)] - struct MasterRow { - #[serde(rename = "type")] - object_type: Option<String>, - name: Option<String>, - } - let json = executor.query_raw( - "select type, name from sqlite_master where name not like 'sqlite_%'", - "[]", - )?; - let rows: Vec<MasterRow> = utils::parse_json(&json)?; - - let mut groups: HashMap<String, Vec<String>> = HashMap::new(); - for row in rows.into_iter() { - let obj_type = row.object_type.unwrap_or_default(); - let name = match row.name { - Some(n) => n, - None => continue, - }; - groups.entry(obj_type).or_default().push(name); - } - - for object_type in ["trigger", "view", "index", "table"] { - if let Some(names) = groups.get(object_type) { - for name in names { - let stmt = match object_type { - "trigger" => format!("DROP TRIGGER IF EXISTS {};", escape_identifier(name)), - "view" => format!("DROP VIEW IF EXISTS {};", escape_identifier(name)), - "index" => format!("DROP INDEX IF EXISTS {};", escape_identifier(name)), - _ => format!("DROP TABLE IF EXISTS {};", escape_identifier(name)), - }; - let _ = executor.exec(&stmt, "[]")?; - } - } - } - Ok(()) -} - -fn create_schema_from_backup<E: SqlExecutor>( - executor: &E, - schema: &[SchemaEntry], -) -> Result<(), SqlError> { - for entry in schema.iter().filter(|s| s.object_type == "table") { - if let Some(sql) = &entry.sql { - executor.exec(sql, "[]")?; - } - } - for entry in schema - .iter() - .filter(|s| s.object_type != "table" && s.sql.is_some()) - { - if let Some(sql) = &entry.sql { - executor.exec(sql, "[]")?; - } - } - Ok(()) -} - -fn insert_rows_from_backup<E: SqlExecutor>( - executor: &E, - backup: &DatabaseBackup, -) -> Result<(), SqlError> { - let mut row_sources: HashMap<&str, &Vec<Map<String, Value>>> = HashMap::new(); - for table in &backup.data { - row_sources.insert(table.name.as_str(), &table.rows); - } - for entry in backup.schema.iter().filter(|s| s.object_type == "table") { - let rows = match row_sources.get(entry.name.as_str()) { - Some(r) => *r, - None => continue, - }; - for row in rows { - insert_row(executor, &entry.name, row)?; - } - } - Ok(()) -} - -fn insert_row<E: SqlExecutor>( - executor: &E, - table: &str, - row: &Map<String, Value>, -) -> Result<(), SqlError> { - if row.is_empty() { - return Ok(()); - } - - let mut cols: BTreeMap<String, &Value> = BTreeMap::new(); - for (k, v) in row { - cols.insert(k.clone(), v); - } - - let column_names: Vec<String> = cols.keys().cloned().collect(); - let placeholders = (0..column_names.len()) - .map(|_| "?") - .collect::<Vec<_>>() - .join(","); - let sql = format!( - "INSERT INTO {} ({}) VALUES ({});", - escape_identifier(table), - column_names - .iter() - .map(|c| escape_identifier(c)) - .collect::<Vec<_>>() - .join(","), - placeholders - ); - - let binds: Vec<Value> = cols.values().map(|v| utils::to_db_bind_value(*v)).collect(); - let params_json = serde_json::to_string(&binds).map_err(SqlError::from)?; - executor.exec(&sql, ¶ms_json)?; - Ok(()) -} - -fn load_schema<E: SqlExecutor>(executor: &E) -> Result<Vec<SchemaEntry>, SqlError> { - let json = executor.query_raw( - "select type, name, tbl_name as table_name, sql from sqlite_master where name not like 'sqlite_%' order by type, name", - "[]", - )?; - #[derive(Deserialize)] - struct RawSchema { - #[serde(rename = "type")] - object_type: Option<String>, - name: Option<String>, - table_name: Option<String>, - sql: Option<String>, - } - let rows: Vec<RawSchema> = utils::parse_json(&json)?; - Ok(rows - .into_iter() - .filter_map(|row| { - let name = row.name?; - let object_type = row.object_type.unwrap_or_default(); - Some(SchemaEntry { - object_type, - name, - table_name: row.table_name, - sql: row.sql, - }) - }) - .collect()) -} - -fn read_tables_for_backup<E: SqlExecutor>( - executor: &E, - schema: &[SchemaEntry], -) -> Result<Vec<TableData>, SqlError> { - let mut data = Vec::new(); - for entry in schema.iter().filter(|s| s.object_type == "table") { - let select_sql = format!("SELECT * FROM {};", escape_identifier(&entry.name)); - let json = executor.query_raw(&select_sql, "[]")?; - let rows: Vec<Map<String, Value>> = utils::parse_json(&json)?; - data.push(TableData { - name: entry.name.clone(), - rows, - }); - } - Ok(data) -} - -fn escape_identifier(name: &str) -> String { - let mut escaped = String::with_capacity(name.len() + 2); - escaped.push('"'); - for c in name.chars() { - if c == '"' { - escaped.push('"'); - } - escaped.push(c); - } - escaped.push('"'); - escaped -} - -fn validate_backup_version(backup: &DatabaseBackup) -> Result<(), SqlError> { - if backup.format_version != DATABASE_BACKUP_VERSION { - return Err(SqlError::InvalidArgument(format!( - "unsupported backup format {}, expected {}", - backup.format_version, DATABASE_BACKUP_VERSION - ))); - } - if backup.tangle_sql_version != TANGLE_SQL_VERSION { - return Err(SqlError::InvalidArgument(format!( - "unsupported tangle-sql version {}, expected {}", - backup.tangle_sql_version, TANGLE_SQL_VERSION - ))); - } - Ok(()) -} diff --git a/tangle-sql/src/lib.rs b/tangle-sql/src/lib.rs @@ -1,460 +0,0 @@ -pub use radroots_sql_core::error::SqlError; -pub use radroots_sql_core::{ExecOutcome, SqlExecutor}; -use radroots_types::types::IError; - -use radroots_tangle_schema::farm::{ - IFarmCreate, - IFarmCreateResolve, - IFarmDelete, - IFarmDeleteResolve, - IFarmFindMany, - IFarmFindManyResolve, - IFarmFindOne, - IFarmFindOneResolve, - IFarmUpdate, - IFarmUpdateResolve, -}; - -use radroots_tangle_schema::location_gcs::{ - ILocationGcsCreate, - ILocationGcsCreateResolve, - ILocationGcsDelete, - ILocationGcsDeleteResolve, - ILocationGcsFindMany, - ILocationGcsFindManyResolve, - ILocationGcsFindOne, - ILocationGcsFindOneResolve, - ILocationGcsUpdate, - ILocationGcsUpdateResolve, -}; - -use radroots_tangle_schema::log_error::{ - ILogErrorCreate, - ILogErrorCreateResolve, - ILogErrorDelete, - ILogErrorDeleteResolve, - ILogErrorFindMany, - ILogErrorFindManyResolve, - ILogErrorFindOne, - ILogErrorFindOneResolve, - ILogErrorUpdate, - ILogErrorUpdateResolve, -}; - -use radroots_tangle_schema::media_image::{ - IMediaImageCreate, - IMediaImageCreateResolve, - IMediaImageDelete, - IMediaImageDeleteResolve, - IMediaImageFindMany, - IMediaImageFindManyResolve, - IMediaImageFindOne, - IMediaImageFindOneResolve, - IMediaImageUpdate, - IMediaImageUpdateResolve, -}; - -use radroots_tangle_schema::nostr_profile::{ - INostrProfileCreate, - INostrProfileCreateResolve, - INostrProfileDelete, - INostrProfileDeleteResolve, - INostrProfileFindMany, - INostrProfileFindManyResolve, - INostrProfileFindOne, - INostrProfileFindOneResolve, - INostrProfileUpdate, - INostrProfileUpdateResolve, -}; - -use radroots_tangle_schema::nostr_relay::{ - INostrRelayCreate, - INostrRelayCreateResolve, - INostrRelayDelete, - INostrRelayDeleteResolve, - INostrRelayFindMany, - INostrRelayFindManyResolve, - INostrRelayFindOne, - INostrRelayFindOneResolve, - INostrRelayUpdate, - INostrRelayUpdateResolve, -}; - -use radroots_tangle_schema::trade_product::{ - ITradeProductCreate, - ITradeProductCreateResolve, - ITradeProductDelete, - ITradeProductDeleteResolve, - ITradeProductFindMany, - ITradeProductFindManyResolve, - ITradeProductFindOne, - ITradeProductFindOneResolve, - ITradeProductUpdate, - ITradeProductUpdateResolve, -}; - -use radroots_tangle_schema::farm_location::{ - IFarmLocationRelation, - IFarmLocationResolve, -}; - -use radroots_tangle_schema::nostr_profile_relay::{ - INostrProfileRelayRelation, - INostrProfileRelayResolve, -}; - -use radroots_tangle_schema::trade_product_location::{ - ITradeProductLocationRelation, - ITradeProductLocationResolve, -}; - -use radroots_tangle_schema::trade_product_media::{ - ITradeProductMediaRelation, - ITradeProductMediaResolve, -}; - -pub mod backup; -pub mod migrations; -pub mod models; -pub use backup::{DatabaseBackup, MigrationBackup, SchemaEntry}; -pub use models::*; - -pub struct TangleSql<E: SqlExecutor> { - executor: E, -} - -impl<E: SqlExecutor> TangleSql<E> { - pub fn new(executor: E) -> Self { - Self { executor } - } - - pub fn executor(&self) -> &E { - &self.executor - } - - pub fn migrate_up(&self) -> Result<(), SqlError> { - crate::migrations::run_all_up(self.executor()) - } - - pub fn migrate_down(&self) -> Result<(), SqlError> { - crate::migrations::run_all_down(self.executor()) - } - - pub fn backup_database(&self) -> Result<DatabaseBackup, SqlError> { - crate::backup::export_database_backup(self.executor()) - } - - pub fn backup_database_json(&self) -> Result<String, SqlError> { - crate::backup::export_database_backup_json(self.executor()) - } - - pub fn restore_database(&self, backup: &DatabaseBackup) -> Result<(), SqlError> { - crate::backup::restore_database_backup(self.executor(), backup) - } - - pub fn restore_database_json(&self, backup_json: &str) -> Result<(), SqlError> { - crate::backup::restore_database_backup_json(self.executor(), backup_json) - } - - pub fn farm_create( - &self, - opts: &IFarmCreate, - ) -> Result<IFarmCreateResolve, IError<SqlError>> { - models::farm::create(self.executor(), opts) - } - - pub fn farm_find_many( - &self, - opts: &IFarmFindMany, - ) -> Result<IFarmFindManyResolve, IError<SqlError>> { - models::farm::find_many(self.executor(), opts) - } - - pub fn farm_find_one( - &self, - opts: &IFarmFindOne, - ) -> Result<IFarmFindOneResolve, IError<SqlError>> { - models::farm::find_one(self.executor(), opts) - } - - pub fn farm_update( - &self, - opts: &IFarmUpdate, - ) -> Result<IFarmUpdateResolve, IError<SqlError>> { - models::farm::update(self.executor(), opts) - } - - pub fn farm_delete( - &self, - opts: &IFarmDelete, - ) -> Result<IFarmDeleteResolve, IError<SqlError>> { - models::farm::delete(self.executor(), opts) - } - - pub fn location_gcs_create( - &self, - opts: &ILocationGcsCreate, - ) -> Result<ILocationGcsCreateResolve, IError<SqlError>> { - models::location_gcs::create(self.executor(), opts) - } - - pub fn location_gcs_find_many( - &self, - opts: &ILocationGcsFindMany, - ) -> Result<ILocationGcsFindManyResolve, IError<SqlError>> { - models::location_gcs::find_many(self.executor(), opts) - } - - pub fn location_gcs_find_one( - &self, - opts: &ILocationGcsFindOne, - ) -> Result<ILocationGcsFindOneResolve, IError<SqlError>> { - models::location_gcs::find_one(self.executor(), opts) - } - - pub fn location_gcs_update( - &self, - opts: &ILocationGcsUpdate, - ) -> Result<ILocationGcsUpdateResolve, IError<SqlError>> { - models::location_gcs::update(self.executor(), opts) - } - - pub fn location_gcs_delete( - &self, - opts: &ILocationGcsDelete, - ) -> Result<ILocationGcsDeleteResolve, IError<SqlError>> { - models::location_gcs::delete(self.executor(), opts) - } - - pub fn log_error_create( - &self, - opts: &ILogErrorCreate, - ) -> Result<ILogErrorCreateResolve, IError<SqlError>> { - models::log_error::create(self.executor(), opts) - } - - pub fn log_error_find_many( - &self, - opts: &ILogErrorFindMany, - ) -> Result<ILogErrorFindManyResolve, IError<SqlError>> { - models::log_error::find_many(self.executor(), opts) - } - - pub fn log_error_find_one( - &self, - opts: &ILogErrorFindOne, - ) -> Result<ILogErrorFindOneResolve, IError<SqlError>> { - models::log_error::find_one(self.executor(), opts) - } - - pub fn log_error_update( - &self, - opts: &ILogErrorUpdate, - ) -> Result<ILogErrorUpdateResolve, IError<SqlError>> { - models::log_error::update(self.executor(), opts) - } - - pub fn log_error_delete( - &self, - opts: &ILogErrorDelete, - ) -> Result<ILogErrorDeleteResolve, IError<SqlError>> { - models::log_error::delete(self.executor(), opts) - } - - pub fn media_image_create( - &self, - opts: &IMediaImageCreate, - ) -> Result<IMediaImageCreateResolve, IError<SqlError>> { - models::media_image::create(self.executor(), opts) - } - - pub fn media_image_find_many( - &self, - opts: &IMediaImageFindMany, - ) -> Result<IMediaImageFindManyResolve, IError<SqlError>> { - models::media_image::find_many(self.executor(), opts) - } - - pub fn media_image_find_one( - &self, - opts: &IMediaImageFindOne, - ) -> Result<IMediaImageFindOneResolve, IError<SqlError>> { - models::media_image::find_one(self.executor(), opts) - } - - pub fn media_image_update( - &self, - opts: &IMediaImageUpdate, - ) -> Result<IMediaImageUpdateResolve, IError<SqlError>> { - models::media_image::update(self.executor(), opts) - } - - pub fn media_image_delete( - &self, - opts: &IMediaImageDelete, - ) -> Result<IMediaImageDeleteResolve, IError<SqlError>> { - models::media_image::delete(self.executor(), opts) - } - - pub fn nostr_profile_create( - &self, - opts: &INostrProfileCreate, - ) -> Result<INostrProfileCreateResolve, IError<SqlError>> { - models::nostr_profile::create(self.executor(), opts) - } - - pub fn nostr_profile_find_many( - &self, - opts: &INostrProfileFindMany, - ) -> Result<INostrProfileFindManyResolve, IError<SqlError>> { - models::nostr_profile::find_many(self.executor(), opts) - } - - pub fn nostr_profile_find_one( - &self, - opts: &INostrProfileFindOne, - ) -> Result<INostrProfileFindOneResolve, IError<SqlError>> { - models::nostr_profile::find_one(self.executor(), opts) - } - - pub fn nostr_profile_update( - &self, - opts: &INostrProfileUpdate, - ) -> Result<INostrProfileUpdateResolve, IError<SqlError>> { - models::nostr_profile::update(self.executor(), opts) - } - - pub fn nostr_profile_delete( - &self, - opts: &INostrProfileDelete, - ) -> Result<INostrProfileDeleteResolve, IError<SqlError>> { - models::nostr_profile::delete(self.executor(), opts) - } - - pub fn nostr_relay_create( - &self, - opts: &INostrRelayCreate, - ) -> Result<INostrRelayCreateResolve, IError<SqlError>> { - models::nostr_relay::create(self.executor(), opts) - } - - pub fn nostr_relay_find_many( - &self, - opts: &INostrRelayFindMany, - ) -> Result<INostrRelayFindManyResolve, IError<SqlError>> { - models::nostr_relay::find_many(self.executor(), opts) - } - - pub fn nostr_relay_find_one( - &self, - opts: &INostrRelayFindOne, - ) -> Result<INostrRelayFindOneResolve, IError<SqlError>> { - models::nostr_relay::find_one(self.executor(), opts) - } - - pub fn nostr_relay_update( - &self, - opts: &INostrRelayUpdate, - ) -> Result<INostrRelayUpdateResolve, IError<SqlError>> { - models::nostr_relay::update(self.executor(), opts) - } - - pub fn nostr_relay_delete( - &self, - opts: &INostrRelayDelete, - ) -> Result<INostrRelayDeleteResolve, IError<SqlError>> { - models::nostr_relay::delete(self.executor(), opts) - } - - pub fn trade_product_create( - &self, - opts: &ITradeProductCreate, - ) -> Result<ITradeProductCreateResolve, IError<SqlError>> { - models::trade_product::create(self.executor(), opts) - } - - pub fn trade_product_find_many( - &self, - opts: &ITradeProductFindMany, - ) -> Result<ITradeProductFindManyResolve, IError<SqlError>> { - models::trade_product::find_many(self.executor(), opts) - } - - pub fn trade_product_find_one( - &self, - opts: &ITradeProductFindOne, - ) -> Result<ITradeProductFindOneResolve, IError<SqlError>> { - models::trade_product::find_one(self.executor(), opts) - } - - pub fn trade_product_update( - &self, - opts: &ITradeProductUpdate, - ) -> Result<ITradeProductUpdateResolve, IError<SqlError>> { - models::trade_product::update(self.executor(), opts) - } - - pub fn trade_product_delete( - &self, - opts: &ITradeProductDelete, - ) -> Result<ITradeProductDeleteResolve, IError<SqlError>> { - models::trade_product::delete(self.executor(), opts) - } - - pub fn farm_location_set( - &self, - opts: &IFarmLocationRelation, - ) -> Result<IFarmLocationResolve, IError<SqlError>> { - models::farm_location::set(self.executor(), opts) - } - - pub fn farm_location_unset( - &self, - opts: &IFarmLocationRelation, - ) -> Result<IFarmLocationResolve, IError<SqlError>> { - models::farm_location::unset(self.executor(), opts) - } - - pub fn nostr_profile_relay_set( - &self, - opts: &INostrProfileRelayRelation, - ) -> Result<INostrProfileRelayResolve, IError<SqlError>> { - models::nostr_profile_relay::set(self.executor(), opts) - } - - pub fn nostr_profile_relay_unset( - &self, - opts: &INostrProfileRelayRelation, - ) -> Result<INostrProfileRelayResolve, IError<SqlError>> { - models::nostr_profile_relay::unset(self.executor(), opts) - } - - pub fn trade_product_location_set( - &self, - opts: &ITradeProductLocationRelation, - ) -> Result<ITradeProductLocationResolve, IError<SqlError>> { - models::trade_product_location::set(self.executor(), opts) - } - - pub fn trade_product_location_unset( - &self, - opts: &ITradeProductLocationRelation, - ) -> Result<ITradeProductLocationResolve, IError<SqlError>> { - models::trade_product_location::unset(self.executor(), opts) - } - - pub fn trade_product_media_set( - &self, - opts: &ITradeProductMediaRelation, - ) -> Result<ITradeProductMediaResolve, IError<SqlError>> { - models::trade_product_media::set(self.executor(), opts) - } - - pub fn trade_product_media_unset( - &self, - opts: &ITradeProductMediaRelation, - ) -> Result<ITradeProductMediaResolve, IError<SqlError>> { - models::trade_product_media::unset(self.executor(), opts) - } - -} diff --git a/tangle-sql/src/migrations.rs b/tangle-sql/src/migrations.rs @@ -1,80 +0,0 @@ -use radroots_sql_core::SqlExecutor; -use radroots_sql_core::error::SqlError; -use radroots_sql_core::migrations::{Migration, migrations_run_all_down, migrations_run_all_up}; - -pub static MIGRATIONS: &[Migration] = &[ - Migration { - name: "0000_init", - up_sql: include_str!("../migrations/0000_init.up.sql"), - down_sql: include_str!("../migrations/0000_init.down.sql"), - }, - Migration { - name: "0001_log_error", - up_sql: include_str!("../migrations/0001_log_error.up.sql"), - down_sql: include_str!("../migrations/0001_log_error.down.sql"), - }, - Migration { - name: "0002_farm", - up_sql: include_str!("../migrations/0002_farm.up.sql"), - down_sql: include_str!("../migrations/0002_farm.down.sql"), - }, - Migration { - name: "0003_location_gcs", - up_sql: include_str!("../migrations/0003_location_gcs.up.sql"), - down_sql: include_str!("../migrations/0003_location_gcs.down.sql"), - }, - Migration { - name: "0004_trade_product", - up_sql: include_str!("../migrations/0004_trade_product.up.sql"), - down_sql: include_str!("../migrations/0004_trade_product.down.sql"), - }, - Migration { - name: "0005_nostr_profile", - up_sql: include_str!("../migrations/0005_nostr_profile.up.sql"), - down_sql: include_str!("../migrations/0005_nostr_profile.down.sql"), - }, - Migration { - name: "0006_nostr_relay", - up_sql: include_str!("../migrations/0006_nostr_relay.up.sql"), - down_sql: include_str!("../migrations/0006_nostr_relay.down.sql"), - }, - Migration { - name: "0007_media_image", - up_sql: include_str!("../migrations/0007_media_image.up.sql"), - down_sql: include_str!("../migrations/0007_media_image.down.sql"), - }, - Migration { - name: "0008_farm_location", - up_sql: include_str!("../migrations/0008_farm_location.up.sql"), - down_sql: include_str!("../migrations/0008_farm_location.down.sql"), - }, - Migration { - name: "0009_nostr_profile_relay", - up_sql: include_str!("../migrations/0009_nostr_profile_relay.up.sql"), - down_sql: include_str!("../migrations/0009_nostr_profile_relay.down.sql"), - }, - Migration { - name: "0010_trade_product_location", - up_sql: include_str!("../migrations/0010_trade_product_location.up.sql"), - down_sql: include_str!("../migrations/0010_trade_product_location.down.sql"), - }, - Migration { - name: "0011_trade_product_media", - up_sql: include_str!("../migrations/0011_trade_product_media.up.sql"), - down_sql: include_str!("../migrations/0011_trade_product_media.down.sql"), - }, -]; - -pub fn run_all_up<E>(executor: &E) -> Result<(), SqlError> -where - E: SqlExecutor, -{ - migrations_run_all_up(executor, MIGRATIONS) -} - -pub fn run_all_down<E>(executor: &E) -> Result<(), SqlError> -where - E: SqlExecutor, -{ - migrations_run_all_down(executor, MIGRATIONS) -} diff --git a/tangle-sql/src/models/farm.rs b/tangle-sql/src/models/farm.rs @@ -1,179 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::farm::{ - IFarmCreate, - IFarmCreateResolve, - IFarmDelete, - IFarmDeleteResolve, - IFarmFieldsFilter, - IFarmFindMany, - IFarmFindManyResolve, - IFarmFindOne, - IFarmFindOneResolve, - IFarmUpdate, - IFarmUpdateResolve, - Farm, - FarmFindManyRel, - FarmQueryBindValues, -}; -use radroots_types::types::{IError, IResult, IResultList}; -use serde_json::Value; - -const TABLE_NAME: &str = "farm"; - -pub fn create<E: SqlExecutor>( - exec: &E, - opts: &IFarmCreate, -) -> Result<IFarmCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; - let id = utils::uuidv4(); - let now = utils::time_created_on(); - let meta: [(&str, Value); 3] = [ - ("id", Value::from(id.clone())), - ("created_at", Value::from(now.clone())), - ("updated_at", Value::from(now.clone())), - ]; - let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; - let _ = exec.exec(&sql, ¶ms_json)?; - let on = FarmQueryBindValues::Id { id: id.clone() }; - let result = find_one_by_on(exec, &on)? - .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; - Ok(IResult { result }) -} - -pub fn find_one<E: SqlExecutor>( - exec: &E, - opts: &IFarmFindOne, -) -> Result<IFarmFindOneResolve, IError<SqlError>> { - let result = match opts { - IFarmFindOne::On(args) => find_one_by_on(exec, &args.on)?, - IFarmFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, - }; - Ok(IResult { result }) -} - -pub fn find_many<E: SqlExecutor>( - exec: &E, - opts: &IFarmFindMany, -) -> Result<IFarmFindManyResolve, IError<SqlError>> { - let results = find_many_filter(exec, &opts.filter)?; - Ok(IResultList { results }) -} - -fn find_many_filter<E: SqlExecutor>( - exec: &E, - filter: &Option<IFarmFieldsFilter>, -) -> Result<Vec<Farm>, IError<SqlError>> { - let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<Farm> = utils::parse_json(&json)?; - Ok(rows) -} - -fn find_one_by_on<E: SqlExecutor>( - exec: &E, - on: &FarmQueryBindValues, -) -> Result<Option<Farm>, IError<SqlError>> { - let (column, value) = on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<Farm> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn rel_query(rel: &FarmFindManyRel) -> (&'static str, Vec<Value>) { - match *rel {} -} - -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, - rel: &FarmFindManyRel, -) -> Result<Option<Farm>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql} LIMIT 1;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<Farm> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<Farm, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<Farm> = utils::parse_json(&json)?; - rows.pop() - .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) -} - -pub fn update<E: SqlExecutor>( - exec: &E, - opts: &IFarmUpdate, -) -> Result<IFarmUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; - if updates.is_empty() { - return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); - } - updates.insert( - String::from("updated_at"), - Value::from(utils::time_created_on()), - ); - let mut set_parts = Vec::with_capacity(updates.len()); - let mut bind_values = Vec::with_capacity(updates.len() + 1); - for (column, value) in updates { - set_parts.push(format!("{column} = ?")); - bind_values.push(utils::to_db_bind_value(&value)); - } - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &opts.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; - model.id - } - }; - bind_values.push(Value::from(id_for_lookup.clone())); - let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); - let params_json = utils::to_params_json(bind_values)?; - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - let updated = select_by_id(exec, &id_for_lookup)?; - Ok(IResult { result: updated }) -} - -pub fn delete<E: SqlExecutor>( - exec: &E, - opts: &IFarmDelete, -) -> Result<IFarmDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts { - IFarmDelete::On(args) => match args.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &args.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; - model.id - } - }, - IFarmDelete::Rel(args) => { - let found = find_one_by_rel(exec, &args.rel)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; - model.id - } - }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; - let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - Ok(IResult { result: id_for_lookup }) -} - -fn rel_lookup_key(rel: &FarmFindManyRel) -> String { - match *rel {} -} diff --git a/tangle-sql/src/models/farm_location.rs b/tangle-sql/src/models/farm_location.rs @@ -1,40 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::farm_location::{ - IFarmLocationRelation, - IFarmLocationResolve, -}; -use radroots_types::types::{IError, IResultPass}; -use serde_json::Value; - -const TABLE_NAME: &str = "farm_location"; - -pub fn set<E: SqlExecutor>( - exec: &E, - opts: &IFarmLocationRelation, -) -> Result<IFarmLocationResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::with_capacity(2); - let (farm_column, farm_value) = opts.farm.to_filter_param(); - query_vals.push(farm_value); - let (location_gcs_column, location_gcs_value) = opts.location_gcs.to_filter_param(); - query_vals.push(location_gcs_value); - let query = format!("INSERT INTO {} (tb_farm, tb_lg) VALUES ((SELECT id FROM farm WHERE {} = ?), (SELECT id FROM location_gcs WHERE {} = ?));", TABLE_NAME, farm_column, location_gcs_column); - let params_json = utils::to_params_json(query_vals)?; - let _ = exec.exec(&query, ¶ms_json)?; - Ok(IResultPass { pass: true }) -} - -pub fn unset<E: SqlExecutor>( - exec: &E, - opts: &IFarmLocationRelation, -) -> Result<IFarmLocationResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::with_capacity(2); - let (farm_column, farm_value) = opts.farm.to_filter_param(); - query_vals.push(farm_value); - let (location_gcs_column, location_gcs_value) = opts.location_gcs.to_filter_param(); - query_vals.push(location_gcs_value); - let query = format!("DELETE FROM {} WHERE tb_farm = (SELECT id FROM farm WHERE {} = ?) AND tb_lg = (SELECT id FROM location_gcs WHERE {} = ?);", TABLE_NAME, farm_column, location_gcs_column); - let params_json = utils::to_params_json(query_vals)?; - let _ = exec.exec(&query, ¶ms_json)?; - Ok(IResultPass { pass: true }) -} diff --git a/tangle-sql/src/models/location_gcs.rs b/tangle-sql/src/models/location_gcs.rs @@ -1,216 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::location_gcs::{ - ILocationGcsCreate, - ILocationGcsCreateResolve, - ILocationGcsDelete, - ILocationGcsDeleteResolve, - ILocationGcsFieldsFilter, - ILocationGcsFindMany, - ILocationGcsFindManyResolve, - ILocationGcsFindOne, - ILocationGcsFindOneResolve, - ILocationGcsUpdate, - ILocationGcsUpdateResolve, - LocationGcs, - LocationGcsFindManyRel, - LocationGcsQueryBindValues, -}; -use radroots_types::types::{IError, IResult, IResultList}; -use serde_json::Value; - -const TABLE_NAME: &str = "location_gcs"; - -pub fn create<E: SqlExecutor>( - exec: &E, - opts: &ILocationGcsCreate, -) -> Result<ILocationGcsCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; - let id = utils::uuidv4(); - let now = utils::time_created_on(); - let meta: [(&str, Value); 3] = [ - ("id", Value::from(id.clone())), - ("created_at", Value::from(now.clone())), - ("updated_at", Value::from(now.clone())), - ]; - let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; - let _ = exec.exec(&sql, ¶ms_json)?; - let on = LocationGcsQueryBindValues::Id { id: id.clone() }; - let result = find_one_by_on(exec, &on)? - .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; - Ok(IResult { result }) -} - -pub fn find_one<E: SqlExecutor>( - exec: &E, - opts: &ILocationGcsFindOne, -) -> Result<ILocationGcsFindOneResolve, IError<SqlError>> { - let result = match opts { - ILocationGcsFindOne::On(args) => find_one_by_on(exec, &args.on)?, - ILocationGcsFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, - }; - Ok(IResult { result }) -} - -pub fn find_many<E: SqlExecutor>( - exec: &E, - opts: &ILocationGcsFindMany, -) -> Result<ILocationGcsFindManyResolve, IError<SqlError>> { - let results = match opts { - ILocationGcsFindMany::Filter { filter } => find_many_filter(exec, filter)?, - ILocationGcsFindMany::Rel { rel } => find_many_by_rel(exec, rel)?, - }; - Ok(IResultList { results }) -} - -fn find_many_filter<E: SqlExecutor>( - exec: &E, - filter: &Option<ILocationGcsFieldsFilter>, -) -> Result<Vec<LocationGcs>, IError<SqlError>> { - let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<LocationGcs> = utils::parse_json(&json)?; - Ok(rows) -} - -fn find_one_by_on<E: SqlExecutor>( - exec: &E, - on: &LocationGcsQueryBindValues, -) -> Result<Option<LocationGcs>, IError<SqlError>> { - let (column, value) = on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<LocationGcs> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn rel_query(rel: &LocationGcsFindManyRel) -> (&'static str, Vec<Value>) { - match rel { - LocationGcsFindManyRel::OnTradeProduct(args) => ( - "SELECT lg.* FROM location_gcs lg JOIN trade_product_location tp_lg ON lg.id = tp_lg.tb_lg WHERE tp_lg.tb_tp = ?", - vec![Value::from(args.id.clone())], - ), - LocationGcsFindManyRel::OffTradeProduct(args) => ( - "SELECT lg.* FROM location_gcs lg WHERE NOT EXISTS (SELECT 1 FROM trade_product_location tp_lg WHERE tp_lg.tb_lg = lg.id AND tp_lg.tb_tp = ?)", - vec![Value::from(args.id.clone())], - ), - LocationGcsFindManyRel::OnFarm(args) => ( - "SELECT lg.* FROM location_gcs lg JOIN farm_location farm_lg ON lg.id = farm_lg.tb_lg WHERE farm_lg.tb_farm = ?", - vec![Value::from(args.id.clone())], - ), - LocationGcsFindManyRel::OffFarm(args) => ( - "SELECT lg.* FROM location_gcs lg WHERE NOT EXISTS (SELECT 1 FROM farm_location farm_lg WHERE farm_lg.tb_lg = lg.id AND farm_lg.tb_farm = ?)", - vec![Value::from(args.id.clone())], - ), - } -} - -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, - rel: &LocationGcsFindManyRel, -) -> Result<Option<LocationGcs>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql} LIMIT 1;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<LocationGcs> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn find_many_by_rel<E: SqlExecutor>( - exec: &E, - rel: &LocationGcsFindManyRel, -) -> Result<Vec<LocationGcs>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql};"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<LocationGcs> = utils::parse_json(&json)?; - Ok(rows) -} - -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<LocationGcs, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<LocationGcs> = utils::parse_json(&json)?; - rows.pop() - .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) -} - -pub fn update<E: SqlExecutor>( - exec: &E, - opts: &ILocationGcsUpdate, -) -> Result<ILocationGcsUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; - if updates.is_empty() { - return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); - } - updates.insert( - String::from("updated_at"), - Value::from(utils::time_created_on()), - ); - let mut set_parts = Vec::with_capacity(updates.len()); - let mut bind_values = Vec::with_capacity(updates.len() + 1); - for (column, value) in updates { - set_parts.push(format!("{column} = ?")); - bind_values.push(utils::to_db_bind_value(&value)); - } - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &opts.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; - model.id - } - }; - bind_values.push(Value::from(id_for_lookup.clone())); - let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); - let params_json = utils::to_params_json(bind_values)?; - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - let updated = select_by_id(exec, &id_for_lookup)?; - Ok(IResult { result: updated }) -} - -pub fn delete<E: SqlExecutor>( - exec: &E, - opts: &ILocationGcsDelete, -) -> Result<ILocationGcsDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts { - ILocationGcsDelete::On(args) => match args.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &args.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; - model.id - } - }, - ILocationGcsDelete::Rel(args) => { - let found = find_one_by_rel(exec, &args.rel)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; - model.id - } - }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; - let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - Ok(IResult { result: id_for_lookup }) -} - -fn rel_lookup_key(rel: &LocationGcsFindManyRel) -> String { - match rel { - LocationGcsFindManyRel::OnTradeProduct(args) => format!("on_trade_product:{}", args.id.as_str()), - LocationGcsFindManyRel::OffTradeProduct(args) => format!("off_trade_product:{}", args.id.as_str()), - LocationGcsFindManyRel::OnFarm(args) => format!("on_farm:{}", args.id.as_str()), - LocationGcsFindManyRel::OffFarm(args) => format!("off_farm:{}", args.id.as_str()), - } -} diff --git a/tangle-sql/src/models/log_error.rs b/tangle-sql/src/models/log_error.rs @@ -1,179 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::log_error::{ - ILogErrorCreate, - ILogErrorCreateResolve, - ILogErrorDelete, - ILogErrorDeleteResolve, - ILogErrorFieldsFilter, - ILogErrorFindMany, - ILogErrorFindManyResolve, - ILogErrorFindOne, - ILogErrorFindOneResolve, - ILogErrorUpdate, - ILogErrorUpdateResolve, - LogError, - LogErrorFindManyRel, - LogErrorQueryBindValues, -}; -use radroots_types::types::{IError, IResult, IResultList}; -use serde_json::Value; - -const TABLE_NAME: &str = "log_error"; - -pub fn create<E: SqlExecutor>( - exec: &E, - opts: &ILogErrorCreate, -) -> Result<ILogErrorCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; - let id = utils::uuidv4(); - let now = utils::time_created_on(); - let meta: [(&str, Value); 3] = [ - ("id", Value::from(id.clone())), - ("created_at", Value::from(now.clone())), - ("updated_at", Value::from(now.clone())), - ]; - let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; - let _ = exec.exec(&sql, ¶ms_json)?; - let on = LogErrorQueryBindValues::Id { id: id.clone() }; - let result = find_one_by_on(exec, &on)? - .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; - Ok(IResult { result }) -} - -pub fn find_one<E: SqlExecutor>( - exec: &E, - opts: &ILogErrorFindOne, -) -> Result<ILogErrorFindOneResolve, IError<SqlError>> { - let result = match opts { - ILogErrorFindOne::On(args) => find_one_by_on(exec, &args.on)?, - ILogErrorFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, - }; - Ok(IResult { result }) -} - -pub fn find_many<E: SqlExecutor>( - exec: &E, - opts: &ILogErrorFindMany, -) -> Result<ILogErrorFindManyResolve, IError<SqlError>> { - let results = find_many_filter(exec, &opts.filter)?; - Ok(IResultList { results }) -} - -fn find_many_filter<E: SqlExecutor>( - exec: &E, - filter: &Option<ILogErrorFieldsFilter>, -) -> Result<Vec<LogError>, IError<SqlError>> { - let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<LogError> = utils::parse_json(&json)?; - Ok(rows) -} - -fn find_one_by_on<E: SqlExecutor>( - exec: &E, - on: &LogErrorQueryBindValues, -) -> Result<Option<LogError>, IError<SqlError>> { - let (column, value) = on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<LogError> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn rel_query(rel: &LogErrorFindManyRel) -> (&'static str, Vec<Value>) { - match *rel {} -} - -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, - rel: &LogErrorFindManyRel, -) -> Result<Option<LogError>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql} LIMIT 1;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<LogError> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<LogError, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<LogError> = utils::parse_json(&json)?; - rows.pop() - .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) -} - -pub fn update<E: SqlExecutor>( - exec: &E, - opts: &ILogErrorUpdate, -) -> Result<ILogErrorUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; - if updates.is_empty() { - return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); - } - updates.insert( - String::from("updated_at"), - Value::from(utils::time_created_on()), - ); - let mut set_parts = Vec::with_capacity(updates.len()); - let mut bind_values = Vec::with_capacity(updates.len() + 1); - for (column, value) in updates { - set_parts.push(format!("{column} = ?")); - bind_values.push(utils::to_db_bind_value(&value)); - } - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &opts.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; - model.id - } - }; - bind_values.push(Value::from(id_for_lookup.clone())); - let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); - let params_json = utils::to_params_json(bind_values)?; - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - let updated = select_by_id(exec, &id_for_lookup)?; - Ok(IResult { result: updated }) -} - -pub fn delete<E: SqlExecutor>( - exec: &E, - opts: &ILogErrorDelete, -) -> Result<ILogErrorDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts { - ILogErrorDelete::On(args) => match args.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &args.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; - model.id - } - }, - ILogErrorDelete::Rel(args) => { - let found = find_one_by_rel(exec, &args.rel)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; - model.id - } - }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; - let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - Ok(IResult { result: id_for_lookup }) -} - -fn rel_lookup_key(rel: &LogErrorFindManyRel) -> String { - match *rel {} -} diff --git a/tangle-sql/src/models/media_image.rs b/tangle-sql/src/models/media_image.rs @@ -1,206 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::media_image::{ - IMediaImageCreate, - IMediaImageCreateResolve, - IMediaImageDelete, - IMediaImageDeleteResolve, - IMediaImageFieldsFilter, - IMediaImageFindMany, - IMediaImageFindManyResolve, - IMediaImageFindOne, - IMediaImageFindOneResolve, - IMediaImageUpdate, - IMediaImageUpdateResolve, - MediaImage, - MediaImageFindManyRel, - MediaImageQueryBindValues, -}; -use radroots_types::types::{IError, IResult, IResultList}; -use serde_json::Value; - -const TABLE_NAME: &str = "media_image"; - -pub fn create<E: SqlExecutor>( - exec: &E, - opts: &IMediaImageCreate, -) -> Result<IMediaImageCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; - let id = utils::uuidv4(); - let now = utils::time_created_on(); - let meta: [(&str, Value); 3] = [ - ("id", Value::from(id.clone())), - ("created_at", Value::from(now.clone())), - ("updated_at", Value::from(now.clone())), - ]; - let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; - let _ = exec.exec(&sql, ¶ms_json)?; - let on = MediaImageQueryBindValues::Id { id: id.clone() }; - let result = find_one_by_on(exec, &on)? - .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; - Ok(IResult { result }) -} - -pub fn find_one<E: SqlExecutor>( - exec: &E, - opts: &IMediaImageFindOne, -) -> Result<IMediaImageFindOneResolve, IError<SqlError>> { - let result = match opts { - IMediaImageFindOne::On(args) => find_one_by_on(exec, &args.on)?, - IMediaImageFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, - }; - Ok(IResult { result }) -} - -pub fn find_many<E: SqlExecutor>( - exec: &E, - opts: &IMediaImageFindMany, -) -> Result<IMediaImageFindManyResolve, IError<SqlError>> { - let results = match opts { - IMediaImageFindMany::Filter { filter } => find_many_filter(exec, filter)?, - IMediaImageFindMany::Rel { rel } => find_many_by_rel(exec, rel)?, - }; - Ok(IResultList { results }) -} - -fn find_many_filter<E: SqlExecutor>( - exec: &E, - filter: &Option<IMediaImageFieldsFilter>, -) -> Result<Vec<MediaImage>, IError<SqlError>> { - let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<MediaImage> = utils::parse_json(&json)?; - Ok(rows) -} - -fn find_one_by_on<E: SqlExecutor>( - exec: &E, - on: &MediaImageQueryBindValues, -) -> Result<Option<MediaImage>, IError<SqlError>> { - let (column, value) = on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn rel_query(rel: &MediaImageFindManyRel) -> (&'static str, Vec<Value>) { - match rel { - MediaImageFindManyRel::OnTradeProduct(args) => ( - "SELECT mu.* FROM media_image mu JOIN trade_product_media tp_lg ON mu.id = tp_lg.tb_mu WHERE tp_lg.tb_tp = ?", - vec![Value::from(args.id.clone())], - ), - MediaImageFindManyRel::OffTradeProduct(args) => ( - "SELECT mu.* FROM media_image mu WHERE NOT EXISTS (SELECT 1 FROM trade_product_media tp_lg WHERE tp_lg.tb_mu = mu.id AND tp_lg.tb_tp = ?)", - vec![Value::from(args.id.clone())], - ), - } -} - -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, - rel: &MediaImageFindManyRel, -) -> Result<Option<MediaImage>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql} LIMIT 1;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn find_many_by_rel<E: SqlExecutor>( - exec: &E, - rel: &MediaImageFindManyRel, -) -> Result<Vec<MediaImage>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql};"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<MediaImage> = utils::parse_json(&json)?; - Ok(rows) -} - -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<MediaImage, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; - rows.pop() - .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) -} - -pub fn update<E: SqlExecutor>( - exec: &E, - opts: &IMediaImageUpdate, -) -> Result<IMediaImageUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; - if updates.is_empty() { - return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); - } - updates.insert( - String::from("updated_at"), - Value::from(utils::time_created_on()), - ); - let mut set_parts = Vec::with_capacity(updates.len()); - let mut bind_values = Vec::with_capacity(updates.len() + 1); - for (column, value) in updates { - set_parts.push(format!("{column} = ?")); - bind_values.push(utils::to_db_bind_value(&value)); - } - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &opts.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; - model.id - } - }; - bind_values.push(Value::from(id_for_lookup.clone())); - let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); - let params_json = utils::to_params_json(bind_values)?; - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - let updated = select_by_id(exec, &id_for_lookup)?; - Ok(IResult { result: updated }) -} - -pub fn delete<E: SqlExecutor>( - exec: &E, - opts: &IMediaImageDelete, -) -> Result<IMediaImageDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts { - IMediaImageDelete::On(args) => match args.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &args.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; - model.id - } - }, - IMediaImageDelete::Rel(args) => { - let found = find_one_by_rel(exec, &args.rel)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; - model.id - } - }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; - let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - Ok(IResult { result: id_for_lookup }) -} - -fn rel_lookup_key(rel: &MediaImageFindManyRel) -> String { - match rel { - MediaImageFindManyRel::OnTradeProduct(args) => format!("on_trade_product:{}", args.id.as_str()), - MediaImageFindManyRel::OffTradeProduct(args) => format!("off_trade_product:{}", args.id.as_str()), - } -} diff --git a/tangle-sql/src/models/mod.rs b/tangle-sql/src/models/mod.rs @@ -1,11 +0,0 @@ -pub mod farm; -pub mod farm_location; -pub mod location_gcs; -pub mod log_error; -pub mod media_image; -pub mod nostr_profile; -pub mod nostr_profile_relay; -pub mod nostr_relay; -pub mod trade_product; -pub mod trade_product_location; -pub mod trade_product_media; diff --git a/tangle-sql/src/models/nostr_profile.rs b/tangle-sql/src/models/nostr_profile.rs @@ -1,206 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::nostr_profile::{ - INostrProfileCreate, - INostrProfileCreateResolve, - INostrProfileDelete, - INostrProfileDeleteResolve, - INostrProfileFieldsFilter, - INostrProfileFindMany, - INostrProfileFindManyResolve, - INostrProfileFindOne, - INostrProfileFindOneResolve, - INostrProfileUpdate, - INostrProfileUpdateResolve, - NostrProfile, - NostrProfileFindManyRel, - NostrProfileQueryBindValues, -}; -use radroots_types::types::{IError, IResult, IResultList}; -use serde_json::Value; - -const TABLE_NAME: &str = "nostr_profile"; - -pub fn create<E: SqlExecutor>( - exec: &E, - opts: &INostrProfileCreate, -) -> Result<INostrProfileCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; - let id = utils::uuidv4(); - let now = utils::time_created_on(); - let meta: [(&str, Value); 3] = [ - ("id", Value::from(id.clone())), - ("created_at", Value::from(now.clone())), - ("updated_at", Value::from(now.clone())), - ]; - let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; - let _ = exec.exec(&sql, ¶ms_json)?; - let on = NostrProfileQueryBindValues::Id { id: id.clone() }; - let result = find_one_by_on(exec, &on)? - .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; - Ok(IResult { result }) -} - -pub fn find_one<E: SqlExecutor>( - exec: &E, - opts: &INostrProfileFindOne, -) -> Result<INostrProfileFindOneResolve, IError<SqlError>> { - let result = match opts { - INostrProfileFindOne::On(args) => find_one_by_on(exec, &args.on)?, - INostrProfileFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, - }; - Ok(IResult { result }) -} - -pub fn find_many<E: SqlExecutor>( - exec: &E, - opts: &INostrProfileFindMany, -) -> Result<INostrProfileFindManyResolve, IError<SqlError>> { - let results = match opts { - INostrProfileFindMany::Filter { filter } => find_many_filter(exec, filter)?, - INostrProfileFindMany::Rel { rel } => find_many_by_rel(exec, rel)?, - }; - Ok(IResultList { results }) -} - -fn find_many_filter<E: SqlExecutor>( - exec: &E, - filter: &Option<INostrProfileFieldsFilter>, -) -> Result<Vec<NostrProfile>, IError<SqlError>> { - let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<NostrProfile> = utils::parse_json(&json)?; - Ok(rows) -} - -fn find_one_by_on<E: SqlExecutor>( - exec: &E, - on: &NostrProfileQueryBindValues, -) -> Result<Option<NostrProfile>, IError<SqlError>> { - let (column, value) = on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn rel_query(rel: &NostrProfileFindManyRel) -> (&'static str, Vec<Value>) { - match rel { - NostrProfileFindManyRel::OnRelay(args) => ( - "SELECT pr.* FROM nostr_profile pr JOIN nostr_profile_relay pr_rl ON pr.id = pr_rl.tb_pr WHERE pr_rl.tb_rl = ?", - vec![Value::from(args.id.clone())], - ), - NostrProfileFindManyRel::OffRelay(args) => ( - "SELECT pr.* FROM nostr_profile pr WHERE NOT EXISTS (SELECT 1 FROM nostr_profile_relay pr_rl WHERE pr_rl.tb_pr = pr.id AND pr_rl.tb_rl = ?)", - vec![Value::from(args.id.clone())], - ), - } -} - -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, - rel: &NostrProfileFindManyRel, -) -> Result<Option<NostrProfile>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql} LIMIT 1;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn find_many_by_rel<E: SqlExecutor>( - exec: &E, - rel: &NostrProfileFindManyRel, -) -> Result<Vec<NostrProfile>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql};"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<NostrProfile> = utils::parse_json(&json)?; - Ok(rows) -} - -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrProfile, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; - rows.pop() - .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) -} - -pub fn update<E: SqlExecutor>( - exec: &E, - opts: &INostrProfileUpdate, -) -> Result<INostrProfileUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; - if updates.is_empty() { - return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); - } - updates.insert( - String::from("updated_at"), - Value::from(utils::time_created_on()), - ); - let mut set_parts = Vec::with_capacity(updates.len()); - let mut bind_values = Vec::with_capacity(updates.len() + 1); - for (column, value) in updates { - set_parts.push(format!("{column} = ?")); - bind_values.push(utils::to_db_bind_value(&value)); - } - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &opts.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; - model.id - } - }; - bind_values.push(Value::from(id_for_lookup.clone())); - let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); - let params_json = utils::to_params_json(bind_values)?; - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - let updated = select_by_id(exec, &id_for_lookup)?; - Ok(IResult { result: updated }) -} - -pub fn delete<E: SqlExecutor>( - exec: &E, - opts: &INostrProfileDelete, -) -> Result<INostrProfileDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts { - INostrProfileDelete::On(args) => match args.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &args.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; - model.id - } - }, - INostrProfileDelete::Rel(args) => { - let found = find_one_by_rel(exec, &args.rel)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; - model.id - } - }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; - let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - Ok(IResult { result: id_for_lookup }) -} - -fn rel_lookup_key(rel: &NostrProfileFindManyRel) -> String { - match rel { - NostrProfileFindManyRel::OnRelay(args) => format!("on_relay:{}", args.id.as_str()), - NostrProfileFindManyRel::OffRelay(args) => format!("off_relay:{}", args.id.as_str()), - } -} diff --git a/tangle-sql/src/models/nostr_profile_relay.rs b/tangle-sql/src/models/nostr_profile_relay.rs @@ -1,40 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::nostr_profile_relay::{ - INostrProfileRelayRelation, - INostrProfileRelayResolve, -}; -use radroots_types::types::{IError, IResultPass}; -use serde_json::Value; - -const TABLE_NAME: &str = "nostr_profile_relay"; - -pub fn set<E: SqlExecutor>( - exec: &E, - opts: &INostrProfileRelayRelation, -) -> Result<INostrProfileRelayResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::with_capacity(2); - let (nostr_profile_column, nostr_profile_value) = opts.nostr_profile.to_filter_param(); - query_vals.push(nostr_profile_value); - let (nostr_relay_column, nostr_relay_value) = opts.nostr_relay.to_filter_param(); - query_vals.push(nostr_relay_value); - let query = format!("INSERT INTO {} (tb_pr, tb_rl) VALUES ((SELECT id FROM nostr_profile WHERE {} = ?), (SELECT id FROM nostr_relay WHERE {} = ?));", TABLE_NAME, nostr_profile_column, nostr_relay_column); - let params_json = utils::to_params_json(query_vals)?; - let _ = exec.exec(&query, ¶ms_json)?; - Ok(IResultPass { pass: true }) -} - -pub fn unset<E: SqlExecutor>( - exec: &E, - opts: &INostrProfileRelayRelation, -) -> Result<INostrProfileRelayResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::with_capacity(2); - let (nostr_profile_column, nostr_profile_value) = opts.nostr_profile.to_filter_param(); - query_vals.push(nostr_profile_value); - let (nostr_relay_column, nostr_relay_value) = opts.nostr_relay.to_filter_param(); - query_vals.push(nostr_relay_value); - let query = format!("DELETE FROM {} WHERE tb_pr = (SELECT id FROM nostr_profile WHERE {} = ?) AND tb_rl = (SELECT id FROM nostr_relay WHERE {} = ?);", TABLE_NAME, nostr_profile_column, nostr_relay_column); - let params_json = utils::to_params_json(query_vals)?; - let _ = exec.exec(&query, ¶ms_json)?; - Ok(IResultPass { pass: true }) -} diff --git a/tangle-sql/src/models/nostr_relay.rs b/tangle-sql/src/models/nostr_relay.rs @@ -1,206 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::nostr_relay::{ - INostrRelayCreate, - INostrRelayCreateResolve, - INostrRelayDelete, - INostrRelayDeleteResolve, - INostrRelayFieldsFilter, - INostrRelayFindMany, - INostrRelayFindManyResolve, - INostrRelayFindOne, - INostrRelayFindOneResolve, - INostrRelayUpdate, - INostrRelayUpdateResolve, - NostrRelay, - NostrRelayFindManyRel, - NostrRelayQueryBindValues, -}; -use radroots_types::types::{IError, IResult, IResultList}; -use serde_json::Value; - -const TABLE_NAME: &str = "nostr_relay"; - -pub fn create<E: SqlExecutor>( - exec: &E, - opts: &INostrRelayCreate, -) -> Result<INostrRelayCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; - let id = utils::uuidv4(); - let now = utils::time_created_on(); - let meta: [(&str, Value); 3] = [ - ("id", Value::from(id.clone())), - ("created_at", Value::from(now.clone())), - ("updated_at", Value::from(now.clone())), - ]; - let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; - let _ = exec.exec(&sql, ¶ms_json)?; - let on = NostrRelayQueryBindValues::Id { id: id.clone() }; - let result = find_one_by_on(exec, &on)? - .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; - Ok(IResult { result }) -} - -pub fn find_one<E: SqlExecutor>( - exec: &E, - opts: &INostrRelayFindOne, -) -> Result<INostrRelayFindOneResolve, IError<SqlError>> { - let result = match opts { - INostrRelayFindOne::On(args) => find_one_by_on(exec, &args.on)?, - INostrRelayFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, - }; - Ok(IResult { result }) -} - -pub fn find_many<E: SqlExecutor>( - exec: &E, - opts: &INostrRelayFindMany, -) -> Result<INostrRelayFindManyResolve, IError<SqlError>> { - let results = match opts { - INostrRelayFindMany::Filter { filter } => find_many_filter(exec, filter)?, - INostrRelayFindMany::Rel { rel } => find_many_by_rel(exec, rel)?, - }; - Ok(IResultList { results }) -} - -fn find_many_filter<E: SqlExecutor>( - exec: &E, - filter: &Option<INostrRelayFieldsFilter>, -) -> Result<Vec<NostrRelay>, IError<SqlError>> { - let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<NostrRelay> = utils::parse_json(&json)?; - Ok(rows) -} - -fn find_one_by_on<E: SqlExecutor>( - exec: &E, - on: &NostrRelayQueryBindValues, -) -> Result<Option<NostrRelay>, IError<SqlError>> { - let (column, value) = on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn rel_query(rel: &NostrRelayFindManyRel) -> (&'static str, Vec<Value>) { - match rel { - NostrRelayFindManyRel::OnProfile(args) => ( - "SELECT rl.* FROM nostr_relay rl JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key = ?", - vec![Value::from(args.public_key.clone())], - ), - NostrRelayFindManyRel::OffProfile(args) => ( - "SELECT rl.* FROM nostr_relay rl LEFT JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl LEFT JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key <> ?", - vec![Value::from(args.public_key.clone())], - ), - } -} - -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, - rel: &NostrRelayFindManyRel, -) -> Result<Option<NostrRelay>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql} LIMIT 1;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn find_many_by_rel<E: SqlExecutor>( - exec: &E, - rel: &NostrRelayFindManyRel, -) -> Result<Vec<NostrRelay>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql};"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<NostrRelay> = utils::parse_json(&json)?; - Ok(rows) -} - -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrRelay, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; - rows.pop() - .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) -} - -pub fn update<E: SqlExecutor>( - exec: &E, - opts: &INostrRelayUpdate, -) -> Result<INostrRelayUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; - if updates.is_empty() { - return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); - } - updates.insert( - String::from("updated_at"), - Value::from(utils::time_created_on()), - ); - let mut set_parts = Vec::with_capacity(updates.len()); - let mut bind_values = Vec::with_capacity(updates.len() + 1); - for (column, value) in updates { - set_parts.push(format!("{column} = ?")); - bind_values.push(utils::to_db_bind_value(&value)); - } - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &opts.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; - model.id - } - }; - bind_values.push(Value::from(id_for_lookup.clone())); - let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); - let params_json = utils::to_params_json(bind_values)?; - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - let updated = select_by_id(exec, &id_for_lookup)?; - Ok(IResult { result: updated }) -} - -pub fn delete<E: SqlExecutor>( - exec: &E, - opts: &INostrRelayDelete, -) -> Result<INostrRelayDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts { - INostrRelayDelete::On(args) => match args.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &args.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; - model.id - } - }, - INostrRelayDelete::Rel(args) => { - let found = find_one_by_rel(exec, &args.rel)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; - model.id - } - }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; - let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - Ok(IResult { result: id_for_lookup }) -} - -fn rel_lookup_key(rel: &NostrRelayFindManyRel) -> String { - match rel { - NostrRelayFindManyRel::OnProfile(args) => format!("on_profile:{}", args.public_key.as_str()), - NostrRelayFindManyRel::OffProfile(args) => format!("off_profile:{}", args.public_key.as_str()), - } -} diff --git a/tangle-sql/src/models/trade_product.rs b/tangle-sql/src/models/trade_product.rs @@ -1,179 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::trade_product::{ - ITradeProductCreate, - ITradeProductCreateResolve, - ITradeProductDelete, - ITradeProductDeleteResolve, - ITradeProductFieldsFilter, - ITradeProductFindMany, - ITradeProductFindManyResolve, - ITradeProductFindOne, - ITradeProductFindOneResolve, - ITradeProductUpdate, - ITradeProductUpdateResolve, - TradeProduct, - TradeProductFindManyRel, - TradeProductQueryBindValues, -}; -use radroots_types::types::{IError, IResult, IResultList}; -use serde_json::Value; - -const TABLE_NAME: &str = "trade_product"; - -pub fn create<E: SqlExecutor>( - exec: &E, - opts: &ITradeProductCreate, -) -> Result<ITradeProductCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; - let id = utils::uuidv4(); - let now = utils::time_created_on(); - let meta: [(&str, Value); 3] = [ - ("id", Value::from(id.clone())), - ("created_at", Value::from(now.clone())), - ("updated_at", Value::from(now.clone())), - ]; - let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; - let _ = exec.exec(&sql, ¶ms_json)?; - let on = TradeProductQueryBindValues::Id { id: id.clone() }; - let result = find_one_by_on(exec, &on)? - .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; - Ok(IResult { result }) -} - -pub fn find_one<E: SqlExecutor>( - exec: &E, - opts: &ITradeProductFindOne, -) -> Result<ITradeProductFindOneResolve, IError<SqlError>> { - let result = match opts { - ITradeProductFindOne::On(args) => find_one_by_on(exec, &args.on)?, - ITradeProductFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, - }; - Ok(IResult { result }) -} - -pub fn find_many<E: SqlExecutor>( - exec: &E, - opts: &ITradeProductFindMany, -) -> Result<ITradeProductFindManyResolve, IError<SqlError>> { - let results = find_many_filter(exec, &opts.filter)?; - Ok(IResultList { results }) -} - -fn find_many_filter<E: SqlExecutor>( - exec: &E, - filter: &Option<ITradeProductFieldsFilter>, -) -> Result<Vec<TradeProduct>, IError<SqlError>> { - let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let rows: Vec<TradeProduct> = utils::parse_json(&json)?; - Ok(rows) -} - -fn find_one_by_on<E: SqlExecutor>( - exec: &E, - on: &TradeProductQueryBindValues, -) -> Result<Option<TradeProduct>, IError<SqlError>> { - let (column, value) = on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn rel_query(rel: &TradeProductFindManyRel) -> (&'static str, Vec<Value>) { - match *rel {} -} - -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, - rel: &TradeProductFindManyRel, -) -> Result<Option<TradeProduct>, IError<SqlError>> { - let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; - let sql = format!("{sql} LIMIT 1;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; - Ok(rows.pop()) -} - -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<TradeProduct, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); - let json = exec.query_raw(&sql, ¶ms_json)?; - let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; - rows.pop() - .ok_or_else(|| IError::from(SqlError::NotFound(id.to_owned()))) -} - -pub fn update<E: SqlExecutor>( - exec: &E, - opts: &ITradeProductUpdate, -) -> Result<ITradeProductUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; - if updates.is_empty() { - return Err(IError::from(SqlError::InvalidArgument(String::from("no fields to update")))); - } - updates.insert( - String::from("updated_at"), - Value::from(utils::time_created_on()), - ); - let mut set_parts = Vec::with_capacity(updates.len()); - let mut bind_values = Vec::with_capacity(updates.len() + 1); - for (column, value) in updates { - set_parts.push(format!("{column} = ?")); - bind_values.push(utils::to_db_bind_value(&value)); - } - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &opts.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; - model.id - } - }; - bind_values.push(Value::from(id_for_lookup.clone())); - let sql = format!("UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ")); - let params_json = utils::to_params_json(bind_values)?; - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - let updated = select_by_id(exec, &id_for_lookup)?; - Ok(IResult { result: updated }) -} - -pub fn delete<E: SqlExecutor>( - exec: &E, - opts: &ITradeProductDelete, -) -> Result<ITradeProductDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts { - ITradeProductDelete::On(args) => match args.on.primary_key() { - Some(id) => id, - None => { - let found = find_one_by_on(exec, &args.on)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; - model.id - } - }, - ITradeProductDelete::Rel(args) => { - let found = find_one_by_rel(exec, &args.rel)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; - model.id - } - }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; - let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); - let outcome = exec.exec(&sql, ¶ms_json)?; - if outcome.changes == 0 { - return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); - } - Ok(IResult { result: id_for_lookup }) -} - -fn rel_lookup_key(rel: &TradeProductFindManyRel) -> String { - match *rel {} -} diff --git a/tangle-sql/src/models/trade_product_location.rs b/tangle-sql/src/models/trade_product_location.rs @@ -1,40 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::trade_product_location::{ - ITradeProductLocationRelation, - ITradeProductLocationResolve, -}; -use radroots_types::types::{IError, IResultPass}; -use serde_json::Value; - -const TABLE_NAME: &str = "trade_product_location"; - -pub fn set<E: SqlExecutor>( - exec: &E, - opts: &ITradeProductLocationRelation, -) -> Result<ITradeProductLocationResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::with_capacity(2); - let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); - query_vals.push(trade_product_value); - let (location_gcs_column, location_gcs_value) = opts.location_gcs.to_filter_param(); - query_vals.push(location_gcs_value); - let query = format!("INSERT INTO {} (tb_tp, tb_lg) VALUES ((SELECT id FROM trade_product WHERE {} = ?), (SELECT id FROM location_gcs WHERE {} = ?));", TABLE_NAME, trade_product_column, location_gcs_column); - let params_json = utils::to_params_json(query_vals)?; - let _ = exec.exec(&query, ¶ms_json)?; - Ok(IResultPass { pass: true }) -} - -pub fn unset<E: SqlExecutor>( - exec: &E, - opts: &ITradeProductLocationRelation, -) -> Result<ITradeProductLocationResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::with_capacity(2); - let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); - query_vals.push(trade_product_value); - let (location_gcs_column, location_gcs_value) = opts.location_gcs.to_filter_param(); - query_vals.push(location_gcs_value); - let query = format!("DELETE FROM {} WHERE tb_tp = (SELECT id FROM trade_product WHERE {} = ?) AND tb_lg = (SELECT id FROM location_gcs WHERE {} = ?);", TABLE_NAME, trade_product_column, location_gcs_column); - let params_json = utils::to_params_json(query_vals)?; - let _ = exec.exec(&query, ¶ms_json)?; - Ok(IResultPass { pass: true }) -} diff --git a/tangle-sql/src/models/trade_product_media.rs b/tangle-sql/src/models/trade_product_media.rs @@ -1,40 +0,0 @@ -use radroots_sql_core::error::SqlError; -use radroots_sql_core::{SqlExecutor, utils}; -use radroots_tangle_schema::trade_product_media::{ - ITradeProductMediaRelation, - ITradeProductMediaResolve, -}; -use radroots_types::types::{IError, IResultPass}; -use serde_json::Value; - -const TABLE_NAME: &str = "trade_product_media"; - -pub fn set<E: SqlExecutor>( - exec: &E, - opts: &ITradeProductMediaRelation, -) -> Result<ITradeProductMediaResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::with_capacity(2); - let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); - query_vals.push(trade_product_value); - let (media_image_column, media_image_value) = opts.media_image.to_filter_param(); - query_vals.push(media_image_value); - let query = format!("INSERT INTO {} (tb_tp, tb_mu) VALUES ((SELECT id FROM trade_product WHERE {} = ?), (SELECT id FROM media_image WHERE {} = ?));", TABLE_NAME, trade_product_column, media_image_column); - let params_json = utils::to_params_json(query_vals)?; - let _ = exec.exec(&query, ¶ms_json)?; - Ok(IResultPass { pass: true }) -} - -pub fn unset<E: SqlExecutor>( - exec: &E, - opts: &ITradeProductMediaRelation, -) -> Result<ITradeProductMediaResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::with_capacity(2); - let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); - query_vals.push(trade_product_value); - let (media_image_column, media_image_value) = opts.media_image.to_filter_param(); - query_vals.push(media_image_value); - let query = format!("DELETE FROM {} WHERE tb_tp = (SELECT id FROM trade_product WHERE {} = ?) AND tb_mu = (SELECT id FROM media_image WHERE {} = ?);", TABLE_NAME, trade_product_column, media_image_column); - let params_json = utils::to_params_json(query_vals)?; - let _ = exec.exec(&query, ¶ms_json)?; - Ok(IResultPass { pass: true }) -}