From 65a53a9f8614075838bdda12338293dc24187a97 Mon Sep 17 00:00:00 2001 From: skanderm Date: Fri, 26 Jul 2024 20:41:03 -0700 Subject: [PATCH] Add feed status, dataplicity_id to feeds (#547) * Allow detection and candidate update actions to accept more attributes * Add online aggregate and dataplicity_id column to feeds * Update accepts for feed create and update --- server/lib/orcasite/radio/feed.ex | 25 +- server/lib/orcasite/radio/feed_segment.ex | 1 + ...add_inserted_at_index_to_feed_segments.exs | 17 + ...0725192709_add_dataplicity_id_to_feeds.exs | 25 ++ .../repo/feed_segments/20240725191221.json | 383 ++++++++++++++++++ .../repo/feeds/20240725192709.json | 285 +++++++++++++ ui/src/graphql/generated/index.ts | 23 ++ 7 files changed, 754 insertions(+), 5 deletions(-) create mode 100644 server/priv/repo/migrations/20240725191221_add_inserted_at_index_to_feed_segments.exs create mode 100644 server/priv/repo/migrations/20240725192709_add_dataplicity_id_to_feeds.exs create mode 100644 server/priv/resource_snapshots/repo/feed_segments/20240725191221.json create mode 100644 server/priv/resource_snapshots/repo/feeds/20240725192709.json diff --git a/server/lib/orcasite/radio/feed.ex b/server/lib/orcasite/radio/feed.ex index ea86d4f4..45709a30 100644 --- a/server/lib/orcasite/radio/feed.ex +++ b/server/lib/orcasite/radio/feed.ex @@ -14,6 +14,7 @@ defmodule Orcasite.Radio.Feed do index [:node_name] index [:visible] index [:slug] + index [:dataplicity_id] end migration_defaults id: "fragment(\"uuid_generate_v7()\")" @@ -36,6 +37,7 @@ defmodule Orcasite.Radio.Feed do attribute :bucket, :string, public?: true attribute :bucket_region, :string, public?: true attribute :cloudfront_url, :string, public?: true + attribute :dataplicity_id, :string, public?: true create_timestamp :inserted_at update_timestamp :updated_at @@ -65,10 +67,21 @@ defmodule Orcasite.Radio.Feed do public?: true end + aggregates do + exists :online, :feed_segments do + public? true + filter expr(inserted_at > ago(30, :second)) + end + end + + relationships do has_many :feed_streams, Orcasite.Radio.FeedStream do public? true end + has_many :feed_segments, Orcasite.Radio.FeedSegment do + public? true + end end policies do @@ -86,12 +99,12 @@ defmodule Orcasite.Radio.Feed do read :read do primary? true - prepare build(load: [:lat_lng, :lat_lng_string]) + prepare build(load: [:lat_lng, :lat_lng_string, :online]) end read :index do filter expr(visible) - prepare build(load: [:lat_lng, :lat_lng_string]) + prepare build(load: [:lat_lng, :lat_lng_string, :online]) end read :get_by_slug do @@ -114,7 +127,8 @@ defmodule Orcasite.Radio.Feed do :visible, :bucket, :bucket_region, - :cloudfront_url + :cloudfront_url, + :dataplicity_id ] argument :lat_lng_string, :string do @@ -137,7 +151,8 @@ defmodule Orcasite.Radio.Feed do :visible, :bucket, :bucket_region, - :cloudfront_url + :cloudfront_url, + :dataplicity_id ] argument :lat_lng_string, :string do @@ -149,7 +164,7 @@ defmodule Orcasite.Radio.Feed do end admin do - table_columns [:id, :name, :slug, :node_name, :location_point, :visible] + table_columns [:id, :name, :slug, :node_name, :location_point, :visible, :online] format_fields location_point: {Jason, :encode!, []}, lat_lng: {Jason, :encode!, []} diff --git a/server/lib/orcasite/radio/feed_segment.ex b/server/lib/orcasite/radio/feed_segment.ex index 73096367..7539828b 100644 --- a/server/lib/orcasite/radio/feed_segment.ex +++ b/server/lib/orcasite/radio/feed_segment.ex @@ -16,6 +16,7 @@ defmodule Orcasite.Radio.FeedSegment do index [:feed_id] index [:feed_stream_id] index [:bucket] + index [:inserted_at] end end diff --git a/server/priv/repo/migrations/20240725191221_add_inserted_at_index_to_feed_segments.exs b/server/priv/repo/migrations/20240725191221_add_inserted_at_index_to_feed_segments.exs new file mode 100644 index 00000000..b7821229 --- /dev/null +++ b/server/priv/repo/migrations/20240725191221_add_inserted_at_index_to_feed_segments.exs @@ -0,0 +1,17 @@ +defmodule Orcasite.Repo.Migrations.AddInsertedAtIndexToFeedSegments do + @moduledoc """ + Updates resources based on their most recent snapshots. + + This file was autogenerated with `mix ash_postgres.generate_migrations` + """ + + use Ecto.Migration + + def up do + create index(:feed_segments, [:inserted_at]) + end + + def down do + drop_if_exists index(:feed_segments, [:inserted_at]) + end +end diff --git a/server/priv/repo/migrations/20240725192709_add_dataplicity_id_to_feeds.exs b/server/priv/repo/migrations/20240725192709_add_dataplicity_id_to_feeds.exs new file mode 100644 index 00000000..67a12859 --- /dev/null +++ b/server/priv/repo/migrations/20240725192709_add_dataplicity_id_to_feeds.exs @@ -0,0 +1,25 @@ +defmodule Orcasite.Repo.Migrations.AddDataplicityIdToFeeds do + @moduledoc """ + Updates resources based on their most recent snapshots. + + This file was autogenerated with `mix ash_postgres.generate_migrations` + """ + + use Ecto.Migration + + def up do + alter table(:feeds) do + add :dataplicity_id, :text + end + + create index(:feeds, [:dataplicity_id]) + end + + def down do + drop_if_exists index(:feeds, [:dataplicity_id]) + + alter table(:feeds) do + remove :dataplicity_id + end + end +end diff --git a/server/priv/resource_snapshots/repo/feed_segments/20240725191221.json b/server/priv/resource_snapshots/repo/feed_segments/20240725191221.json new file mode 100644 index 00000000..7bea71a7 --- /dev/null +++ b/server/priv/resource_snapshots/repo/feed_segments/20240725191221.json @@ -0,0 +1,383 @@ +{ + "attributes": [ + { + "default": "fragment(\"uuid_generate_v7()\")", + "size": null, + "type": "uuid", + "source": "id", + "references": null, + "allow_nil?": false, + "primary_key?": true, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "utc_datetime", + "source": "start_time", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "utc_datetime", + "source": "end_time", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "decimal", + "source": "duration", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "bucket", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "bucket_region", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "cloudfront_url", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "playlist_timestamp", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "playlist_path", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "playlist_m3u8_path", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "segment_path", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "file_name", + "references": null, + "allow_nil?": false, + "primary_key?": false, + "generated?": false + }, + { + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", + "size": null, + "type": "utc_datetime_usec", + "source": "inserted_at", + "references": null, + "allow_nil?": false, + "primary_key?": false, + "generated?": false + }, + { + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", + "size": null, + "type": "utc_datetime_usec", + "source": "updated_at", + "references": null, + "allow_nil?": false, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "uuid", + "source": "feed_id", + "references": { + "name": "feed_segments_feed_id_fkey", + "table": "feeds", + "primary_key?": true, + "schema": null, + "destination_attribute": "id", + "multitenancy": { + "global": null, + "strategy": null, + "attribute": null + }, + "on_delete": null, + "on_update": null, + "deferrable": false, + "match_with": null, + "match_type": null, + "index?": false, + "destination_attribute_default": null, + "destination_attribute_generated": null + }, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "uuid", + "source": "feed_stream_id", + "references": { + "name": "feed_segments_feed_stream_id_fkey", + "table": "feed_streams", + "primary_key?": true, + "schema": null, + "destination_attribute": "id", + "multitenancy": { + "global": null, + "strategy": null, + "attribute": null + }, + "on_delete": null, + "on_update": null, + "deferrable": false, + "match_with": null, + "match_type": null, + "index?": false, + "destination_attribute_default": null, + "destination_attribute_generated": null + }, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + } + ], + "table": "feed_segments", + "hash": "982739E3EA32BAC99918F1BF32FF8221939BA9A923B5E5AA5238E991B2EB9E4C", + "repo": "Elixir.Orcasite.Repo", + "identities": [ + { + "name": "feed_segment_path", + "keys": [ + { + "type": "atom", + "value": "segment_path" + } + ], + "where": null, + "base_filter": null, + "nils_distinct?": true, + "all_tenants?": false, + "index_name": "feed_segments_feed_segment_path_index" + }, + { + "name": "feed_segment_timestamp", + "keys": [ + { + "type": "atom", + "value": "feed_id" + }, + { + "type": "atom", + "value": "start_time" + } + ], + "where": null, + "base_filter": null, + "nils_distinct?": true, + "all_tenants?": false, + "index_name": "feed_segments_feed_segment_timestamp_index" + } + ], + "custom_indexes": [ + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "where": null, + "fields": [ + { + "type": "atom", + "value": "start_time" + } + ], + "unique": false, + "all_tenants?": false, + "error_fields": [ + "start_time" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + }, + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "where": null, + "fields": [ + { + "type": "atom", + "value": "end_time" + } + ], + "unique": false, + "all_tenants?": false, + "error_fields": [ + "end_time" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + }, + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "where": null, + "fields": [ + { + "type": "atom", + "value": "feed_id" + } + ], + "unique": false, + "all_tenants?": false, + "error_fields": [ + "feed_id" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + }, + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "where": null, + "fields": [ + { + "type": "atom", + "value": "feed_stream_id" + } + ], + "unique": false, + "all_tenants?": false, + "error_fields": [ + "feed_stream_id" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + }, + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "where": null, + "fields": [ + { + "type": "atom", + "value": "bucket" + } + ], + "unique": false, + "all_tenants?": false, + "error_fields": [ + "bucket" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + }, + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "where": null, + "fields": [ + { + "type": "atom", + "value": "inserted_at" + } + ], + "unique": false, + "all_tenants?": false, + "error_fields": [ + "inserted_at" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + } + ], + "schema": null, + "multitenancy": { + "global": null, + "strategy": null, + "attribute": null + }, + "base_filter": null, + "check_constraints": [], + "custom_statements": [], + "has_create_action": true +} \ No newline at end of file diff --git a/server/priv/resource_snapshots/repo/feeds/20240725192709.json b/server/priv/resource_snapshots/repo/feeds/20240725192709.json new file mode 100644 index 00000000..4ae7104d --- /dev/null +++ b/server/priv/resource_snapshots/repo/feeds/20240725192709.json @@ -0,0 +1,285 @@ +{ + "attributes": [ + { + "default": "fragment(\"uuid_generate_v7()\")", + "size": null, + "type": "uuid", + "source": "id", + "references": null, + "allow_nil?": false, + "primary_key?": true, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "name", + "references": null, + "allow_nil?": false, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "node_name", + "references": null, + "allow_nil?": false, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "slug", + "references": null, + "allow_nil?": false, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "geometry", + "source": "location_point", + "references": null, + "allow_nil?": false, + "primary_key?": false, + "generated?": false + }, + { + "default": "\"\"", + "size": null, + "type": "text", + "source": "intro_html", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "\"\"", + "size": null, + "type": "text", + "source": "image_url", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "true", + "size": null, + "type": "boolean", + "source": "visible", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "bucket", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "bucket_region", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "cloudfront_url", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "dataplicity_id", + "references": null, + "allow_nil?": true, + "primary_key?": false, + "generated?": false + }, + { + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", + "size": null, + "type": "utc_datetime_usec", + "source": "inserted_at", + "references": null, + "allow_nil?": false, + "primary_key?": false, + "generated?": false + }, + { + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", + "size": null, + "type": "utc_datetime_usec", + "source": "updated_at", + "references": null, + "allow_nil?": false, + "primary_key?": false, + "generated?": false + } + ], + "table": "feeds", + "hash": "99973C5AE25C93EA36B8CDCC884A4A925D9E05BDF3D1659589D36BFBDD9CFF8C", + "repo": "Elixir.Orcasite.Repo", + "identities": [ + { + "name": "unique_slug", + "keys": [ + { + "type": "atom", + "value": "slug" + } + ], + "where": null, + "base_filter": null, + "nils_distinct?": true, + "all_tenants?": false, + "index_name": "feeds_unique_slug_index" + } + ], + "custom_indexes": [ + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "fields": [ + { + "type": "atom", + "value": "name" + } + ], + "where": null, + "unique": false, + "all_tenants?": false, + "error_fields": [ + "name" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + }, + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "fields": [ + { + "type": "atom", + "value": "node_name" + } + ], + "where": null, + "unique": false, + "all_tenants?": false, + "error_fields": [ + "node_name" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + }, + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "fields": [ + { + "type": "atom", + "value": "visible" + } + ], + "where": null, + "unique": false, + "all_tenants?": false, + "error_fields": [ + "visible" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + }, + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "fields": [ + { + "type": "atom", + "value": "slug" + } + ], + "where": null, + "unique": false, + "all_tenants?": false, + "error_fields": [ + "slug" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + }, + { + "message": null, + "name": null, + "table": null, + "include": null, + "prefix": null, + "fields": [ + { + "type": "atom", + "value": "dataplicity_id" + } + ], + "where": null, + "unique": false, + "all_tenants?": false, + "error_fields": [ + "dataplicity_id" + ], + "concurrently": false, + "using": null, + "nulls_distinct": true + } + ], + "schema": null, + "multitenancy": { + "global": null, + "attribute": null, + "strategy": null + }, + "base_filter": null, + "check_constraints": [], + "custom_statements": [], + "has_create_action": true +} \ No newline at end of file diff --git a/ui/src/graphql/generated/index.ts b/ui/src/graphql/generated/index.ts index f1dbdaa5..48c19287 100644 --- a/ui/src/graphql/generated/index.ts +++ b/ui/src/graphql/generated/index.ts @@ -441,6 +441,7 @@ export type Feed = { bucket?: Maybe; bucketRegion?: Maybe; cloudfrontUrl?: Maybe; + feedSegments: Array; feedStreams: Array; id: Scalars["ID"]["output"]; imageUrl?: Maybe; @@ -450,11 +451,19 @@ export type Feed = { mapUrl?: Maybe; name: Scalars["String"]["output"]; nodeName: Scalars["String"]["output"]; + online?: Maybe; slug: Scalars["String"]["output"]; thumbUrl?: Maybe; visible?: Maybe; }; +export type FeedFeedSegmentsArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + sort?: InputMaybe>>; +}; + export type FeedFeedStreamsArgs = { filter?: InputMaybe; limit?: InputMaybe; @@ -523,6 +532,7 @@ export type FeedFilterInput = { bucket?: InputMaybe; bucketRegion?: InputMaybe; cloudfrontUrl?: InputMaybe; + feedSegments?: InputMaybe; feedStreams?: InputMaybe; id?: InputMaybe; imageUrl?: InputMaybe; @@ -531,6 +541,7 @@ export type FeedFilterInput = { name?: InputMaybe; nodeName?: InputMaybe; not?: InputMaybe>; + online?: InputMaybe; or?: InputMaybe>; slug?: InputMaybe; visible?: InputMaybe; @@ -579,6 +590,17 @@ export type FeedFilterNodeName = { notEq?: InputMaybe; }; +export type FeedFilterOnline = { + eq?: InputMaybe; + greaterThan?: InputMaybe; + greaterThanOrEqual?: InputMaybe; + in?: InputMaybe>>; + isNil?: InputMaybe; + lessThan?: InputMaybe; + lessThanOrEqual?: InputMaybe; + notEq?: InputMaybe; +}; + export type FeedFilterSlug = { eq?: InputMaybe; greaterThan?: InputMaybe; @@ -835,6 +857,7 @@ export type FeedSortField = | "LOCATION_POINT" | "NAME" | "NODE_NAME" + | "ONLINE" | "SLUG" | "VISIBLE";