Created
September 8, 2022 18:41
-
-
Save zachdaniel/2573cb05d85c9aa9b56bea76be409112 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Mix.Tasks.DumpHackerNews do | |
@moduledoc "Grabs the current first few pages of hackernews and saves them to a json file" | |
use Mix.Task | |
defmodule TopStory do | |
@moduledoc false | |
use Ash.Resource, | |
data_layer: AshJsonApiWrapper.DataLayer | |
json_api_wrapper do | |
finch MyApp.Finch | |
endpoints do | |
base "https://hacker-news.firebaseio.com/v0/" | |
endpoint :read do | |
path "topstories.json" | |
end | |
end | |
fields do | |
field :id do | |
path "" | |
end | |
end | |
end | |
attributes do | |
integer_primary_key :id | |
end | |
actions do | |
defaults [:read] | |
end | |
relationships do | |
has_one :story, Mix.Tasks.DumpHackerNews.Story do | |
source_field :id | |
destination_field :id | |
end | |
end | |
end | |
defmodule ShortUrl do | |
@moduledoc false | |
use Ash.Calculation | |
def calculate(records, _, _) do | |
Enum.map(records, fn record -> | |
URI.parse(record.url) | |
|> Map.put(:path, nil) | |
|> Map.put(:scheme, nil) | |
|> Map.put(:query, nil) | |
|> to_string() | |
end) | |
end | |
end | |
defmodule Story do | |
@moduledoc false | |
use Ash.Resource, | |
data_layer: AshJsonApiWrapper.DataLayer | |
calculations do | |
calculate(:short_url, :string, ShortUrl) | |
end | |
preparations do | |
prepare build(load: :short_url) | |
end | |
attributes do | |
integer_primary_key :id | |
attribute :by, :string do | |
allow_nil? false | |
end | |
attribute :score, :integer do | |
allow_nil? false | |
end | |
attribute :title, :string | |
attribute :body, :string | |
attribute :url, :string | |
end | |
json_api_wrapper do | |
finch MyApp.Finch | |
endpoints do | |
base "https://hacker-news.firebaseio.com/v0/" | |
get_endpoint :read, :id do | |
path "item/:id.json" | |
end | |
end | |
end | |
actions do | |
defaults [:read] | |
end | |
relationships do | |
has_one :user, Mix.Tasks.DumpHackerNews.User do | |
source_field :by | |
destination_field :id | |
end | |
end | |
end | |
defmodule User do | |
@moduledoc false | |
use Ash.Resource, | |
data_layer: AshJsonApiWrapper.DataLayer | |
attributes do | |
attribute :id, :string do | |
primary_key? true | |
allow_nil? false | |
end | |
end | |
json_api_wrapper do | |
finch MyApp.Finch | |
endpoints do | |
base "https://hacker-news.firebaseio.com/v0/" | |
get_endpoint :read, :id do | |
path "user/:id.json" | |
end | |
end | |
fields do | |
field :id do | |
path "id" | |
end | |
end | |
end | |
actions do | |
defaults [:read] | |
end | |
end | |
defmodule Api do | |
@moduledoc false | |
use Ash.Api | |
resources do | |
allow_unregistered? true | |
end | |
end | |
@shortdoc @moduledoc | |
def run(_argv) do | |
Mix.Task.run("compile") | |
Finch.start_link(name: MyApp.Finch) | |
stories = | |
TopStory | |
|> Ash.Query.load(story: :user) | |
|> Api.read!() | |
|> Enum.map(& &1.story) | |
user_ids = user_ids(stories) | |
stories | |
|> Enum.map(fn story -> | |
story | |
|> take_public_attrs() | |
|> Map.put(:by, user_ids[story.by]) | |
end) | |
|> write_seed_data("posts.json") | |
stories | |
|> Enum.map(& &1.user) | |
|> Enum.map(fn user -> | |
user | |
|> take_public_attrs() | |
|> Map.put(:id, user_ids[user.id]) | |
|> Map.put(:username, user.id) | |
end) | |
|> Enum.uniq_by(& &1.username) | |
|> write_seed_data("users.json") | |
end | |
defp write_seed_data(data, name) do | |
:my_app | |
|> :code.priv_dir() | |
|> Path.join("scripts/files/slacker_news/") | |
|> Path.join(name) | |
|> File.write!(Jason.encode!(data, pretty: true)) | |
end | |
defp take_public_attrs(%resource{} = record) do | |
loaded_calculations = | |
resource | |
|> Ash.Resource.Info.public_calculations() | |
|> Enum.filter(&Ash.Resource.Info.loaded?(record, &1.name)) | |
resource | |
|> Ash.Resource.Info.public_attributes() | |
|> Enum.concat(loaded_calculations) | |
|> Enum.map(& &1.name) | |
|> then(fn attrs -> | |
Map.take(record, attrs) | |
end) | |
end | |
defp user_ids(stories) do | |
Enum.reduce(stories, %{}, fn story, user_ids -> | |
if Map.get(user_ids, story.by) do | |
user_ids | |
else | |
Map.put(user_ids, story.by, Ash.UUID.generate()) | |
end | |
end) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment