From 60272769429e46a8b2b48426b3b699ef80eb1f16 Mon Sep 17 00:00:00 2001 From: Zack Siri Date: Wed, 27 Nov 2024 20:22:22 +0700 Subject: [PATCH 1/9] Refactor caching for posts module --- lib/opsmaru/cache.ex | 8 +- lib/opsmaru/content/post/manager.ex | 83 +++++++++++-------- lib/opsmaru/sanity.ex | 5 ++ lib/opsmaru_web/live/blog_live.ex | 2 +- lib/opsmaru_web/live/blog_live/index.ex | 38 +++++---- lib/opsmaru_web/live/home_live.ex | 2 +- .../live/hooks/perspective_hook.ex | 4 - test/opsmaru_web/live/blog_live_test.exs | 2 +- 8 files changed, 86 insertions(+), 58 deletions(-) diff --git a/lib/opsmaru/cache.ex b/lib/opsmaru/cache.ex index b07b757..bb8ecc1 100644 --- a/lib/opsmaru/cache.ex +++ b/lib/opsmaru/cache.ex @@ -1,5 +1,11 @@ defmodule Opsmaru.Cache do use Nebulex.Cache, otp_app: :opsmaru, - adapter: Nebulex.Adapters.Replicated + adapter: Nebulex.Adapters.Replicated, + default_key_generator: __MODULE__ + + @behaviour Nebulex.Caching.KeyGenerator + + @impl true + def generate(mod, fun, args), do: :erlang.phash2({mod, fun, args}) end diff --git a/lib/opsmaru/content/post/manager.ex b/lib/opsmaru/content/post/manager.ex index 1452130..0e9ef71 100644 --- a/lib/opsmaru/content/post/manager.ex +++ b/lib/opsmaru/content/post/manager.ex @@ -21,7 +21,7 @@ defmodule Opsmaru.Content.Post.Manager do } """ - @decorate cacheable(cache: Cache, key: {:posts, options}, opts: [ttl: @ttl]) + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) def list(options \\ []) do perspective = Keyword.get(options, :perspective, "published") @@ -31,24 +31,29 @@ defmodule Opsmaru.Content.Post.Manager do last_id = Keyword.get(options, :last_id, "") last_published_at = Keyword.get(options, :last_published_at, Date.utc_today()) - @base_query - |> Sanity.query( - %{ - start_index: start_index, - end_index: end_index, - category: category, - last_id: last_id, - last_published_at: last_published_at - }, - perspective: perspective - ) - |> Sanity.request!(sanity_request_opts()) - |> Sanity.result!() - |> Enum.map(&Post.parse/1) + data = + @base_query + |> Sanity.query( + %{ + start_index: start_index, + end_index: end_index, + category: category, + last_id: last_id, + last_published_at: last_published_at + }, + perspective: perspective + ) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Post.parse/1) + + %{data: data, perspective: perspective} end - @decorate cacheable(cache: Cache, key: {:featured_posts, options}, opts: [ttl: @ttl]) + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) def featured(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + limit = Keyword.get(options, :limit, 3) query = @@ -62,15 +67,18 @@ defmodule Opsmaru.Content.Post.Manager do } """ - query - |> Sanity.query(%{featured: true, limit: limit}, perspective: "published") - |> Sanity.request!(sanity_request_opts()) - |> Sanity.result!() - |> Enum.map(&Post.parse/1) + data = + query + |> Sanity.query(%{featured: true, limit: limit}, perspective: perspective) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Post.parse/1) + + %{data: data, perspective: perspective} end @spec show(String.t(), Keyword.t()) :: %Post{} - @decorate cacheable(cache: Cache, key: {:posts, slug}, opts: [ttl: @ttl]) + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) def show(slug, options \\ []) do perspective = Keyword.get(options, :perspective, "published") @@ -91,12 +99,14 @@ defmodule Opsmaru.Content.Post.Manager do post = Post.parse(post_params) full_content = Req.get!(post.content).body - %{post | content: full_content} + %{data: %{post | content: full_content}, perspective: perspective} end - @spec feed() :: [%Post{}] - @decorate cacheable(cache: Cache, key: :posts_feed, opts: [ttl: :timer.hours(24)]) - def feed do + @spec feed(Keyword.t()) :: [%Post{}] + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: :timer.hours(24)]) + def feed(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + query = ~S""" *[_type == "post" && defined(slug.current)] | order(featured, published_at desc) { ..., @@ -107,17 +117,21 @@ defmodule Opsmaru.Content.Post.Manager do } """ - %Sanity.Response{body: %{"result" => posts}} = + data = query - |> Sanity.query(%{}, perspective: "published") + |> Sanity.query(%{}, perspective: perspective) |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Post.parse/1) - Enum.map(posts, &Post.parse/1) + %{data: data, perspective: perspective} end - @spec count(Keyword.t()) :: integer - @decorate cacheable(cache: Cache, key: {:posts_count, options}, opts: [ttl: :timer.hours(24)]) + @spec count(Keyword.t()) :: %{data: integer, perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: :timer.hours(24)]) def count(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + category = Keyword.get(options, :category) query = ~S""" @@ -127,11 +141,12 @@ defmodule Opsmaru.Content.Post.Manager do ]) """ - %Sanity.Response{body: %{"result" => posts_count}} = + data = query - |> Sanity.query(%{category: category}, perspective: "published") + |> Sanity.query(%{category: category}, perspective: perspective) |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() - posts_count + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/sanity.ex b/lib/opsmaru/sanity.ex index e676518..acb6ca9 100644 --- a/lib/opsmaru/sanity.ex +++ b/lib/opsmaru/sanity.ex @@ -2,4 +2,9 @@ defmodule Opsmaru.Sanity do def sanity_request_opts do Application.get_env(:opsmaru, __MODULE__) end + + def sanity_cache?(%{perspective: "raw"}), do: false + + def sanity_cache?(%{perspective: "published", data: []}), do: false + def sanity_cache?(%{perspective: "published"}), do: true end diff --git a/lib/opsmaru_web/live/blog_live.ex b/lib/opsmaru_web/live/blog_live.ex index 530fa6f..7b99393 100644 --- a/lib/opsmaru_web/live/blog_live.ex +++ b/lib/opsmaru_web/live/blog_live.ex @@ -7,7 +7,7 @@ defmodule OpsmaruWeb.BlogLive do import OpsmaruWeb.MarkdownHelper def mount(%{"id" => slug}, _session, socket) do - post = Content.show_post(slug, perspective: socket.assigns.perspective) + %{data: post} = Content.show_post(slug, perspective: socket.assigns.perspective) socket = socket diff --git a/lib/opsmaru_web/live/blog_live/index.ex b/lib/opsmaru_web/live/blog_live/index.ex index 11afa42..fbebf35 100644 --- a/lib/opsmaru_web/live/blog_live/index.ex +++ b/lib/opsmaru_web/live/blog_live/index.ex @@ -12,7 +12,7 @@ defmodule OpsmaruWeb.BlogLive.Index do def mount(_params, _session, socket) do page = Content.show_page("blog") header_section = Enum.find(page.sections, &(&1.slug == "blog-header")) - featured_posts = Content.featured_posts() + %{data: featured_posts} = Content.featured_posts() categories = Posts.list_categories() socket = @@ -79,7 +79,7 @@ defmodule OpsmaruWeb.BlogLive.Index do def handle_params(%{"page" => page} = params, _url, %{assigns: assigns} = socket) do category = Map.get(params, "category") - posts_count = Content.posts_count(category: category) + %{data: posts_count} = Content.posts_count(category: category) current_path = ~p"/blog?page=#{page}" @@ -90,12 +90,14 @@ defmodule OpsmaruWeb.BlogLive.Index do if last_post do last_post else - Content.list_posts( - end_index: @per_page, - category: category, - perspective: assigns.perspective - ) - |> List.last() + %{data: posts} = + Content.list_posts( + end_index: @per_page, + category: category, + perspective: assigns.perspective + ) + + List.last(posts) end last_id = last_post.id @@ -105,12 +107,15 @@ defmodule OpsmaruWeb.BlogLive.Index do if page == assigns.current_page do assigns.posts else - Content.list_posts( - end_index: @per_page, - last_id: last_id, - last_published_at: last_published_at, - perspective: assigns.perspective - ) + %{data: posts} = + Content.list_posts( + end_index: @per_page, + last_id: last_id, + last_published_at: last_published_at, + perspective: assigns.perspective + ) + + posts end socket = @@ -135,14 +140,15 @@ defmodule OpsmaruWeb.BlogLive.Index do ~p"/blog" end - posts = + %{data: posts} = Content.list_posts( end_index: @per_page, category: category, perspective: assigns.perspective ) - posts_count = Content.posts_count(category: category) + %{data: posts_count} = + Content.posts_count(category: category, perspective: assigns.perspective) socket = socket diff --git a/lib/opsmaru_web/live/home_live.ex b/lib/opsmaru_web/live/home_live.ex index 55dcaf7..c47de2e 100644 --- a/lib/opsmaru_web/live/home_live.ex +++ b/lib/opsmaru_web/live/home_live.ex @@ -13,7 +13,7 @@ defmodule OpsmaruWeb.HomeLive do hero_section = Enum.find(page.sections, &(&1.slug == "home-hero")) slides_section = Enum.find(page.sections, &(&1.slug == "home-slides")) top_bento_section = Enum.find(page.sections, &(&1.slug == "home-top-bento")) - featured_posts = Content.featured_posts() + %{data: featured_posts} = Content.featured_posts() slides = Content.list_slides() diff --git a/lib/opsmaru_web/live/hooks/perspective_hook.ex b/lib/opsmaru_web/live/hooks/perspective_hook.ex index 33fc0c2..edfdd0e 100644 --- a/lib/opsmaru_web/live/hooks/perspective_hook.ex +++ b/lib/opsmaru_web/live/hooks/perspective_hook.ex @@ -1,14 +1,10 @@ defmodule OpsmaruWeb.PerspectiveHook do import Phoenix.Component - alias Opsmaru.Cache - def on_mount(:default, _params, _session, %{host_uri: uri} = socket) do socket = assign_new(socket, :perspective, fn -> if uri.host =~ "preview" do - Cache.flush() - "raw" else "published" diff --git a/test/opsmaru_web/live/blog_live_test.exs b/test/opsmaru_web/live/blog_live_test.exs index f065ef7..3547e83 100644 --- a/test/opsmaru_web/live/blog_live_test.exs +++ b/test/opsmaru_web/live/blog_live_test.exs @@ -7,7 +7,7 @@ defmodule OpsmaruWeb.BlogLiveTest do describe "blog page" do test "can visit blog page", %{conn: conn} do - posts = Content.list_posts() + %{data: posts} = Content.list_posts() alpine_post = Enum.find(posts, fn post -> post.slug == "alpine-linux" end) From 51c4b419ad6eed763c244ab7bd4a84d5e1ec318f Mon Sep 17 00:00:00 2001 From: Zack Siri Date: Wed, 27 Nov 2024 20:25:44 +0700 Subject: [PATCH 2/9] Add type spec --- lib/opsmaru/content/post/manager.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/opsmaru/content/post/manager.ex b/lib/opsmaru/content/post/manager.ex index 0e9ef71..75c0de3 100644 --- a/lib/opsmaru/content/post/manager.ex +++ b/lib/opsmaru/content/post/manager.ex @@ -21,6 +21,7 @@ defmodule Opsmaru.Content.Post.Manager do } """ + @spec featured(Keyword.t()) :: %{data: [%Post{}], perspective: String.t()} @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) def list(options \\ []) do perspective = Keyword.get(options, :perspective, "published") @@ -50,6 +51,7 @@ defmodule Opsmaru.Content.Post.Manager do %{data: data, perspective: perspective} end + @spec featured(Keyword.t()) :: %{data: [%Post{}], perspective: String.t()} @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) def featured(options \\ []) do perspective = Keyword.get(options, :perspective, "published") @@ -77,7 +79,7 @@ defmodule Opsmaru.Content.Post.Manager do %{data: data, perspective: perspective} end - @spec show(String.t(), Keyword.t()) :: %Post{} + @spec show(String.t(), Keyword.t()) :: %{data: %Post{}, perspective: String.t()} @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) def show(slug, options \\ []) do perspective = Keyword.get(options, :perspective, "published") From 7a72d036ee81a97820d7efe656d6a4ca47e0a5d2 Mon Sep 17 00:00:00 2001 From: Zack Siri Date: Wed, 27 Nov 2024 20:43:22 +0700 Subject: [PATCH 3/9] Add perspective support for show_page call --- lib/opsmaru/content/page/manager.ex | 54 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/lib/opsmaru/content/page/manager.ex b/lib/opsmaru/content/page/manager.ex index 3a63b66..670bd7a 100644 --- a/lib/opsmaru/content/page/manager.ex +++ b/lib/opsmaru/content/page/manager.ex @@ -7,40 +7,38 @@ defmodule Opsmaru.Content.Page.Manager do @ttl :timer.hours(1) - @decorate cacheable(cache: Cache, key: {:pages, slug}, opts: [ttl: @ttl]) - def show(slug) do - ~S""" - *[_type == "page" && slug.current == $slug][0]{ - ..., - "sections": *[ _type == "pageSection" && references(^._id) ]{ + @spec show(String.t(), Keyword.t()) :: %{data: %Page{}, perspective: String.t()} + @decorate cacheable(cache: Cache, opts: [ttl: @ttl]) + def show(slug, options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + + page = + ~S""" + *[_type == "page" && slug.current == $slug][0]{ ..., - "contents": *[ _type == "pageContent" && references(^._id) ]{ + "sections": *[ _type == "pageSection" && references(^._id) ]{ ..., - "markdown": markdown.asset -> url - }, - "cards": *[ _type == "pageCard" && references(^._id) ]{ - ..., - card -> {..., "cover": {"url": cover.asset -> url, "alt": cover.alt}} + "contents": *[ _type == "pageContent" && references(^._id) ]{ + ..., + "markdown": markdown.asset -> url + }, + "cards": *[ _type == "pageCard" && references(^._id) ]{ + ..., + card -> {..., "cover": {"url": cover.asset -> url, "alt": cover.alt}} + } } } - } - """ - |> Sanity.query(%{"slug" => slug}, perspective: "published") - |> Sanity.request!(sanity_request_opts()) - |> Sanity.result!() - |> case do - nil -> - nil - - page -> - page = Page.parse(page) + """ + |> Sanity.query(%{"slug" => slug}, perspective: perspective) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Page.parse() - sections = - page.sections - |> Enum.map(&process_section/1) + sections = + page.sections + |> Enum.map(&process_section/1) - %{page | sections: sections} - end + %{data: %{page | sections: sections}, perspective: perspective} end defp process_section(section) do From 3c130a1d9bdca83330b267b96e5776aaa10cfbc5 Mon Sep 17 00:00:00 2001 From: Zack Siri Date: Wed, 27 Nov 2024 20:43:57 +0700 Subject: [PATCH 4/9] Add conditional caching --- lib/opsmaru/content/page/manager.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/opsmaru/content/page/manager.ex b/lib/opsmaru/content/page/manager.ex index 670bd7a..39ac92f 100644 --- a/lib/opsmaru/content/page/manager.ex +++ b/lib/opsmaru/content/page/manager.ex @@ -8,7 +8,7 @@ defmodule Opsmaru.Content.Page.Manager do @ttl :timer.hours(1) @spec show(String.t(), Keyword.t()) :: %{data: %Page{}, perspective: String.t()} - @decorate cacheable(cache: Cache, opts: [ttl: @ttl]) + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) def show(slug, options \\ []) do perspective = Keyword.get(options, :perspective, "published") From 3ae323ed0c7e73b6dac4115de9d63e13aa371d42 Mon Sep 17 00:00:00 2001 From: Zack Siri Date: Thu, 28 Nov 2024 10:44:31 +0700 Subject: [PATCH 5/9] Add support for dynamic perspective and improve caching --- lib/opsmaru/content.ex | 10 ++--- lib/opsmaru/content/course/manager.ex | 5 ++- lib/opsmaru/content/logo/manager.ex | 14 ++++--- lib/opsmaru/content/movie/manager.ex | 14 ++++--- lib/opsmaru/content/price/manager.ex | 2 +- lib/opsmaru/content/product/manager.ex | 44 +++++++++++----------- lib/opsmaru/content/slide/manager.ex | 13 ++++--- lib/opsmaru/content/technology/manager.ex | 21 +++++++---- lib/opsmaru/content/testimonial/manager.ex | 18 +++++---- lib/opsmaru/courses/category/manager.ex | 22 +++++++---- lib/opsmaru/courses/episode/manager.ex | 5 ++- lib/opsmaru/courses/section/manager.ex | 18 ++++++--- lib/opsmaru/features.ex | 2 +- lib/opsmaru/features/category/manager.ex | 24 ++++++------ lib/opsmaru/pages.ex | 2 +- lib/opsmaru/pages/faq/manager.ex | 19 ++++++---- lib/opsmaru/posts/category/manager.ex | 17 +++++++-- lib/opsmaru/products/feature/manager.ex | 23 +++++------ lib/opsmaru_web/live/blog_live.ex | 4 +- lib/opsmaru_web/live/blog_live/index.ex | 8 ++-- lib/opsmaru_web/live/course_live.ex | 8 ++-- lib/opsmaru_web/live/course_live/index.ex | 15 +++++--- lib/opsmaru_web/live/episode_live.ex | 15 +++++--- lib/opsmaru_web/live/home_live.ex | 10 ++--- lib/opsmaru_web/live/legal_live.ex | 2 +- lib/opsmaru_web/live/pricing_live.ex | 26 +++++++------ test/opsmaru_web/live/course_live_test.exs | 2 +- 27 files changed, 215 insertions(+), 148 deletions(-) diff --git a/lib/opsmaru/content.ex b/lib/opsmaru/content.ex index b1d15e8..6d4b4a8 100644 --- a/lib/opsmaru/content.ex +++ b/lib/opsmaru/content.ex @@ -19,7 +19,7 @@ defmodule Opsmaru.Content do alias __MODULE__.Technology - defdelegate list_technologies(options \\ [end_index: 5]), + defdelegate list_technologies(options \\ []), to: Technology.Manager, as: :list @@ -43,7 +43,7 @@ defmodule Opsmaru.Content do to: Post.Manager, as: :featured - defdelegate posts_feed, + defdelegate posts_feed(options \\ []), to: Post.Manager, as: :feed @@ -59,19 +59,19 @@ defmodule Opsmaru.Content do alias __MODULE__.Movie - defdelegate show_movie(slug), + defdelegate show_movie(slug, options \\ []), to: Movie.Manager, as: :show alias __MODULE__.Product - defdelegate list_products, + defdelegate list_products(options \\ []), to: Product.Manager, as: :list alias __MODULE__.Page - defdelegate show_page(slug), + defdelegate show_page(slug, options \\ []), to: Page.Manager, as: :show diff --git a/lib/opsmaru/content/course/manager.ex b/lib/opsmaru/content/course/manager.ex index a63dfcb..d95b0ee 100644 --- a/lib/opsmaru/content/course/manager.ex +++ b/lib/opsmaru/content/course/manager.ex @@ -7,7 +7,8 @@ defmodule Opsmaru.Content.Course.Manager do @ttl :timer.hours(1) - @decorate cacheable(cache: Cache, key: {:course, slug}, opts: [ttl: @ttl]) + @spec show(String.t(), Keyword.t()) :: %{data: %Course{}, perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) def show(slug, options \\ []) do perspective = Keyword.get(options, :perspective, "published") @@ -38,6 +39,6 @@ defmodule Opsmaru.Content.Course.Manager do course = Course.parse(course) full_overview = Req.get!(course.overview).body - %{course | overview: full_overview} + %{data: %{course | overview: full_overview}, perspective: perspective} end end diff --git a/lib/opsmaru/content/logo/manager.ex b/lib/opsmaru/content/logo/manager.ex index 1c8493b..6ae0fee 100644 --- a/lib/opsmaru/content/logo/manager.ex +++ b/lib/opsmaru/content/logo/manager.ex @@ -5,17 +5,21 @@ defmodule Opsmaru.Content.Logo.Manager do alias Opsmaru.Cache alias Opsmaru.Content.Logo - @decorate cacheable(cache: Cache, key: :logos, opts: [ttl: :timer.hours(1)]) - def list(_options \\ []) do + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: :timer.hours(1)]) + def list(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + query = ~s""" *[_type == "logo"] | order(name asc){..., "image": image.asset -> url} """ - %Sanity.Response{body: %{"result" => logos}} = + data = query - |> Sanity.query(%{}, perspective: "published") + |> Sanity.query(%{}, perspective: perspective) |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Logo.parse/1) - Enum.map(logos, &Logo.parse/1) + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/content/movie/manager.ex b/lib/opsmaru/content/movie/manager.ex index b22b6b8..fdbde84 100644 --- a/lib/opsmaru/content/movie/manager.ex +++ b/lib/opsmaru/content/movie/manager.ex @@ -6,8 +6,10 @@ defmodule Opsmaru.Content.Movie.Manager do alias Opsmaru.Content.Movie @spec show(String.t()) :: %Movie{} - @decorate cacheable(cache: Cache, key: {:movie, slug}, opts: [ttl: :timer.hours(1)]) - def show(slug) do + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: :timer.hours(1)]) + def show(slug, options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + query = ~S""" *[_type == "movie" && slug.current == $slug][0]{ ..., @@ -19,11 +21,13 @@ defmodule Opsmaru.Content.Movie.Manager do } """ - %Sanity.Response{body: %{"result" => movie_params}} = + data = query - |> Sanity.query(%{slug: slug}, perspective: "published") + |> Sanity.query(%{slug: slug}, perspective: perspective) |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Movie.parse() - Movie.parse(movie_params) + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/content/price/manager.ex b/lib/opsmaru/content/price/manager.ex index 97b784f..5bb89a0 100644 --- a/lib/opsmaru/content/price/manager.ex +++ b/lib/opsmaru/content/price/manager.ex @@ -5,7 +5,7 @@ defmodule Opsmaru.Content.Price.Manager do @ttl :timer.hours(1) - @decorate cacheable(cache: Cache, key: :prices, opts: [ttl: @ttl]) + @decorate cacheable(cache: Cache, opts: [ttl: @ttl]) def list do query = "active: 'true' AND metadata['app']: 'instellar'" diff --git a/lib/opsmaru/content/product/manager.ex b/lib/opsmaru/content/product/manager.ex index 81d59fd..b72c71c 100644 --- a/lib/opsmaru/content/product/manager.ex +++ b/lib/opsmaru/content/product/manager.ex @@ -7,33 +7,33 @@ defmodule Opsmaru.Content.Product.Manager do @ttl :timer.hours(1) - @decorate cacheable(cache: Cache, key: :products, opts: [ttl: @ttl]) - def list(_options \\ []) do + @spec list(Keyword.t()) :: %{data: [%Product{}], perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) + def list(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + stripe_products = load_stripe_products() sanity_query = ~S""" - *[_type == "product"] + *[_type == "product"] | order(index asc) """ - Sanity.query(sanity_query, %{}) - |> Sanity.request!(sanity_request_opts()) - |> case do - %Sanity.Response{body: %{"result" => products}} -> - products - |> Enum.map(&Product.parse/1) - |> Enum.map(fn product -> - matched_product = - Enum.find(stripe_products, fn stripe_product -> - product.reference == stripe_product.name - end) - - %{product | stripe_product: matched_product} - end) - |> Enum.sort_by(& &1.index, :asc) - - error -> - error - end + data = + sanity_query + |> Sanity.query(%{}, perspective: perspective) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Product.parse/1) + |> Enum.map(fn product -> + matched_product = + Enum.find(stripe_products, fn stripe_product -> + product.reference == stripe_product.name + end) + + %{product | stripe_product: matched_product} + end) + + %{data: data, perspective: perspective} end defp load_stripe_products do diff --git a/lib/opsmaru/content/slide/manager.ex b/lib/opsmaru/content/slide/manager.ex index 0f66618..5e8322f 100644 --- a/lib/opsmaru/content/slide/manager.ex +++ b/lib/opsmaru/content/slide/manager.ex @@ -7,9 +7,10 @@ defmodule Opsmaru.Content.Slide.Manager do @ttl :timer.hours(1) - @decorate cacheable(cache: Cache, key: {:slides, options}, opts: [ttl: @ttl]) + @spec list(Keyword.t()) :: %{data: [%Slide{}], perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) def list(options \\ []) do - options = Enum.into(options, %{}) + perspective = Keyword.get(options, :perspective, "published") query = ~S""" @@ -19,11 +20,13 @@ defmodule Opsmaru.Content.Slide.Manager do } """ - %Sanity.Response{body: %{"result" => slides}} = + data = query - |> Sanity.query(options, perspective: "published") + |> Sanity.query(%{}, perspective: perspective) |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Slide.parse/1) - Enum.map(slides, &Slide.parse/1) + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/content/technology/manager.ex b/lib/opsmaru/content/technology/manager.ex index f3e4799..2c399c5 100644 --- a/lib/opsmaru/content/technology/manager.ex +++ b/lib/opsmaru/content/technology/manager.ex @@ -7,9 +7,11 @@ defmodule Opsmaru.Content.Technology.Manager do @ttl :timer.hours(1) - @decorate cacheable(cache: Cache, key: {:technologies, options}, opts: [ttl: @ttl]) - def list(options \\ [end_index: 5]) do - options = Enum.into(options, %{}) + @spec list(Keyword.t()) :: %{data: [%Technology{}], perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) + def list(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + end_index = Keyword.get(options, :end_index, 5) query = ~S""" *[_type == "technology"][0..$end_index] { @@ -19,10 +21,13 @@ defmodule Opsmaru.Content.Technology.Manager do } """ - query - |> Sanity.query(options, perspective: "published") - |> Sanity.request!(sanity_request_opts()) - |> Sanity.result!() - |> Enum.map(&Technology.parse/1) + data = + query + |> Sanity.query(%{end_index: end_index}, perspective: perspective) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Technology.parse/1) + + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/content/testimonial/manager.ex b/lib/opsmaru/content/testimonial/manager.ex index 850a673..e3316f5 100644 --- a/lib/opsmaru/content/testimonial/manager.ex +++ b/lib/opsmaru/content/testimonial/manager.ex @@ -7,9 +7,10 @@ defmodule Opsmaru.Content.Testimonial.Manager do @ttl :timer.hours(1) - @decorate cacheable(cache: Cache, key: {:testimonials, options}, opts: [ttl: @ttl]) + @spec list(Keyword.t()) :: %{data: [%Testimonial{}], perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) def list(options \\ []) do - options = Enum.into(options, %{}) + perspective = Keyword.get(options, :perspective, "published") query = ~S""" *[_type == "testimonial"] { @@ -18,10 +19,13 @@ defmodule Opsmaru.Content.Testimonial.Manager do } """ - query - |> Sanity.query(options, perspective: "published") - |> Sanity.request!(sanity_request_opts()) - |> Sanity.result!() - |> Enum.map(&Testimonial.parse/1) + data = + query + |> Sanity.query(options, perspective: perspective) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Testimonial.parse/1) + + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/courses/category/manager.ex b/lib/opsmaru/courses/category/manager.ex index 44eb9b1..db339e4 100644 --- a/lib/opsmaru/courses/category/manager.ex +++ b/lib/opsmaru/courses/category/manager.ex @@ -1,10 +1,15 @@ defmodule Opsmaru.Courses.Category.Manager do + use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Cache alias Opsmaru.Courses.Category - def list(options \\ [featured: false]) do - options = Enum.into(options, %{}) + @spec list(Keyword.t()) :: %{data: [%Category{}], perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: :timer.hours(1)]) + def list(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + featured = Keyword.get(options, :featured, false) query = ~s""" *[_type == "courseCategory" && featured == $featured] | order(index asc){ @@ -28,10 +33,13 @@ defmodule Opsmaru.Courses.Category.Manager do } """ - query - |> Sanity.query(options, perspective: "published") - |> Sanity.request!(sanity_request_opts()) - |> Sanity.result!() - |> Enum.map(&Category.parse/1) + data = + query + |> Sanity.query(%{featured: featured}, perspective: perspective) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Category.parse/1) + + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/courses/episode/manager.ex b/lib/opsmaru/courses/episode/manager.ex index 7759d45..b578813 100644 --- a/lib/opsmaru/courses/episode/manager.ex +++ b/lib/opsmaru/courses/episode/manager.ex @@ -2,8 +2,11 @@ defmodule Opsmaru.Courses.Episode.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Cache alias Opsmaru.Courses.Episode + @spec show(String.t(), String.t(), Keyword.t()) :: %{data: %Episode{}, perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: :timer.hours(1)]) def show(course_slug, episode_slug, options \\ []) do perspective = Keyword.get(options, :perspective, "published") @@ -34,6 +37,6 @@ defmodule Opsmaru.Courses.Episode.Manager do full_content = Req.get!(episode.content).body - %{episode | content: full_content} + %{data: %{episode | content: full_content}, perspective: perspective} end end diff --git a/lib/opsmaru/courses/section/manager.ex b/lib/opsmaru/courses/section/manager.ex index 8c8c3a2..45a5dff 100644 --- a/lib/opsmaru/courses/section/manager.ex +++ b/lib/opsmaru/courses/section/manager.ex @@ -4,7 +4,10 @@ defmodule Opsmaru.Courses.Section.Manager do alias Opsmaru.Courses.Section - def list(course_id: course_id) do + def list(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + course_id = Keyword.fetch!(options, :course_id) + query = ~S""" *[_type == "courseSection" && course._ref == $course_id] | order(index asc){ ..., @@ -24,10 +27,13 @@ defmodule Opsmaru.Courses.Section.Manager do } """ - query - |> Sanity.query(%{course_id: "#{course_id}"}, perspective: "published") - |> Sanity.request!(sanity_request_opts()) - |> Sanity.result!() - |> Enum.map(&Section.parse/1) + data = + query + |> Sanity.query(%{course_id: "#{course_id}"}, perspective: perspective) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Section.parse/1) + + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/features.ex b/lib/opsmaru/features.ex index a558e04..776e3c6 100644 --- a/lib/opsmaru/features.ex +++ b/lib/opsmaru/features.ex @@ -1,7 +1,7 @@ defmodule Opsmaru.Features do alias __MODULE__.Category - defdelegate list_categories(options \\ [cache: true]), + defdelegate list_categories(options \\ []), to: Category.Manager, as: :list end diff --git a/lib/opsmaru/features/category/manager.ex b/lib/opsmaru/features/category/manager.ex index 6cd14fd..faaf8b1 100644 --- a/lib/opsmaru/features/category/manager.ex +++ b/lib/opsmaru/features/category/manager.ex @@ -17,18 +17,18 @@ defmodule Opsmaru.Features.Category.Manager do } """ - @decorate cacheable(cache: Cache, key: :feature_categories, opts: [ttl: @ttl]) - def list(_options \\ []) do - @base_query - |> Sanity.query(%{}, perspective: "published") - |> Sanity.request!(sanity_request_opts()) - |> case do - %Sanity.Response{body: %{"result" => categories}} -> - Enum.map(categories, &Category.parse/1) - |> Enum.sort_by(& &1.index, :asc) + @spec list(Keyword.t()) :: %{data: [%Category{}], perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) + def list(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") - error -> - error - end + data = + @base_query + |> Sanity.query(%{}, perspective: perspective) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Category.parse/1) + + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/pages.ex b/lib/opsmaru/pages.ex index 42ddd9a..ba68089 100644 --- a/lib/opsmaru/pages.ex +++ b/lib/opsmaru/pages.ex @@ -1,7 +1,7 @@ defmodule Opsmaru.Pages do alias __MODULE__.FAQ - defdelegate list_faqs(page), + defdelegate list_faqs(page, options \\ []), to: FAQ.Manager, as: :list end diff --git a/lib/opsmaru/pages/faq/manager.ex b/lib/opsmaru/pages/faq/manager.ex index 478a71a..f303f61 100644 --- a/lib/opsmaru/pages/faq/manager.ex +++ b/lib/opsmaru/pages/faq/manager.ex @@ -9,18 +9,21 @@ defmodule Opsmaru.Pages.FAQ.Manager do @ttl :timer.hours(1) @base_query ~S""" - *[_type == "pageFaq"]{..., faq -> {question, answer}} + *[_type == "pageFaq" && page -> slug.current == $page_slug] | order(index asc) {..., faq -> {question, answer}} """ - @decorate cacheable(cache: Cache, key: {:faqs, page_slug}, opts: [ttl: @ttl]) - def list(%Page{slug: page_slug}) do - %Sanity.Response{body: %{"result" => faqs}} = + @spec list(%Page{}, Keyword.t()) :: %{data: [%FAQ{}], perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) + def list(%Page{slug: page_slug}, options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + + data = @base_query - |> Sanity.query(%{"page.slug.current" => page_slug}, perspective: "published") + |> Sanity.query(%{page_slug: page_slug}, perspective: perspective) |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&FAQ.parse/1) - faqs - |> Enum.map(&FAQ.parse/1) - |> Enum.sort_by(& &1.index, :asc) + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/posts/category/manager.ex b/lib/opsmaru/posts/category/manager.ex index 08dfbad..3552101 100644 --- a/lib/opsmaru/posts/category/manager.ex +++ b/lib/opsmaru/posts/category/manager.ex @@ -1,20 +1,29 @@ defmodule Opsmaru.Posts.Category.Manager do + use Nebulex.Caching import Opsmaru.Sanity alias Opsmaru.Posts.Category - def list(_options \\ []) do + @ttl :timer.hours(1) + + @spec list(Keyword.t()) :: %{data: [%Category{}], perspective: String.t()} + @decorate cacheable(cache: Opsmaru.Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) + def list(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + query = ~s""" *[_type == "postCategory" && count(*[_type == "post" && defined(slug.current) && ^._id in categories[]._ref]) > 0 ] | order(name asc){...} """ - %Sanity.Response{body: %{"result" => categories}} = + data = query - |> Sanity.query(%{}, perspective: "published") + |> Sanity.query(%{}, perspective: perspective) |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Category.parse/1) - Enum.map(categories, &Category.parse/1) + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/products/feature/manager.ex b/lib/opsmaru/products/feature/manager.ex index 9389e5b..12e7d72 100644 --- a/lib/opsmaru/products/feature/manager.ex +++ b/lib/opsmaru/products/feature/manager.ex @@ -13,17 +13,18 @@ defmodule Opsmaru.Products.Feature.Manager do } """ - @decorate cacheable(cache: Cache, key: :product_features, opts: [ttl: @ttl]) - def list(_options \\ []) do - @base_query - |> Sanity.query(%{}, perspective: "published") - |> Sanity.request!(sanity_request_opts()) - |> case do - %Sanity.Response{body: %{"result" => features}} -> - Enum.map(features, &Feature.parse/1) + @spec list(Keyword.t()) :: %{data: [%Feature{}], perspective: String.t()} + @decorate cacheable(cache: Cache, match: &sanity_cache?/1, opts: [ttl: @ttl]) + def list(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") - error -> - error - end + data = + @base_query + |> Sanity.query(%{}, perspective: perspective) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Feature.parse/1) + + %{data: data, perspective: perspective} end end diff --git a/lib/opsmaru_web/live/blog_live.ex b/lib/opsmaru_web/live/blog_live.ex index 7b99393..ded4f79 100644 --- a/lib/opsmaru_web/live/blog_live.ex +++ b/lib/opsmaru_web/live/blog_live.ex @@ -6,8 +6,8 @@ defmodule OpsmaruWeb.BlogLive do import OpsmaruWeb.MarkdownHelper - def mount(%{"id" => slug}, _session, socket) do - %{data: post} = Content.show_post(slug, perspective: socket.assigns.perspective) + def mount(%{"id" => slug}, _session, %{assigns: assigns} = socket) do + %{data: post} = Content.show_post(slug, perspective: assigns.perspective) socket = socket diff --git a/lib/opsmaru_web/live/blog_live/index.ex b/lib/opsmaru_web/live/blog_live/index.ex index fbebf35..8c0e6cf 100644 --- a/lib/opsmaru_web/live/blog_live/index.ex +++ b/lib/opsmaru_web/live/blog_live/index.ex @@ -9,11 +9,11 @@ defmodule OpsmaruWeb.BlogLive.Index do @per_page 5 - def mount(_params, _session, socket) do - page = Content.show_page("blog") + def mount(_params, _session, %{assigns: assigns} = socket) do + %{data: page} = Content.show_page("blog", perspective: assigns.perspective) header_section = Enum.find(page.sections, &(&1.slug == "blog-header")) - %{data: featured_posts} = Content.featured_posts() - categories = Posts.list_categories() + %{data: featured_posts} = Content.featured_posts(perspective: assigns.perspective) + %{data: categories} = Posts.list_categories(perspective: assigns.perspective) socket = socket diff --git a/lib/opsmaru_web/live/course_live.ex b/lib/opsmaru_web/live/course_live.ex index 66fccfd..3ec2d61 100644 --- a/lib/opsmaru_web/live/course_live.ex +++ b/lib/opsmaru_web/live/course_live.ex @@ -11,9 +11,11 @@ defmodule OpsmaruWeb.CourseLive do import OpsmaruWeb.MarkdownHelper - def mount(%{"id" => slug}, _session, socket) do - course = Content.show_course(slug, perspective: socket.assigns.perspective) - sections = Courses.list_sections(course_id: course.id) + def mount(%{"id" => slug}, _session, %{assigns: assigns} = socket) do + %{data: course} = Content.show_course(slug, perspective: assigns.perspective) + + %{data: sections} = + Courses.list_sections(course_id: course.id, perspective: assigns.perspective) first_section = Enum.find(sections, fn section -> section.index == 1 end) diff --git a/lib/opsmaru_web/live/course_live/index.ex b/lib/opsmaru_web/live/course_live/index.ex index 6aa6537..2709d6f 100644 --- a/lib/opsmaru_web/live/course_live/index.ex +++ b/lib/opsmaru_web/live/course_live/index.ex @@ -9,15 +9,20 @@ defmodule OpsmaruWeb.CourseLive.Index do @page_slug "learn" - def mount(_params, _session, socket) do - page = Content.show_page(@page_slug) + def mount(_params, _session, %{assigns: assigns} = socket) do + %{data: page} = Content.show_page(@page_slug, perspective: assigns.perspective) header_section = Enum.find(page.sections, &(&1.slug == "#{@page_slug}-header")) get_support_section = Enum.find(page.sections, &(&1.slug == "#{@page_slug}-get-support")) - featured_categories = Courses.list_categories(featured: true) - technologies = Content.list_technologies(end_index: 12) - categories = Courses.list_categories(featured: false) + %{data: featured_categories} = + Courses.list_categories(featured: true, perspective: assigns.perspective) + + %{data: technologies} = + Content.list_technologies(end_index: 12, perspective: assigns.perspective) + + %{data: categories} = + Courses.list_categories(featured: false, perspective: assigns.perspective) socket = socket diff --git a/lib/opsmaru_web/live/episode_live.ex b/lib/opsmaru_web/live/episode_live.ex index 8f4aa4b..74390c1 100644 --- a/lib/opsmaru_web/live/episode_live.ex +++ b/lib/opsmaru_web/live/episode_live.ex @@ -10,13 +10,18 @@ defmodule OpsmaruWeb.EpisodeLive do import OpsmaruWeb.MarkdownHelper - def mount(%{"course_id" => course_slug, "id" => episode_slug}, _session, socket) do - course = Content.show_course(course_slug, perspective: socket.assigns.perspective) + def mount( + %{"course_id" => course_slug, "id" => episode_slug}, + _session, + %{assigns: assigns} = socket + ) do + %{data: course} = Content.show_course(course_slug, perspective: assigns.perspective) - episode = - Courses.show_episode(course_slug, episode_slug, perspective: socket.assigns.perspective) + %{data: episode} = + Courses.show_episode(course_slug, episode_slug, perspective: assigns.perspective) - sections = Courses.list_sections(course_id: course.id) + %{data: sections} = + Courses.list_sections(course_id: course.id, perspective: assigns.perspective) socket = socket diff --git a/lib/opsmaru_web/live/home_live.ex b/lib/opsmaru_web/live/home_live.ex index c47de2e..52bc9fa 100644 --- a/lib/opsmaru_web/live/home_live.ex +++ b/lib/opsmaru_web/live/home_live.ex @@ -7,19 +7,19 @@ defmodule OpsmaruWeb.HomeLive do alias OpsmaruWeb.HomeComponents alias OpsmaruWeb.BlogComponents - def mount(_params, _session, socket) do - page = Content.show_page("home") + def mount(_params, _session, %{assigns: assigns} = socket) do + %{data: page} = Content.show_page("home", perspective: assigns.perspective) hero_section = Enum.find(page.sections, &(&1.slug == "home-hero")) slides_section = Enum.find(page.sections, &(&1.slug == "home-slides")) top_bento_section = Enum.find(page.sections, &(&1.slug == "home-top-bento")) %{data: featured_posts} = Content.featured_posts() - slides = Content.list_slides() + %{data: slides} = Content.list_slides(perspective: assigns.perspective) - logos = Content.list_logos() + %{data: logos} = Content.list_logos(perspective: assigns.perspective) - testimonials = Content.list_testimonials() + %{data: testimonials} = Content.list_testimonials(perspective: assigns.perspective) socket = socket diff --git a/lib/opsmaru_web/live/legal_live.ex b/lib/opsmaru_web/live/legal_live.ex index 2e14378..4705a5e 100644 --- a/lib/opsmaru_web/live/legal_live.ex +++ b/lib/opsmaru_web/live/legal_live.ex @@ -10,7 +10,7 @@ defmodule OpsmaruWeb.LegalLive do end def mount(%{"id" => slug}, _session, socket) do - with %Content.Page{} = page <- Content.show_page(slug) do + with %{data: %Content.Page{} = page} <- Content.show_page(slug) do main_section = Enum.find(page.sections, &(&1.slug == "#{slug}-main")) diff --git a/lib/opsmaru_web/live/pricing_live.ex b/lib/opsmaru_web/live/pricing_live.ex index 8ded1ea..c1cd35a 100644 --- a/lib/opsmaru_web/live/pricing_live.ex +++ b/lib/opsmaru_web/live/pricing_live.ex @@ -13,17 +13,17 @@ defmodule OpsmaruWeb.PricingLive do import __MODULE__.DataLoader @impl true - def mount(_params, _session, socket) do + def mount(_params, _session, %{assigns: assigns} = socket) do prices = load_prices() - page = Content.show_page("pricing") - faqs = Pages.list_faqs(page) + %{data: page} = Content.show_page("pricing", perspective: assigns.perspective) + %{data: faqs} = Pages.list_faqs(page, perspective: assigns.perspective) - categories = Features.list_categories() - product_features = Products.list_features() - logos = Content.list_logos() + %{data: categories} = Features.list_categories(perspective: assigns.perspective) + %{data: product_features} = Products.list_features(perspective: assigns.perspective) + %{data: logos} = Content.list_logos(perspective: assigns.perspective) - testimonials = Content.list_testimonials() + %{data: testimonials} = Content.list_testimonials(perspective: assigns.perspective) selected_testimonial = Enum.random(testimonials) @@ -219,13 +219,15 @@ defmodule OpsmaruWeb.PricingLive do end @impl true - def handle_params(%{"interval" => interval}, _uri, socket) do + def handle_params(%{"interval" => interval}, _uri, %{assigns: assigns} = socket) do prices = load_prices(interval) active_products_names = Enum.map(prices, & &1.product.name) + %{data: products} = Content.list_products(perspective: assigns.perspective) + products = - Content.list_products() + products |> Enum.filter(fn product -> product.reference in active_products_names end) @@ -239,12 +241,14 @@ defmodule OpsmaruWeb.PricingLive do {:noreply, socket} end - def handle_params(_, _uri, socket) do + def handle_params(_, _uri, %{assigns: assigns} = socket) do prices = load_prices() active_products_names = Enum.map(prices, & &1.product.name) + %{data: products} = Content.list_products(perspective: assigns.perspective) + products = - Content.list_products() + products |> Enum.filter(fn product -> product.reference in active_products_names end) diff --git a/test/opsmaru_web/live/course_live_test.exs b/test/opsmaru_web/live/course_live_test.exs index 87f691a..0f5b13e 100644 --- a/test/opsmaru_web/live/course_live_test.exs +++ b/test/opsmaru_web/live/course_live_test.exs @@ -7,7 +7,7 @@ defmodule OpsmaruWeb.CourseLiveTest do describe "course detail page" do test "can visit course detail page", %{conn: conn} do - categories = Courses.list_categories(featured: true) + %{data: categories} = Courses.list_categories(featured: true) courses = Enum.flat_map(categories, fn category -> category.courses end) course = Enum.find(courses, fn course -> course.slug == "setup-aws-infrastructure" end) From 2e4fc55eb44f03ed723cee88407a31a7f6ba7083 Mon Sep 17 00:00:00 2001 From: Zack Siri Date: Thu, 28 Nov 2024 11:03:57 +0700 Subject: [PATCH 6/9] Pass perspectives in to top_bento section --- lib/opsmaru_web/components/home_components.ex | 33 ++++++++++++++----- lib/opsmaru_web/live/home_live.ex | 3 +- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/lib/opsmaru_web/components/home_components.ex b/lib/opsmaru_web/components/home_components.ex index 4982c7a..b222cd6 100644 --- a/lib/opsmaru_web/components/home_components.ex +++ b/lib/opsmaru_web/components/home_components.ex @@ -97,6 +97,7 @@ defmodule OpsmaruWeb.HomeComponents do end attr :section, Pages.Section, required: true + attr :perspective, :string, default: "published" def top_bento(assigns) do h2 = Enum.find(assigns.section.contents, &(&1.slug == "home-top-bento-title")) @@ -117,11 +118,26 @@ defmodule OpsmaruWeb.HomeComponents do <%= @description.body %>
- <.top_left card={Enum.find(@section.cards, &(&1.position == "top-left"))} /> - <.top_right card={Enum.find(@section.cards, &(&1.position == "top-right"))} /> - <.bottom_left card={Enum.find(@section.cards, &(&1.position == "bottom-left"))} /> - <.bottom_middle card={Enum.find(@section.cards, &(&1.position == "bottom-center"))} /> - <.bottom_right card={Enum.find(@section.cards, &(&1.position == "bottom-right"))} /> + <.top_left + card={Enum.find(@section.cards, &(&1.position == "top-left"))} + perspective={@perspective} + /> + <.top_right + card={Enum.find(@section.cards, &(&1.position == "top-right"))} + perspective={@perspective} + /> + <.bottom_left + card={Enum.find(@section.cards, &(&1.position == "bottom-left"))} + perspective={@perspective} + /> + <.bottom_center + card={Enum.find(@section.cards, &(&1.position == "bottom-center"))} + perspective={@perspective} + /> + <.bottom_right + card={Enum.find(@section.cards, &(&1.position == "bottom-right"))} + perspective={@perspective} + />
@@ -186,11 +202,12 @@ defmodule OpsmaruWeb.HomeComponents do end attr :card, Pages.Card, required: true + attr :perspective, :string, default: "published" - def bottom_middle(assigns) do + def bottom_center(assigns) do ~H"""
- <.technologies :if={@card.hook == "MountTechnologies"} /> + <.technologies :if={@card.hook == "MountTechnologies"} perspective={@perspective} /> <.card_content card={@card} />
""" @@ -234,7 +251,7 @@ defmodule OpsmaruWeb.HomeComponents do end defp technologies(assigns) do - technologies = Content.list_technologies() + %{data: technologies} = Content.list_technologies(perspective: assigns.perspective) assigns = assign(assigns, :technologies, technologies) diff --git a/lib/opsmaru_web/live/home_live.ex b/lib/opsmaru_web/live/home_live.ex index 52bc9fa..0f8984c 100644 --- a/lib/opsmaru_web/live/home_live.ex +++ b/lib/opsmaru_web/live/home_live.ex @@ -41,6 +41,7 @@ defmodule OpsmaruWeb.HomeLive do attr :slides, :list, required: true attr :testimonials, :list, required: true attr :featured_posts, :list, default: [] + attr :perspective, :string, default: "published" def render(assigns) do ~H""" @@ -77,7 +78,7 @@ defmodule OpsmaruWeb.HomeLive do
- +
From dcce7098a5d938c68d90c064699d87b9300f4765 Mon Sep 17 00:00:00 2001 From: Zack Siri Date: Thu, 28 Nov 2024 11:17:20 +0700 Subject: [PATCH 7/9] test that home is rendering technologies dataset correctly --- lib/opsmaru_web/components/home_components.ex | 4 ++++ test/opsmaru_web/live/home_live_test.exs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/lib/opsmaru_web/components/home_components.ex b/lib/opsmaru_web/components/home_components.ex index b222cd6..100661f 100644 --- a/lib/opsmaru_web/components/home_components.ex +++ b/lib/opsmaru_web/components/home_components.ex @@ -145,6 +145,7 @@ defmodule OpsmaruWeb.HomeComponents do end attr :card, Pages.Card, required: true + attr :perspective, :string, default: "published" def top_left(assigns) do ~H""" @@ -164,6 +165,7 @@ defmodule OpsmaruWeb.HomeComponents do end attr :card, Pages.Card, required: true + attr :perspective, :string, default: "published" def top_right(assigns) do ~H""" @@ -183,6 +185,7 @@ defmodule OpsmaruWeb.HomeComponents do end attr :card, Pages.Card, required: true + attr :perspective, :string, default: "published" def bottom_left(assigns) do ~H""" @@ -214,6 +217,7 @@ defmodule OpsmaruWeb.HomeComponents do end attr :card, Pages.Card, required: true + attr :perspective, :string, default: "published" def bottom_right(assigns) do ~H""" diff --git a/test/opsmaru_web/live/home_live_test.exs b/test/opsmaru_web/live/home_live_test.exs index 05699ed..c6faf48 100644 --- a/test/opsmaru_web/live/home_live_test.exs +++ b/test/opsmaru_web/live/home_live_test.exs @@ -11,6 +11,8 @@ defmodule OpsmaruWeb.HomeLiveTest do assert rendered =~ "Deploy" assert rendered =~ "Monetize" + + assert rendered =~ "Cloud provider" || rendered =~ "Web framework" end end end From ac1cf6e06ac0f125ed409c7bd1b8e2b4d04876fa Mon Sep 17 00:00:00 2001 From: Zack Siri Date: Thu, 28 Nov 2024 11:31:40 +0700 Subject: [PATCH 8/9] Use sanity response struct --- lib/opsmaru/content/course/manager.ex | 5 +++- lib/opsmaru/content/logo/manager.ex | 4 ++- lib/opsmaru/content/movie/manager.ex | 4 ++- lib/opsmaru/content/navigation/manager.ex | 25 +++++++++++-------- lib/opsmaru/content/page/manager.ex | 4 ++- lib/opsmaru/content/post/manager.ex | 10 +++++--- lib/opsmaru/content/product/manager.ex | 4 ++- lib/opsmaru/content/slide/manager.ex | 4 ++- lib/opsmaru/content/technology/manager.ex | 4 ++- lib/opsmaru/content/testimonial/manager.ex | 4 ++- lib/opsmaru/courses/category/manager.ex | 4 ++- lib/opsmaru/courses/episode/manager.ex | 4 ++- lib/opsmaru/features/category/manager.ex | 4 ++- lib/opsmaru/pages/faq/manager.ex | 4 ++- lib/opsmaru/posts/category/manager.ex | 4 ++- lib/opsmaru/products/feature/manager.ex | 4 ++- lib/opsmaru/sanity.ex | 4 +++ lib/opsmaru_web/live/hooks/navigation_hook.ex | 8 +++--- 18 files changed, 71 insertions(+), 33 deletions(-) diff --git a/lib/opsmaru/content/course/manager.ex b/lib/opsmaru/content/course/manager.ex index d95b0ee..2e83938 100644 --- a/lib/opsmaru/content/course/manager.ex +++ b/lib/opsmaru/content/course/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Content.Course.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Content.Course @@ -39,6 +41,7 @@ defmodule Opsmaru.Content.Course.Manager do course = Course.parse(course) full_overview = Req.get!(course.overview).body - %{data: %{course | overview: full_overview}, perspective: perspective} + + %Response{data: %{course | overview: full_overview}, perspective: perspective} end end diff --git a/lib/opsmaru/content/logo/manager.ex b/lib/opsmaru/content/logo/manager.ex index 6ae0fee..92b7381 100644 --- a/lib/opsmaru/content/logo/manager.ex +++ b/lib/opsmaru/content/logo/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Content.Logo.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Content.Logo @@ -20,6 +22,6 @@ defmodule Opsmaru.Content.Logo.Manager do |> Sanity.result!() |> Enum.map(&Logo.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/content/movie/manager.ex b/lib/opsmaru/content/movie/manager.ex index fdbde84..cf47be5 100644 --- a/lib/opsmaru/content/movie/manager.ex +++ b/lib/opsmaru/content/movie/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Content.Movie.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Content.Movie @@ -28,6 +30,6 @@ defmodule Opsmaru.Content.Movie.Manager do |> Sanity.result!() |> Movie.parse() - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/content/navigation/manager.ex b/lib/opsmaru/content/navigation/manager.ex index ff0c30c..931bae3 100644 --- a/lib/opsmaru/content/navigation/manager.ex +++ b/lib/opsmaru/content/navigation/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Content.Navigation.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Content.Navigation @ttl :timer.hours(24) @@ -15,17 +17,18 @@ defmodule Opsmaru.Content.Navigation.Manager do } """ - @decorate cacheable(cache: Opsmaru.Cache, key: :navigations, opts: [ttl: @ttl]) - def list(_options \\ []) do - @base_query - |> Sanity.query(%{}, perspective: "published") - |> Sanity.request!(sanity_request_opts()) - |> case do - %Sanity.Response{body: %{"result" => navigations}} -> - Enum.map(navigations, &Navigation.parse/1) + @spec list(Keyword.t()) :: %{data: [%Navigation{}], perspective: String.t()} + @decorate cacheable(cache: Opsmaru.Cache, opts: [ttl: @ttl]) + def list(options \\ []) do + perspective = Keyword.get(options, :perspective, "published") + + data = + @base_query + |> Sanity.query(%{}, perspective: perspective) + |> Sanity.request!(sanity_request_opts()) + |> Sanity.result!() + |> Enum.map(&Navigation.parse/1) - error -> - error - end + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/content/page/manager.ex b/lib/opsmaru/content/page/manager.ex index 39ac92f..5203151 100644 --- a/lib/opsmaru/content/page/manager.ex +++ b/lib/opsmaru/content/page/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Content.Page.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Content.Page @@ -38,7 +40,7 @@ defmodule Opsmaru.Content.Page.Manager do page.sections |> Enum.map(&process_section/1) - %{data: %{page | sections: sections}, perspective: perspective} + %Response{data: %{page | sections: sections}, perspective: perspective} end defp process_section(section) do diff --git a/lib/opsmaru/content/post/manager.ex b/lib/opsmaru/content/post/manager.ex index 75c0de3..01711e6 100644 --- a/lib/opsmaru/content/post/manager.ex +++ b/lib/opsmaru/content/post/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Content.Post.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Content.Post @@ -48,7 +50,7 @@ defmodule Opsmaru.Content.Post.Manager do |> Sanity.result!() |> Enum.map(&Post.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end @spec featured(Keyword.t()) :: %{data: [%Post{}], perspective: String.t()} @@ -76,7 +78,7 @@ defmodule Opsmaru.Content.Post.Manager do |> Sanity.result!() |> Enum.map(&Post.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end @spec show(String.t(), Keyword.t()) :: %{data: %Post{}, perspective: String.t()} @@ -101,7 +103,7 @@ defmodule Opsmaru.Content.Post.Manager do post = Post.parse(post_params) full_content = Req.get!(post.content).body - %{data: %{post | content: full_content}, perspective: perspective} + %Response{data: %{post | content: full_content}, perspective: perspective} end @spec feed(Keyword.t()) :: [%Post{}] @@ -149,6 +151,6 @@ defmodule Opsmaru.Content.Post.Manager do |> Sanity.request!(sanity_request_opts()) |> Sanity.result!() - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/content/product/manager.ex b/lib/opsmaru/content/product/manager.ex index b72c71c..747fbc5 100644 --- a/lib/opsmaru/content/product/manager.ex +++ b/lib/opsmaru/content/product/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Content.Product.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Content.Product @@ -33,7 +35,7 @@ defmodule Opsmaru.Content.Product.Manager do %{product | stripe_product: matched_product} end) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end defp load_stripe_products do diff --git a/lib/opsmaru/content/slide/manager.ex b/lib/opsmaru/content/slide/manager.ex index 5e8322f..9f38a09 100644 --- a/lib/opsmaru/content/slide/manager.ex +++ b/lib/opsmaru/content/slide/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Content.Slide.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Content.Slide @@ -27,6 +29,6 @@ defmodule Opsmaru.Content.Slide.Manager do |> Sanity.result!() |> Enum.map(&Slide.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/content/technology/manager.ex b/lib/opsmaru/content/technology/manager.ex index 2c399c5..7dc43ad 100644 --- a/lib/opsmaru/content/technology/manager.ex +++ b/lib/opsmaru/content/technology/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Content.Technology.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Content.Technology @@ -28,6 +30,6 @@ defmodule Opsmaru.Content.Technology.Manager do |> Sanity.result!() |> Enum.map(&Technology.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/content/testimonial/manager.ex b/lib/opsmaru/content/testimonial/manager.ex index e3316f5..9b7ef76 100644 --- a/lib/opsmaru/content/testimonial/manager.ex +++ b/lib/opsmaru/content/testimonial/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Content.Testimonial.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Content.Testimonial @@ -26,6 +28,6 @@ defmodule Opsmaru.Content.Testimonial.Manager do |> Sanity.result!() |> Enum.map(&Testimonial.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/courses/category/manager.ex b/lib/opsmaru/courses/category/manager.ex index db339e4..1f040ca 100644 --- a/lib/opsmaru/courses/category/manager.ex +++ b/lib/opsmaru/courses/category/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Courses.Category.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Courses.Category @@ -40,6 +42,6 @@ defmodule Opsmaru.Courses.Category.Manager do |> Sanity.result!() |> Enum.map(&Category.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/courses/episode/manager.ex b/lib/opsmaru/courses/episode/manager.ex index b578813..ec091d9 100644 --- a/lib/opsmaru/courses/episode/manager.ex +++ b/lib/opsmaru/courses/episode/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Courses.Episode.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Courses.Episode @@ -37,6 +39,6 @@ defmodule Opsmaru.Courses.Episode.Manager do full_content = Req.get!(episode.content).body - %{data: %{episode | content: full_content}, perspective: perspective} + %Response{data: %{episode | content: full_content}, perspective: perspective} end end diff --git a/lib/opsmaru/features/category/manager.ex b/lib/opsmaru/features/category/manager.ex index faaf8b1..b085ae6 100644 --- a/lib/opsmaru/features/category/manager.ex +++ b/lib/opsmaru/features/category/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Features.Category.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Features.Category @@ -29,6 +31,6 @@ defmodule Opsmaru.Features.Category.Manager do |> Sanity.result!() |> Enum.map(&Category.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/pages/faq/manager.ex b/lib/opsmaru/pages/faq/manager.ex index f303f61..14831b9 100644 --- a/lib/opsmaru/pages/faq/manager.ex +++ b/lib/opsmaru/pages/faq/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Pages.FAQ.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Content.Page alias Opsmaru.Pages.FAQ @@ -24,6 +26,6 @@ defmodule Opsmaru.Pages.FAQ.Manager do |> Sanity.result!() |> Enum.map(&FAQ.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/posts/category/manager.ex b/lib/opsmaru/posts/category/manager.ex index 3552101..0827c54 100644 --- a/lib/opsmaru/posts/category/manager.ex +++ b/lib/opsmaru/posts/category/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Posts.Category.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Posts.Category @ttl :timer.hours(1) @@ -24,6 +26,6 @@ defmodule Opsmaru.Posts.Category.Manager do |> Sanity.result!() |> Enum.map(&Category.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/products/feature/manager.ex b/lib/opsmaru/products/feature/manager.ex index 12e7d72..d624bbe 100644 --- a/lib/opsmaru/products/feature/manager.ex +++ b/lib/opsmaru/products/feature/manager.ex @@ -2,6 +2,8 @@ defmodule Opsmaru.Products.Feature.Manager do use Nebulex.Caching import Opsmaru.Sanity + alias Opsmaru.Sanity.Response + alias Opsmaru.Cache alias Opsmaru.Products.Feature @@ -25,6 +27,6 @@ defmodule Opsmaru.Products.Feature.Manager do |> Sanity.result!() |> Enum.map(&Feature.parse/1) - %{data: data, perspective: perspective} + %Response{data: data, perspective: perspective} end end diff --git a/lib/opsmaru/sanity.ex b/lib/opsmaru/sanity.ex index acb6ca9..f720e71 100644 --- a/lib/opsmaru/sanity.ex +++ b/lib/opsmaru/sanity.ex @@ -1,4 +1,8 @@ defmodule Opsmaru.Sanity do + defmodule Response do + defstruct [:data, :perspective] + end + def sanity_request_opts do Application.get_env(:opsmaru, __MODULE__) end diff --git a/lib/opsmaru_web/live/hooks/navigation_hook.ex b/lib/opsmaru_web/live/hooks/navigation_hook.ex index 0f7811a..55633f7 100644 --- a/lib/opsmaru_web/live/hooks/navigation_hook.ex +++ b/lib/opsmaru_web/live/hooks/navigation_hook.ex @@ -4,10 +4,10 @@ defmodule OpsmaruWeb.NavigationHook do alias Opsmaru.Content - def on_mount(:main, _params, _session, socket) do - navigation = - Content.list_navigations() - |> Enum.find(&(&1.slug == "main")) + def on_mount(:main, _params, _session, %{assigns: assigns} = socket) do + %{data: navigations} = Content.list_navigations(perspective: assigns.perspective) + + navigation = Enum.find(navigations, &(&1.slug == "main")) socket = assign_new(socket, :main_nav, fn -> navigation end) From 2339c255907924d693eb12bd0ea909f93266f055 Mon Sep 17 00:00:00 2001 From: Zack Siri Date: Thu, 28 Nov 2024 12:50:00 +0700 Subject: [PATCH 9/9] Update version --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index bf0b9b8..a13e83b 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Opsmaru.MixProject do def project do [ app: :opsmaru, - version: "0.1.8", + version: "0.1.9", elixir: "~> 1.14", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod,