Skip to content

Instantly share code, notes, and snippets.

@zachdaniel
Created September 8, 2022 18:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zachdaniel/2573cb05d85c9aa9b56bea76be409112 to your computer and use it in GitHub Desktop.
Save zachdaniel/2573cb05d85c9aa9b56bea76be409112 to your computer and use it in GitHub Desktop.
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