improvement: upgrade to 3.0

This commit is contained in:
Zach Daniel 2024-04-02 20:24:23 -04:00
parent 6a943f22d7
commit 2221ef824d
59 changed files with 381 additions and 548 deletions

View file

@ -24,34 +24,43 @@ import { LiveSocket } from "phoenix_live_view";
import topbar from "../vendor/topbar";
function setCookie(name, value) {
document.cookie = name + "=" + value + ";path=/;" + "expires=Fri, 31 Dec 9999 23:59:59 GMT;";
document.cookie =
name + "=" + value + ";path=/;" + "expires=Fri, 31 Dec 9999 23:59:59 GMT;";
}
function getCookie(name) {
const cookie = document.cookie.split("; ").find((row) => row.startsWith(name + "="))
const cookie = document.cookie
.split("; ")
.find((row) => row.startsWith(name + "="));
if (cookie) {
return cookie.split("=")[1]
return cookie.split("=")[1];
}
}
function cookiesAreAllowed() {
return getCookie("cookieconsent_status") === "allow"
return getCookie("cookieconsent_status") === "allow";
}
function get_platform() {
// 2022 way of detecting. Note : this userAgentData feature is available only in secure contexts (HTTPS)
if (typeof navigator.userAgentData !== 'undefined' && navigator.userAgentData != null) {
if (
typeof navigator.userAgentData !== "undefined" &&
navigator.userAgentData != null
) {
return navigator.userAgentData.platform;
}
// Deprecated but still works for most of the browser
if (typeof navigator.platform !== 'undefined') {
if (typeof navigator.userAgent !== 'undefined' && /android/.test(navigator.userAgent.toLowerCase())) {
if (typeof navigator.platform !== "undefined") {
if (
typeof navigator.userAgent !== "undefined" &&
/android/.test(navigator.userAgent.toLowerCase())
) {
// android device's navigator.platform is often set as 'linux', so let's use userAgent for them
return 'android';
return "android";
}
return navigator.platform;
}
return 'unknown';
return "unknown";
}
let platform = get_platform();
@ -110,13 +119,15 @@ let scrolled = false;
Hooks.RightNav = {
mounted() {
this.intersectionObserver =
new IntersectionObserver((entries) =>
this.onScrollChange(entries), { rootMargin: "-10% 0px -89% 0px" }
);
this.intersectionObserver = new IntersectionObserver(
(entries) => this.onScrollChange(entries),
{ rootMargin: "-10% 0px -89% 0px" },
);
this.observeElements()
window.addEventListener("hashchange", (event) => { this.handleHashChange(); });
this.observeElements();
window.addEventListener("hashchange", (event) => {
this.handleHashChange();
});
},
updated() {
this.intersectionObserver.disconnect();
@ -129,7 +140,9 @@ Hooks.RightNav = {
},
onScrollChange(entries) {
// Wait for scrolling from initial page load to complete
if (!scrolled) { return; }
if (!scrolled) {
return;
}
for (entry of entries) {
if (entry.isIntersecting) {
@ -139,19 +152,21 @@ Hooks.RightNav = {
},
handleHashChange() {
if (window.location.hash) {
this.setAriaCurrent(window.location.hash.substring(1))
this.setAriaCurrent(window.location.hash.substring(1));
// Disable the insersection observer for 1s while the browser
// scrolls the selected element to the top.
scrolled = false;
setTimeout(() => { scrolled = true }, 1000);
setTimeout(() => {
scrolled = true;
}, 1000);
}
},
setAriaCurrent(id) {
const el = document.getElementById("right-nav-" + id);
if (el) {
for (elem of document.querySelectorAll('#right-nav a[aria-current]')) {
elem.removeAttribute('aria-current');
for (elem of document.querySelectorAll("#right-nav a[aria-current]")) {
elem.removeAttribute("aria-current");
}
el.setAttribute("aria-current", "true");
}
@ -201,9 +216,9 @@ window.addEventListener("phx:page-loading-start", ({ detail }) => {
scrolled = false;
// close mobile sidebar on navigation
mobileSideBar = document.getElementById("mobile-sidebar-hide")
mobileSideBar = document.getElementById("mobile-sidebar-hide");
if (mobileSideBar) {
mobileSideBar.click()
mobileSideBar.click();
}
if (!topBarScheduled) {
@ -219,13 +234,17 @@ window.addEventListener("phx:page-loading-stop", ({ detail }) => {
if (detail.kind === "initial" && window.location.hash) {
scrollEl = document.getElementById(window.location.hash.substring(1));
} else if (detail.kind == "patch" && !window.location.hash) {
scrollEl = document.querySelector("#docs-window .nav-anchor") || document.querySelector("#docs-window h1");
scrollEl =
document.querySelector("#docs-window .nav-anchor") ||
document.querySelector("#docs-window h1");
}
if (scrollEl) {
Hooks.RightNav.setAriaCurrent(scrollEl.id);
// Not using scroll polyfill here - doesn't respect scroll-padding-top CSS
scrollEl.scrollIntoView({ block: 'start' })
setTimeout(() => { scrolled = true; }, 1000);
scrollEl.scrollIntoView({ block: "start" });
setTimeout(() => {
scrolled = true;
}, 1000);
} else {
scrolled = true;
}
@ -243,13 +262,6 @@ window.addEventListener("phx:js:scroll-to", (e) => {
});
});
window.addEventListener("phx:selected-types", (e) => {
if (cookiesAreAllowed()) {
const cookie = e.detail.types.join(",");
setCookies("selected_types", cookie)
}
});
window.addEventListener("keydown", (event) => {
if ((event.metaKey || event.ctrlKey) && event.key === "k") {
document.getElementById("search-button").click();
@ -258,9 +270,11 @@ window.addEventListener("keydown", (event) => {
});
window.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
const closeSearchVersions = document.getElementById("close-search-versions");
const closeSearchVersions = document.getElementById(
"close-search-versions",
);
if (closeSearchVersions && closeSearchVersions.offsetParent !== null) {
closeSearchVersions.click()
closeSearchVersions.click();
} else {
document.getElementById("close-search").click();
}
@ -288,7 +302,7 @@ liveSocket.connect();
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket;
window.addEventListener("load", function() {
window.addEventListener("load", function () {
window.cookieconsent.initialise({
content: {
message:

View file

@ -25,7 +25,7 @@ config :ash_appsignal,
config :appsignal, :config, revision: "test-4"
config :ash_hq,
ash_apis: [
ash_domains: [
AshHq.Accounts,
AshHq.Blog,
AshHq.Docs,
@ -40,7 +40,6 @@ config :ash_hq, AshHq.Repo,
config :spark, :formatter,
remove_parens?: true,
"Ash.Registry": [],
"Ash.Resource": [
type: Ash.Resource,
section_order: [

View file

@ -2,13 +2,10 @@ defmodule AshHq.Accounts do
@moduledoc """
Handles user and user token related operations/state
"""
use Ash.Api, otp_app: :ash_hq
authorization do
authorize :by_default
end
use Ash.Domain, otp_app: :ash_hq
resources do
registry AshHq.Accounts.Registry
resource AshHq.Accounts.User
resource AshHq.Accounts.UserToken
end
end

View file

@ -1,11 +0,0 @@
defmodule AshHq.Accounts.Registry do
@moduledoc false
use Ash.Registry,
extensions: [Ash.Registry.ResourceValidations]
entries do
entry AshHq.Accounts.User
entry AshHq.Accounts.UserToken
end
end

View file

@ -1,26 +0,0 @@
defmodule AshHq.Accounts.User.Changes.RemoveAllTokens do
@moduledoc """
Removes all tokens for a given user.
Since Ash does not yet support bulk actions, this goes straight to the data layer.
"""
use Ash.Resource.Change
require Ash.Query
def change(changeset, _opts, _context) do
Ash.Changeset.after_action(
changeset,
fn _changeset, user ->
{:ok, query} =
AshHq.Accounts.UserToken
|> Ash.Query.filter(user_id == ^user.id)
|> Ash.Query.data_layer_query()
AshHq.Repo.delete_all(query)
{:ok, user}
end,
prepend?: true
)
end
end

View file

@ -6,25 +6,5 @@ defmodule AshHq.Accounts.User.Policies do
policy action(:read) do
authorize_if(expr(id == ^actor(:id)))
end
policy action(:update_email) do
description("A logged in user can update their email")
authorize_if(expr(id == ^actor(:id)))
end
policy action(:resend_confirmation_instructions) do
description("A logged in user can request an email confirmation")
authorize_if(expr(id == ^actor(:id)))
end
policy action(:change_password) do
description("A logged in user can reset their password")
authorize_if(expr(id == ^actor(:id)))
end
policy action(:update_merch_settings) do
description("A logged in user can update their merch settings")
authorize_if(expr(id == ^actor(:id)))
end
end
end

View file

@ -2,6 +2,7 @@ defmodule AshHq.Accounts.User do
@moduledoc false
use Ash.Resource,
domain: AshHq.Accounts,
data_layer: AshPostgres.DataLayer,
fragments: [AshHq.Accounts.User.Policies]
@ -11,130 +12,6 @@ defmodule AshHq.Accounts.User do
actions do
defaults [:read]
# create :register_with_github do
# argument :user_info, :map do
# allow_nil? false
# end
# argument :oauth_tokens, :map do
# allow_nil? false
# end
# change fn changeset, _ ->
# user_info = Ash.Changeset.get_argument(changeset, :user_info)
# changeset =
# if user_info["email_verified"] do
# Ash.Changeset.force_change_attribute(
# changeset,
# :confirmed_at,
# Ash.Changeset.get_attribute(changeset, :confirmed_at) || DateTime.utc_now()
# )
# else
# changeset
# end
# changeset
# |> Ash.Changeset.change_attribute(:email, Map.get(user_info, "email"))
# |> Ash.Changeset.change_attribute(:github_info, user_info)
# end
# change AshAuthentication.GenerateTokenChange
# upsert? true
# upsert_identity :unique_email
# end
# update :change_password do
# accept []
# argument :current_password, :string do
# sensitive? true
# allow_nil? false
# end
# argument :password, :string do
# sensitive? true
# allow_nil? false
# end
# argument :password_confirmation, :string do
# sensitive? true
# allow_nil? false
# end
# change set_context(%{strategy_name: :password})
# validate confirm(:password, :password_confirmation)
# validate {AshAuthentication.Strategy.Password.PasswordValidation,
# strategy_name: :password, password_argument: :current_password} do
# only_when_valid? true
# before_action? true
# end
# change AshAuthentication.Strategy.Password.HashPasswordChange
# end
# update :update_email do
# accept [:email]
# argument :current_password, :string do
# sensitive? true
# allow_nil? false
# end
# change set_context(%{strategy_name: :password})
# validate {AshAuthentication.Strategy.Password.PasswordValidation,
# password_argument: :current_password} do
# only_when_valid? true
# before_action? true
# end
# end
# update :resend_confirmation_instructions do
# accept []
# change fn changeset, _context ->
# Ash.Changeset.before_action(changeset, fn changeset ->
# case AshHq.Accounts.UserToken.email_token_for_user(changeset.data.id,
# authorize?: false
# ) do
# {:ok, %{extra_data: %{"email" => changing_to}}} ->
# temp_changeset = %{
# changeset
# | attributes: Map.put(changeset.attributes, :email, changing_to)
# }
# strategy = AshAuthentication.Info.strategy!(changeset.resource, :confirm)
# {:ok, token} =
# AshAuthentication.AddOn.Confirmation.confirmation_token(
# strategy,
# temp_changeset,
# changeset.data
# )
# AshHq.Accounts.User.Senders.SendConfirmationEmail.send(changeset.data, token, [])
# changeset
# _ ->
# Ash.Changeset.add_error(changeset, "Could not determine what email to use")
# end
# end)
# end
# end
# update :update_merch_settings do
# argument :address, :string
# argument :name, :string
# accept [:shirt_size]
# change set_attribute(:encrypted_address, arg(:address))
# change set_attribute(:encrypted_name, arg(:name))
# end
end
attributes do
@ -146,7 +23,7 @@ defmodule AshHq.Accounts.User do
max_length: 160
]
attribute :hashed_password, :string, private?: true, sensitive?: true
attribute :hashed_password, :string, sensitive?: true
attribute :encrypted_name, :string
attribute :encrypted_address, :string
@ -160,7 +37,6 @@ defmodule AshHq.Accounts.User do
relationships do
has_one :token, AshHq.Accounts.UserToken do
destination_attribute :user_id
private? true
end
end
@ -169,12 +45,6 @@ defmodule AshHq.Accounts.User do
repo AshHq.Repo
end
code_interface do
define_for AshHq.Accounts
define :resend_confirmation_instructions
define :register_with_password, args: [:email, :password, :password_confirmation]
end
resource do
description """
Represents the user of a system.
@ -188,9 +58,6 @@ defmodule AshHq.Accounts.User do
end
changes do
change AshHq.Accounts.User.Changes.RemoveAllTokens,
where: [action_is(:password_reset_with_password)]
change {AshHq.Changes.Encrypt, fields: [:encrypted_address, :encrypted_name]}
end

View file

@ -2,27 +2,16 @@ defmodule AshHq.Accounts.UserToken do
@moduledoc false
use Ash.Resource,
domain: AshHq.Accounts,
data_layer: AshPostgres.DataLayer,
authorizers: [Ash.Policy.Authorizer]
actions do
defaults [:read, :destroy]
read :email_token_for_user do
get? true
argument :user_id, :uuid do
allow_nil? false
end
prepare build(sort: [updated_at: :desc], limit: 1)
filter expr(purpose == "confirm" and not is_nil(extra_data[:email]))
end
defaults [:read]
end
token do
api AshHq.Accounts
attributes do
uuid_primary_key :id
end
relationships do
@ -32,8 +21,7 @@ defmodule AshHq.Accounts.UserToken do
policies do
policy always() do
description """
There are currently no usages of user tokens resource that should be publicly
accessible, they should all be using authorize?: false.
There are currently no usages of user tokens resource that should be publicly accessible.
"""
forbid_if always()
@ -49,30 +37,9 @@ defmodule AshHq.Accounts.UserToken do
end
end
code_interface do
define_for AshHq.Accounts
define :destroy
define :email_token_for_user, args: [:user_id]
end
resource do
description """
Represents a token allowing a user to log in, reset their password, or confirm their email.
"""
end
changes do
change fn changeset, _ ->
case changeset.context[:ash_authentication][:user] do
nil ->
changeset
user ->
Ash.Changeset.manage_relationship(changeset, :user, user,
type: :append_and_remove
)
end
end,
on: [:create]
end
end

View file

@ -1,52 +0,0 @@
defmodule AshHq.ApiHelpers do
@moduledoc false
defmacro __using__(_) do
quote do
def stream(query, opts \\ []) do
api = __MODULE__
query = Ash.Query.to_query(query)
query =
if query.action do
query
else
Ash.Query.for_read(
query,
Ash.Resource.Info.primary_action!(query.resource, :read).name
)
end
Stream.resource(
fn -> nil end,
fn
false ->
{:halt, nil}
after_keyset ->
if is_nil(query.action.pagination) || !query.action.pagination.keyset? do
raise "Keyset pagination must be enabled"
end
keyset = if after_keyset != nil, do: [after: after_keyset], else: []
page_opts = Keyword.merge([limit: 100], keyset)
opts =
[
page: page_opts
]
|> Keyword.merge(opts)
case api.read!(query, opts) do
%{more?: true, results: results} ->
{results, List.last(results).__metadata__.keyset}
%{results: results} ->
{results, false}
end
end,
& &1
)
end
end
end
end

View file

@ -7,6 +7,7 @@ defmodule AshHq.Application do
@impl true
def start(_type, _args) do
:erlang.system_flag(:backtrace_depth, 1000)
Appsignal.Phoenix.LiveView.attach()
# topologies = Application.get_env(:libcluster, :topologies) || []
@ -48,8 +49,8 @@ defmodule AshHq.Application do
end
defp oban_worker do
apis = Application.fetch_env!(:ash_hq, :ash_apis)
domains = Application.fetch_env!(:ash_hq, :ash_domains)
config = Application.fetch_env!(:ash_hq, Oban)
{Oban, AshOban.config(apis, config)}
{Oban, AshOban.config(domains, config)}
end
end

View file

@ -1,7 +1,7 @@
defmodule AshHq.Blog do
@moduledoc "An api for interacting with the blog"
use Ash.Api,
extensions: [AshAdmin.Api]
@moduledoc "A domain for interacting with the blog"
use Ash.Domain,
extensions: [AshAdmin.Domain]
admin do
show? true
@ -9,6 +9,7 @@ defmodule AshHq.Blog do
end
resources do
registry AshHq.Blog.Registry
resource AshHq.Blog.Post
resource AshHq.Blog.Tag
end
end

View file

@ -1,10 +0,0 @@
defmodule AshHq.Blog.Registry do
@moduledoc "The resources used in the blog"
use Ash.Registry,
extensions: [Ash.Registry.ResourceValidations]
entries do
entry AshHq.Blog.Post
entry AshHq.Blog.Tag
end
end

View file

@ -1,6 +1,7 @@
defmodule AshHq.Blog.Post do
@moduledoc "A blog post. Uses the AshBlog data layer and therefore is static"
use Ash.Resource,
domain: AshHq.Blog,
otp_app: :ash_hq,
data_layer: AshBlog.DataLayer,
extensions: [AshHq.Docs.Extensions.RenderMarkdown, AshAdmin.Resource]
@ -22,6 +23,7 @@ defmodule AshHq.Blog.Post do
end
actions do
default_accept :*
defaults [:create, :read, :update]
read :published do
@ -52,11 +54,13 @@ defmodule AshHq.Blog.Post do
uuid_primary_key :id
attribute :tag_line, :string do
public? true
allow_nil? false
constraints max_length: 250
end
attribute :tag_names, {:array, :ci_string} do
public? true
constraints items: [
match: ~r/^[a-zA-Z]*$/,
casing: :lower
@ -64,10 +68,12 @@ defmodule AshHq.Blog.Post do
end
attribute :author, :string do
public? true
allow_nil? false
end
attribute :body_html, :string do
public? true
writable? false
end
@ -76,6 +82,7 @@ defmodule AshHq.Blog.Post do
relationships do
has_many :tags, AshHq.Blog.Tag do
public? true
manual fn posts, %{query: query} ->
all_tags = Enum.flat_map(posts, &(&1.tag_names || []))
@ -83,7 +90,7 @@ defmodule AshHq.Blog.Post do
query
|> Ash.Query.unset([:limit, :offset])
|> Ash.Query.filter(name in ^all_tags)
|> AshHq.Blog.read!()
|> Ash.read!()
{:ok,
Map.new(posts, fn post ->
@ -94,7 +101,6 @@ defmodule AshHq.Blog.Post do
end
code_interface do
define_for AshHq.Blog
define :published
define :by_slug, args: [:slug]
end

View file

@ -1,6 +1,7 @@
defmodule AshHq.Blog.Tag do
@moduledoc "A tag that can be applied to a post. Currently uses CSV data layer and therefore is static"
use Ash.Resource,
domain: AshHq.Blog,
data_layer: AshCsv.DataLayer
csv do
@ -11,6 +12,7 @@ defmodule AshHq.Blog.Tag do
end
actions do
default_accept :*
defaults [:create, :read, :update, :destroy]
create :upsert do
@ -21,6 +23,7 @@ defmodule AshHq.Blog.Tag do
attributes do
attribute :name, :ci_string do
public? true
allow_nil? false
primary_key? true
constraints casing: :lower
@ -28,7 +31,6 @@ defmodule AshHq.Blog.Tag do
end
code_interface do
define_for AshHq.Blog
define :upsert, args: [:name]
define :read
define :destroy

View file

@ -1,6 +1,6 @@
defmodule AshHq.Calculations.Decrypt do
@moduledoc "Decrypts a given value on demand"
use Ash.Calculation
use Ash.Resource.Calculation
def calculate(records, opts, _) do
{:ok,
@ -19,10 +19,6 @@ defmodule AshHq.Calculations.Decrypt do
end)}
end
def select(_, opts, _) do
[opts[:field]]
end
def load(_, opts, _) do
[opts[:field]]
end

View file

@ -1,6 +1,6 @@
defmodule AshHq.Discord do
@moduledoc "Discord api import & interactions"
use Ash.Api
use Ash.Domain
resources do
resource AshHq.Discord.Attachment

View file

@ -1,24 +1,27 @@
defmodule AshHq.Discord.Attachment do
@moduledoc "A discord attachment on a message"
use Ash.Resource,
domain: AshHq.Discord,
data_layer: AshPostgres.DataLayer
actions do
default_accept :*
defaults [:create, :read, :update, :destroy]
end
attributes do
integer_primary_key :id, generated?: false, writable?: true
attribute :filename, :string
attribute :size, :integer
attribute :url, :string
attribute :proxy_url, :string
attribute :height, :integer
attribute :width, :integer
attribute :filename, :string, public?: true
attribute :size, :integer, public?: true
attribute :url, :string, public?: true
attribute :proxy_url, :string, public?: true
attribute :height, :integer, public?: true
attribute :width, :integer, public?: true
end
relationships do
belongs_to :message, AshHq.Discord.Message do
public? true
allow_nil? false
attribute_type :integer
end

View file

@ -4,9 +4,11 @@ defmodule AshHq.Discord.Channel do
"""
use Ash.Resource,
domain: AshHq.Discord,
data_layer: AshPostgres.DataLayer
actions do
default_accept :*
defaults [:create, :read, :update, :destroy]
create :upsert do
@ -18,16 +20,20 @@ defmodule AshHq.Discord.Channel do
integer_primary_key :id, writable?: true, generated?: false
attribute :name, :string do
public? true
allow_nil? false
end
attribute :order, :integer do
public? true
allow_nil? false
end
end
relationships do
has_many :threads, AshHq.Discord.Thread
has_many :threads, AshHq.Discord.Thread do
public? true
end
end
postgres do
@ -36,7 +42,6 @@ defmodule AshHq.Discord.Channel do
end
code_interface do
define_for AshHq.Discord
define :read
define :upsert
end

View file

@ -3,6 +3,7 @@ defmodule AshHq.Discord.Message do
Discord messages synchronized by the discord bot
"""
use Ash.Resource,
domain: AshHq.Discord,
data_layer: AshPostgres.DataLayer,
extensions: [
AshHq.Docs.Extensions.RenderMarkdown,
@ -10,6 +11,8 @@ defmodule AshHq.Discord.Message do
]
actions do
default_accept :*
defaults [:read, :destroy]
create :create do
@ -25,6 +28,7 @@ defmodule AshHq.Discord.Message do
end
update :update do
require_atomic? false
primary? true
argument :attachments, {:array, :map}
argument :reactions, {:array, :map}
@ -59,25 +63,37 @@ defmodule AshHq.Discord.Message do
integer_primary_key :id, generated?: false, writable?: true
attribute :author, :string do
public? true
allow_nil? false
end
attribute :content, :string
attribute :content_html, :string
attribute :content, :string do
public? true
end
attribute :content_html, :string do
public? true
end
attribute :timestamp, :utc_datetime do
public? true
allow_nil? false
end
end
relationships do
belongs_to :thread, AshHq.Discord.Thread do
public? true
attribute_type :integer
allow_nil? false
end
has_many :attachments, AshHq.Discord.Attachment
has_many :reactions, AshHq.Discord.Reaction
has_many :attachments, AshHq.Discord.Attachment do
public? true
end
has_many :reactions, AshHq.Discord.Reaction do
public? true
end
end
postgres do

View file

@ -3,9 +3,11 @@ defmodule AshHq.Discord.Reaction do
Reactions store emoji reaction counts.
"""
use Ash.Resource,
domain: AshHq.Discord,
data_layer: AshPostgres.DataLayer
actions do
default_accept :*
defaults [:create, :read, :update, :destroy]
end
@ -13,16 +15,19 @@ defmodule AshHq.Discord.Reaction do
uuid_primary_key :id
attribute :count, :integer do
public? true
allow_nil? false
end
attribute :emoji, :string do
public? true
allow_nil? false
end
end
relationships do
belongs_to :message, AshHq.Discord.Message do
public? true
attribute_type :integer
allow_nil? false
end

View file

@ -1,9 +1,11 @@
defmodule AshHq.Discord.Tag do
@moduledoc "A tag that can be applied to a post. Currently uses CSV data layer and therefore is static"
use Ash.Resource,
domain: AshHq.Discord,
data_layer: AshPostgres.DataLayer
actions do
default_accept :*
defaults [:create, :read, :update, :destroy]
create :upsert do
@ -16,14 +18,15 @@ defmodule AshHq.Discord.Tag do
integer_primary_key :id, generated?: false, writable?: true
attribute :name, :ci_string do
public? true
allow_nil? false
end
end
relationships do
belongs_to :channel, AshHq.Discord.Channel do
public? true
attribute_type :integer
attribute_writable? true
end
end
@ -33,7 +36,6 @@ defmodule AshHq.Discord.Tag do
end
code_interface do
define_for AshHq.Discord
define :upsert, args: [:channel_id, :id, :name]
define :read
define :destroy

View file

@ -4,11 +4,13 @@ defmodule AshHq.Discord.Thread do
"""
use Ash.Resource,
domain: AshHq.Discord,
data_layer: AshPostgres.DataLayer
import Ecto.Query
actions do
default_accept :*
defaults [:create, :read, :update, :destroy]
read :feed do
@ -64,31 +66,39 @@ defmodule AshHq.Discord.Thread do
attributes do
integer_primary_key :id, generated?: false, writable?: true
attribute :type, :integer
attribute :type, :integer do
public? true
end
attribute :name, :string do
public? true
allow_nil? false
end
attribute :author, :string do
public? true
allow_nil? false
end
attribute :create_timestamp, :utc_datetime do
public? true
allow_nil? false
end
end
relationships do
has_many :messages, AshHq.Discord.Message
has_many :messages, AshHq.Discord.Message do
public? true
end
belongs_to :channel, AshHq.Discord.Channel do
public? true
attribute_type :integer
allow_nil? false
attribute_writable? true
end
many_to_many :tags, AshHq.Discord.Tag do
public? true
through AshHq.Discord.ThreadTag
source_attribute_on_join_resource :thread_id
destination_attribute_on_join_resource :tag_id
@ -101,7 +111,6 @@ defmodule AshHq.Discord.Thread do
end
code_interface do
define_for AshHq.Discord
define :upsert
define :by_id, action: :read, get_by: [:id]
define :feed, args: [:channel]

View file

@ -1,9 +1,11 @@
defmodule AshHq.Discord.ThreadTag do
@moduledoc "Joins a thread to a tag"
use Ash.Resource,
domain: AshHq.Discord,
data_layer: AshPostgres.DataLayer
actions do
default_accept :*
defaults [:read, :destroy]
create :tag do
@ -13,16 +15,16 @@ defmodule AshHq.Discord.ThreadTag do
relationships do
belongs_to :thread, AshHq.Discord.Thread do
public? true
primary_key? true
allow_nil? false
attribute_writable? true
attribute_type :integer
end
belongs_to :tag, AshHq.Discord.Tag do
public? true
primary_key? true
allow_nil? false
attribute_writable? true
attribute_type :integer
end
end
@ -33,7 +35,6 @@ defmodule AshHq.Discord.ThreadTag do
end
code_interface do
define_for AshHq.Discord
define :tag, args: [:thread_id, :tag_id]
end
end

View file

@ -2,14 +2,12 @@ defmodule AshHq.Docs do
@moduledoc """
Handles documentation data.
"""
use Ash.Api,
use Ash.Domain,
extensions: [
AshGraphql.Api,
AshAdmin.Api
AshGraphql.Domain,
AshAdmin.Domain
]
use AshHq.ApiHelpers
admin do
show? true
default_resource_page :primary_read
@ -20,6 +18,14 @@ defmodule AshHq.Docs do
end
resources do
registry AshHq.Docs.Registry
resource AshHq.Docs.Dsl
resource AshHq.Docs.Extension
resource AshHq.Docs.Function
resource AshHq.Docs.Guide
resource AshHq.Docs.Library
resource AshHq.Docs.LibraryVersion
resource AshHq.Docs.MixTask
resource AshHq.Docs.Module
resource AshHq.Docs.Option
end
end

View file

@ -30,7 +30,7 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown.Changes.RenderMarkdown do
Ash.Changeset.get_attribute(changeset, :name)
changeset.resource == AshHq.Docs.Function ->
AshHq.Docs.get!(
Ash.get!(
AshHq.Docs.Module,
Ash.Changeset.get_attribute(changeset, :module_id)
).name
@ -49,7 +49,7 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown.Changes.RenderMarkdown do
AshHq.Docs.Library
|> Ash.Query.select(:name)
|> Ash.Query.filter(versions.id == ^library_version_id)
|> AshHq.Docs.read_one!()
|> Ash.read_one!()
|> Map.get(:name)
end

View file

@ -45,7 +45,6 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
Ash.Resource.Dsl,
[:attributes],
:attribute,
private?: true,
name: config.sanitized_name_attribute,
type: :string,
allow_nil?: false

View file

@ -3,8 +3,8 @@ defmodule AshHq.Docs.Extensions.Search.Types do
A static list of all search types that currently exist
"""
@search_types [AshHq.Docs.Registry]
|> Enum.flat_map(&Ash.Registry.Info.entries/1)
@search_types [AshHq.Docs]
|> Enum.flat_map(&Ash.Domain.Info.resources/1)
|> Enum.filter(&(AshHq.Docs.Extensions.Search in Spark.extensions(&1)))
|> Enum.map(&AshHq.Docs.Extensions.Search.type/1)
|> Enum.uniq()

View file

@ -73,7 +73,7 @@ defmodule AshHq.Docs.Indexer do
resource
|> Ash.Query.filter(id in ^ids)
|> Ash.Query.load(AshHq.Docs.Extensions.Search.load_for_search(resource))
|> AshHq.Docs.read!()
|> Ash.read!()
|> Enum.map(fn item ->
Ash.Resource.put_metadata(item, :search_score, scores[item.id])
end)
@ -125,7 +125,7 @@ defmodule AshHq.Docs.Indexer do
defp dsls do
AshHq.Docs.Dsl
|> Ash.Query.load([:library_name, :extension_module])
|> AshHq.Docs.stream!()
|> Ash.stream!()
|> Stream.map(fn dsl ->
%{
"id" => id("dsl", dsl.id),
@ -140,7 +140,7 @@ defmodule AshHq.Docs.Indexer do
defp guides do
AshHq.Docs.Guide
|> Ash.Query.load(library_version: :library)
|> AshHq.Docs.stream!()
|> Ash.stream!()
|> Stream.map(fn guide ->
%{
"id" => id("guide", guide.id),
@ -154,7 +154,7 @@ defmodule AshHq.Docs.Indexer do
defp options do
AshHq.Docs.Option
|> Ash.Query.load([:library_name, :extension_module])
|> AshHq.Docs.stream!()
|> Ash.stream!()
|> Stream.map(fn option ->
%{
"id" => id("option", option.id),
@ -169,7 +169,7 @@ defmodule AshHq.Docs.Indexer do
defp modules do
AshHq.Docs.Module
|> Ash.Query.load([:library_name])
|> AshHq.Docs.stream!()
|> Ash.stream!()
|> Stream.map(fn module ->
%{
"id" => id("module", module.id),
@ -184,7 +184,7 @@ defmodule AshHq.Docs.Indexer do
defp mix_tasks do
AshHq.Docs.MixTask
|> Ash.Query.load([:library_name])
|> AshHq.Docs.stream!()
|> Ash.stream!()
|> Stream.map(fn mix_task ->
%{
"id" => id("mix_task", mix_task.id),
@ -199,7 +199,7 @@ defmodule AshHq.Docs.Indexer do
defp functions do
AshHq.Docs.Function
|> Ash.Query.load([:library_name, :call_name])
|> AshHq.Docs.stream!()
|> Ash.stream!()
|> Stream.map(fn function ->
%{
"id" => id("function", function.id),

View file

@ -1,17 +0,0 @@
defmodule AshHq.Docs.Registry do
@moduledoc false
use Ash.Registry,
extensions: [Ash.Registry.ResourceValidations]
entries do
entry AshHq.Docs.Dsl
entry AshHq.Docs.Extension
entry AshHq.Docs.Function
entry AshHq.Docs.Guide
entry AshHq.Docs.Library
entry AshHq.Docs.LibraryVersion
entry AshHq.Docs.MixTask
entry AshHq.Docs.Module
entry AshHq.Docs.Option
end
end

View file

@ -2,10 +2,12 @@ defmodule AshHq.Docs.Dsl do
@moduledoc false
use Ash.Resource,
domain: AshHq.Docs,
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
actions do
default_accept :*
defaults [:update, :destroy]
read :read do
@ -57,39 +59,61 @@ defmodule AshHq.Docs.Dsl do
uuid_primary_key :id
attribute :name, :string do
public? true
allow_nil? false
end
attribute :requires_extension, :string
attribute :requires_extension, :string do
public? true
end
attribute :doc, :string do
public? true
allow_nil? false
constraints trim?: false, allow_empty?: true
default ""
end
attribute :doc_html, :string do
public? true
constraints trim?: false, allow_empty?: true
writable? false
end
attribute :imports, {:array, :string} do
public? true
default []
end
attribute :examples, {:array, :string}
attribute :args, {:array, :string}
attribute :examples, {:array, :string} do
public? true
end
attribute :args, {:array, :string} do
public? true
end
attribute :optional_args, {:array, :string} do
public? true
default []
end
attribute :arg_defaults, :map
attribute :path, {:array, :string}
attribute :recursive_as, :string
attribute :order, :integer, allow_nil?: false
attribute :arg_defaults, :map do
public? true
end
attribute :path, {:array, :string} do
public? true
end
attribute :recursive_as, :string do
public? true
end
attribute :order, :integer do
public? true
allow_nil? false
end
attribute :type, :atom do
public? true
allow_nil? false
constraints one_of: [:entity, :section]
end
@ -99,16 +123,24 @@ defmodule AshHq.Docs.Dsl do
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
public? true
allow_nil? true
end
belongs_to :extension, AshHq.Docs.Extension do
public? true
allow_nil? true
end
belongs_to :dsl, __MODULE__
has_many :options, AshHq.Docs.Option
has_many :dsls, __MODULE__
belongs_to :dsl, __MODULE__ do
public? true
end
has_many :options, AshHq.Docs.Option do
public? true
end
has_many :dsls, __MODULE__ do
public? true
end
end
postgres do
@ -124,7 +156,6 @@ defmodule AshHq.Docs.Dsl do
end
code_interface do
define_for AshHq.Docs
define :read
end

View file

@ -2,9 +2,11 @@ defmodule AshHq.Docs.Extension do
@moduledoc false
use Ash.Resource,
domain: AshHq.Docs,
data_layer: AshPostgres.DataLayer
actions do
default_accept :*
defaults [:update, :destroy]
read :read do
@ -33,18 +35,25 @@ defmodule AshHq.Docs.Extension do
attributes do
uuid_primary_key :id
attribute :module, :string
attribute :module, :string do
public? true
end
timestamps()
end
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
public? true
allow_nil? true
end
has_many :dsls, AshHq.Docs.Dsl
has_many :options, AshHq.Docs.Option
has_many :dsls, AshHq.Docs.Dsl do
public? true
end
has_many :options, AshHq.Docs.Option do
public? true
end
end
postgres do
@ -57,7 +66,6 @@ defmodule AshHq.Docs.Extension do
end
code_interface do
define_for AshHq.Docs
define :destroy
end

View file

@ -2,10 +2,12 @@ defmodule AshHq.Docs.Function do
@moduledoc false
use Ash.Resource,
domain: AshHq.Docs,
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
actions do
default_accept :*
defaults [:update, :destroy]
read :read do
@ -51,45 +53,58 @@ defmodule AshHq.Docs.Function do
uuid_primary_key :id
attribute :name, :string do
public? true
allow_nil? false
end
attribute :file, :string
attribute :line, :integer
attribute :file, :string do
public? true
end
attribute :line, :integer do
public? true
end
attribute :arity, :integer do
public? true
allow_nil? false
end
attribute :type, :atom do
public? true
constraints one_of: [:function, :macro, :callback, :type]
allow_nil? false
end
attribute :heads, {:array, :string} do
public? true
default []
end
attribute :heads_html, {:array, :string} do
public? true
default []
end
attribute :doc, :string do
public? true
allow_nil? false
constraints trim?: false, allow_empty?: true
default ""
end
attribute :doc_html, :string do
public? true
constraints trim?: false, allow_empty?: true
writable? false
end
attribute :order, :integer do
public? true
allow_nil? false
end
attribute :deprecated, :string do
public? true
allow_nil? true
end
@ -98,10 +113,12 @@ defmodule AshHq.Docs.Function do
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
public? true
allow_nil? true
end
belongs_to :module, AshHq.Docs.Module do
public? true
allow_nil? true
end
end
@ -115,9 +132,6 @@ defmodule AshHq.Docs.Function do
end
end
code_interface do
define_for AshHq.Docs
end
resource do
description "A function in a module exposed by an Ash library"

View file

@ -1,6 +1,7 @@
defmodule AshHq.Docs.Guide do
@moduledoc false
use Ash.Resource,
domain: AshHq.Docs,
data_layer: AshPostgres.DataLayer,
extensions: [
AshHq.Docs.Extensions.Search,
@ -10,6 +11,7 @@ defmodule AshHq.Docs.Guide do
]
actions do
default_accept :*
defaults [:create, :update, :destroy]
read :read do
@ -66,39 +68,47 @@ defmodule AshHq.Docs.Guide do
uuid_primary_key :id
attribute :order, :integer do
public? true
allow_nil? false
end
attribute :name, :string do
public? true
allow_nil? false
end
attribute :text, :string do
public? true
allow_nil? false
constraints trim?: false, allow_empty?: true
default ""
end
attribute :text_html, :string do
public? true
constraints trim?: false, allow_empty?: true
writable? false
end
attribute :category, :string do
public? true
default "Topics"
allow_nil? false
end
attribute :route, :string do
public? true
allow_nil? false
end
attribute :sanitized_route, :string do
public? true
allow_nil? false
writable? false
end
attribute :default, :boolean do
public? true
default false
allow_nil? false
end
@ -121,9 +131,6 @@ defmodule AshHq.Docs.Guide do
end
end
code_interface do
define_for AshHq.Docs
end
resource do
description "Represents a markdown guide exposed by a library"

View file

@ -75,7 +75,7 @@ defmodule AshHq.Docs.Library.Actions.Import do
Logger.info("Starting import of #{name}: #{version}")
if AshHq.Docs.exists?(
if Ash.exists?(
Ash.Query.for_read(AshHq.Docs.LibraryVersion, :read)
|> Ash.Query.filter(library_id == ^library.id and version == ^version)
) do

View file

@ -1,10 +1,12 @@
defmodule AshHq.Docs.Library do
@moduledoc false
use Ash.Resource,
domain: AshHq.Docs,
data_layer: AshPostgres.DataLayer,
extensions: [AshOban]
actions do
default_accept :*
defaults [:create, :update, :destroy]
read :read do
@ -31,6 +33,7 @@ defmodule AshHq.Docs.Library do
end
update :import do
require_atomic? false
transaction? false
argument :metadata, :map do
@ -49,7 +52,7 @@ defmodule AshHq.Docs.Library do
end
oban do
api AshHq.Docs
domain AshHq.Docs
triggers do
trigger :import do
@ -69,32 +72,42 @@ defmodule AshHq.Docs.Library do
uuid_primary_key :id
attribute :name, :string do
public? true
allow_nil? false
end
attribute :display_name, :string do
public? true
allow_nil? false
end
attribute :order, :integer do
public? true
allow_nil? false
end
attribute :description, :string
attribute :description, :string do
public? true
end
attribute :repo_org, :string do
public? true
allow_nil? false
default "ash-project"
end
attribute :module_prefixes, {:array, :string} do
public? true
allow_nil? false
default []
end
attribute :mix_project, :string
attribute :mix_project, :string do
public? true
end
attribute :skip_versions, {:array, :string} do
public? true
default []
allow_nil? false
end
@ -103,9 +116,12 @@ defmodule AshHq.Docs.Library do
end
relationships do
has_many :versions, AshHq.Docs.LibraryVersion
has_many :versions, AshHq.Docs.LibraryVersion do
public? true
end
has_one :latest_library_version, AshHq.Docs.LibraryVersion do
public? true
sort version: :desc
from_many? true
end
@ -119,8 +135,6 @@ defmodule AshHq.Docs.Library do
end
code_interface do
define_for AshHq.Docs
define :read
define :by_name, args: [:name], get?: true
define :create

View file

@ -5,10 +5,10 @@ defmodule AshHq.Docs.Library.Preparations.FilterPendingImport do
query
|> Ash.Query.ensure_selected([:name])
|> Ash.Query.load(:latest_version)
|> Ash.Query.after_action(fn query, results ->
|> Ash.Query.after_action(fn _query, results ->
pending_import =
results
|> query.api.load!(:latest_version)
|> Ash.load!(:latest_version)
|> Enum.flat_map(fn result ->
hex_info =
Finch.build(:get, "https://hex.pm/api/packages/#{result.name}")

View file

@ -2,10 +2,12 @@ defmodule AshHq.Docs.LibraryVersion do
@moduledoc false
use Ash.Resource,
domain: AshHq.Docs,
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
actions do
default_accept :*
defaults [:update, :destroy]
read :read do
@ -90,10 +92,12 @@ defmodule AshHq.Docs.LibraryVersion do
uuid_primary_key :id
attribute :version, :string do
public? true
allow_nil? false
end
attribute :hydrated, :boolean do
public? true
default false
allow_nil? false
end
@ -103,13 +107,22 @@ defmodule AshHq.Docs.LibraryVersion do
relationships do
belongs_to :library, AshHq.Docs.Library do
public? true
allow_nil? true
end
has_many :extensions, AshHq.Docs.Extension
has_many :guides, AshHq.Docs.Guide
has_many :modules, AshHq.Docs.Module
has_many :mix_tasks, AshHq.Docs.MixTask
has_many :extensions, AshHq.Docs.Extension do
public? true
end
has_many :guides, AshHq.Docs.Guide do
public? true
end
has_many :modules, AshHq.Docs.Module do
public? true
end
has_many :mix_tasks, AshHq.Docs.MixTask do
public? true
end
end
postgres do
@ -118,7 +131,6 @@ defmodule AshHq.Docs.LibraryVersion do
end
code_interface do
define_for AshHq.Docs
define :build, args: [:library, :version]
define :defined_for, args: [:library, :versions]
define :by_version, args: [:library, :version]

View file

@ -2,10 +2,12 @@ defmodule AshHq.Docs.MixTask do
@moduledoc false
use Ash.Resource,
domain: AshHq.Docs,
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
actions do
default_accept :*
defaults [:update, :destroy]
read :read do
@ -48,30 +50,39 @@ defmodule AshHq.Docs.MixTask do
uuid_primary_key :id
attribute :name, :string do
public? true
allow_nil? false
end
attribute :category, :string do
public? true
allow_nil? false
default "Misc"
end
attribute :file, :string
attribute :file, :string do
public? true
end
attribute :module_name, :string
attribute :module_name, :string do
public? true
end
attribute :doc, :string do
public? true
allow_nil? false
constraints trim?: false, allow_empty?: true
default ""
end
attribute :doc_html, :string do
public? true
constraints trim?: false, allow_empty?: true
writable? false
end
attribute :order, :integer do
public? true
allow_nil? false
end
@ -80,6 +91,7 @@ defmodule AshHq.Docs.MixTask do
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
public? true
allow_nil? true
end
end
@ -93,9 +105,6 @@ defmodule AshHq.Docs.MixTask do
end
end
code_interface do
define_for AshHq.Docs
end
resource do
description "Represents a mix task that has been exposed by a library"

View file

@ -2,10 +2,12 @@ defmodule AshHq.Docs.Module do
@moduledoc false
use Ash.Resource,
domain: AshHq.Docs,
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
actions do
default_accept :*
defaults [:update, :destroy]
read :read do
@ -51,28 +53,35 @@ defmodule AshHq.Docs.Module do
uuid_primary_key :id
attribute :name, :string do
public? true
allow_nil? false
end
attribute :category, :string do
public? true
allow_nil? false
default "Misc"
end
attribute :file, :string
attribute :file, :string do
public? true
end
attribute :doc, :string do
public? true
allow_nil? false
constraints trim?: false, allow_empty?: true
default ""
end
attribute :doc_html, :string do
public? true
constraints trim?: false, allow_empty?: true
writable? false
end
attribute :order, :integer do
public? true
allow_nil? false
end
@ -81,10 +90,13 @@ defmodule AshHq.Docs.Module do
relationships do
belongs_to :library_version, AshHq.Docs.LibraryVersion do
public? true
allow_nil? true
end
has_many :functions, AshHq.Docs.Function
has_many :functions, AshHq.Docs.Function do
public? true
end
end
postgres do
@ -96,9 +108,6 @@ defmodule AshHq.Docs.Module do
end
end
code_interface do
define_for AshHq.Docs
end
resource do
description "Represents a module that has been exposed by a library"

View file

@ -2,10 +2,12 @@ defmodule AshHq.Docs.Option do
@moduledoc false
use Ash.Resource,
domain: AshHq.Docs,
data_layer: AshPostgres.DataLayer,
extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown]
actions do
default_accept :*
defaults [:update, :destroy]
read :read do
@ -53,52 +55,70 @@ defmodule AshHq.Docs.Option do
uuid_primary_key :id
attribute :name, :string do
public? true
allow_nil? false
end
attribute :type, :string do
public? true
allow_nil? false
end
attribute :doc, :string do
public? true
allow_nil? false
constraints trim?: false, allow_empty?: true
default ""
end
attribute :doc_html, :string do
public? true
constraints trim?: false, allow_empty?: true
writable? false
end
attribute :required, :boolean do
public? true
allow_nil? false
default false
end
attribute :argument_index, :integer
attribute :argument_index, :integer do
public? true
end
attribute :links, :map do
public? true
default %{}
end
attribute :default, :string
attribute :path, {:array, :string}
attribute :order, :integer, allow_nil?: false
attribute :default, :string do
public? true
end
attribute :path, {:array, :string} do
public? true
end
attribute :order, :integer do
public? true
allow_nil? false
end
timestamps()
end
relationships do
belongs_to :dsl, AshHq.Docs.Dsl do
public? true
allow_nil? true
end
belongs_to :library_version, AshHq.Docs.LibraryVersion do
public? true
allow_nil? true
end
belongs_to :extension, AshHq.Docs.Extension do
public? true
allow_nil? true
end
end
@ -114,7 +134,6 @@ defmodule AshHq.Docs.Option do
end
code_interface do
define_for AshHq.Docs
define :read
end

View file

@ -5,9 +5,8 @@ defmodule AshHq.Github.Contributor.Actions.Import do
def run(_input, _, _) do
AshHq.Docs.Library
|> AshHq.Docs.read!()
|> Ash.stream!()
|> Stream.flat_map(fn library ->
:timer.sleep(1000)
opts = []
opts =
@ -33,7 +32,7 @@ defmodule AshHq.Github.Contributor.Actions.Import do
Map.put(contributor, "order", index)
end)
|> Stream.uniq_by(&Map.get(&1, "id"))
|> AshHq.Github.bulk_create(AshHq.Github.Contributor, :create,
|> Ash.bulk_create(AshHq.Github.Contributor, :create,
upsert?: true,
upsert_fields: [:order, :login, :avatar_url, :html_url],
return_errors?: true,

View file

@ -1,10 +1,12 @@
defmodule AshHq.Github.Contributor do
@moduledoc "A contributor to any package deployed on Ash HQ."
use Ash.Resource,
domain: AshHq.Github,
data_layer: AshPostgres.DataLayer,
extensions: [AshOban]
actions do
default_accept :*
defaults [:create, :read, :update, :destroy]
read :in_order do
@ -19,7 +21,7 @@ defmodule AshHq.Github.Contributor do
end
oban do
api AshHq.Github
domain AshHq.Github
scheduled_actions do
schedule :import, "0 */6 * * *" do
@ -34,10 +36,10 @@ defmodule AshHq.Github.Contributor do
writable? true
end
attribute :login, :string, allow_nil?: false
attribute :avatar_url, :string, allow_nil?: false
attribute :html_url, :string, allow_nil?: false
attribute :order, :integer, allow_nil?: false
attribute :login, :string, allow_nil?: false, public?: true
attribute :avatar_url, :string, allow_nil?: false, public?: true
attribute :html_url, :string, allow_nil?: false, public?: true
attribute :order, :integer, allow_nil?: false, public?: true
end
postgres do
@ -46,7 +48,6 @@ defmodule AshHq.Github.Contributor do
end
code_interface do
define_for AshHq.Github
define :in_order
end

View file

@ -1,6 +1,6 @@
defmodule AshHq.Github do
@moduledoc "Api for interacting with data synchronized from github."
use Ash.Api
@moduledoc "Domain for interacting with data synchronized from github."
use Ash.Domain
resources do
resource AshHq.Github.Contributor

View file

@ -1,5 +1,5 @@
defmodule AshHq.MailingList.EmailNotifier do
@moduledoc "Sends emails from events in the MailingList api"
@moduledoc "Sends emails from events in the MailingList domain"
use Ash.Notifier
def notify(%Ash.Notifier.Notification{

View file

@ -2,9 +2,9 @@ defmodule AshHq.MailingList do
@moduledoc """
Handles documentation data.
"""
use Ash.Api, otp_app: :ash_hq
use Ash.Domain, otp_app: :ash_hq
resources do
registry AshHq.MailingList.Registry
resource AshHq.MailingList.Email
end
end

View file

@ -1,9 +0,0 @@
defmodule AshHq.MailingList.Registry do
@moduledoc false
use Ash.Registry,
extensions: [Ash.Registry.ResourceValidations]
entries do
entry AshHq.MailingList.Email
end
end

View file

@ -2,10 +2,12 @@ defmodule AshHq.MailingList.Email do
@moduledoc false
use Ash.Resource,
domain: AshHq.MailingList,
data_layer: AshPostgres.DataLayer,
notifiers: AshHq.MailingList.EmailNotifier
actions do
default_accept :*
defaults [:create, :read]
end
@ -13,6 +15,7 @@ defmodule AshHq.MailingList.Email do
uuid_primary_key :id
attribute :email, :ci_string do
public? true
allow_nil? false
end
@ -25,8 +28,6 @@ defmodule AshHq.MailingList.Email do
end
code_interface do
define_for AshHq.MailingList
define :all, action: :read
end

View file

@ -13,12 +13,4 @@ defmodule AshHqWeb.LiveUserAuth do
{:cont, assign(socket, :current_user, nil)}
end
end
def on_mount(:live_user_required, _params, _session, socket) do
if socket.assigns[:current_user] do
{:cont, socket}
else
{:halt, Phoenix.LiveView.redirect(socket, to: ~p"/sign-in")}
end
end
end

View file

@ -224,7 +224,7 @@ defmodule AshHqWeb.Pages.Docs do
|> load_for_search()
new_libraries =
AshHq.Docs.load!(socket.assigns.libraries, versions: [guides: guides_query])
Ash.load!(socket.assigns.libraries, versions: [guides: guides_query])
assign(socket, :libraries, new_libraries)
end
@ -331,7 +331,7 @@ defmodule AshHqWeb.Pages.Docs do
resource
|> Ash.Query.select(field)
|> Ash.Query.filter(id == ^record.id)
|> AshHq.Docs.read_one!()
|> Ash.read_one!()
|> Map.get(field)
Map.put(record, field, value)

View file

@ -97,7 +97,7 @@ defmodule AshHqWeb.Pages.Forum do
|> Ash.Query.filter(channel_id == ^socket.assigns.channel.id)
|> Ash.Query.select(:name)
|> Ash.Query.sort(:name)
|> AshHq.Discord.read!()
|> Ash.read!()
|> Enum.map(&to_string(&1.name))
assign(socket, :tags, tags)

View file

@ -489,7 +489,6 @@ defmodule AshHqWeb.Pages.Home do
contributors: contributors,
email_form:
AshPhoenix.Form.for_create(AshHq.MailingList.Email, :create,
api: AshHq.MailingList,
upsert?: true,
upsert_identity: :unique_email
)
@ -638,6 +637,7 @@ defmodule AshHqWeb.Pages.Home do
@post_example """
defmodule Example.Post do
use Ash.Resource,
domain: Example,
data_layer: AshPostgres.DataLayer
resource do

View file

@ -3,8 +3,7 @@ defmodule AshHqWeb.SessionPlug do
@behaviour Plug
@cookies_to_replicate [
"theme",
"selected_types"
"theme"
]
def init(_), do: []

View file

@ -9,7 +9,7 @@ defmodule AshHqWeb.RedirectToHex do
AshHq.Docs.Module
|> Ash.Query.filter(name == ^dsl_target or sanitized_name == ^dsl_target)
|> Ash.Query.load(to_load)
|> AshHq.Docs.read_one()
|> Ash.read_one()
|> case do
{:ok, module} when not is_nil(module) ->
{:halt, redirect(socket, external: AshHqWeb.DocRoutes.doc_link(module))}
@ -30,7 +30,7 @@ defmodule AshHqWeb.RedirectToHex do
AshHq.Docs.MixTask
|> Ash.Query.filter(name == ^mix_task or sanitized_name == ^mix_task)
|> Ash.Query.load(to_load)
|> AshHq.Docs.read_one()
|> Ash.read_one()
|> case do
{:ok, module} when not is_nil(module) ->
{:halt, redirect(socket, external: AshHqWeb.DocRoutes.doc_link(module))}
@ -51,7 +51,7 @@ defmodule AshHqWeb.RedirectToHex do
AshHq.Docs.Module
|> Ash.Query.filter(name == ^module or sanitized_name == ^module)
|> Ash.Query.load(to_load)
|> AshHq.Docs.read_one()
|> Ash.read_one()
|> case do
{:ok, module} when not is_nil(module) ->
{:halt, redirect(socket, external: AshHqWeb.DocRoutes.doc_link(module))}
@ -67,7 +67,7 @@ defmodule AshHqWeb.RedirectToHex do
AshHq.Docs.Module
|> Ash.Query.filter(name == ^module or sanitized_name == ^module)
|> Ash.Query.load(to_load)
|> AshHq.Docs.read_one()
|> Ash.read_one()
|> case do
{:ok, module} when not is_nil(module) ->
{:halt, redirect(socket, external: AshHqWeb.DocRoutes.doc_link(module))}

View file

@ -10,12 +10,10 @@ defmodule AshHqWeb.Router do
plug(:put_root_layout, {AshHqWeb.LayoutView, :root})
plug(:protect_from_forgery)
plug(AshHqWeb.SessionPlug)
plug(:load_from_session)
end
pipeline :api do
plug(:accepts, ["json"])
plug(:load_from_bearer)
end
pipeline :admin_basic_auth do

View file

@ -2,9 +2,9 @@ defmodule AshHqWeb.Schema do
@moduledoc "The absinthe graphql schema"
use Absinthe.Schema
@apis [AshHq.Docs]
@domains [AshHq.Docs]
use AshGraphql, apis: @apis
use AshGraphql, domains: @domains
query do
end

View file

@ -1,18 +1,20 @@
<!DOCTYPE html>
<html lang="en" class="<%= "#{@configured_theme}" %>">
<html lang="en" class={@configured_theme}>
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<%= csrf_meta_tag() %>
<%= live_title_tag assigns[:page_title] || "Ash Framework" %>
<.live_title>
<%= assigns[:page_title] || "Ash Framework" %>
</.live_title>
<%= if Application.get_env(:ash_hq, :analytics?) do %>
<script defer data-domain="ash-hq.org" src="https://plausible.io/js/plausible.js"></script>
<% end %>
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.css" />
<script src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.2/jquery.min.js"></script>
<link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/assets/app.css") %>"/>
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/assets/app.css")}/>
</head>
<body class="h-full">
<%= case live_flash(@flash, :info) do %>
@ -34,6 +36,6 @@
<%= if @live_action == :media do %>
<script src="https://platform.twitter.com/widgets.js" charset="utf-8" ></script>
<% end %>
<script defer phx-track-static type="text/javascript" src="<%= Routes.static_path(@conn, "/assets/app.js") %>"></script>
<script defer phx-track-static type="text/javascript" src={Routes.static_path(@conn, "/assets/app.js")}></script>
</body>
</html>

View file

@ -41,7 +41,6 @@ defmodule AshHqWeb.AppViewLive do
uri={@uri}
close={close_search()}
libraries={@libraries}
selected_types={@selected_types}
change_types="change-types"
change_versions="change-versions"
remove_version="remove_version"
@ -158,23 +157,6 @@ defmodule AshHqWeb.AppViewLive do
{:noreply, socket}
end
def handle_event("change-types", %{"types" => types}, socket) do
types =
types
|> Enum.filter(fn {_, value} ->
value == "true"
end)
|> Enum.map(&elem(&1, 0))
{:noreply,
socket
|> assign(
:selected_types,
types
)
|> push_event("selected-types", %{types: types})}
end
def handle_event("toggle_theme", _, socket) do
theme =
case socket.assigns.configured_theme do
@ -206,19 +188,6 @@ defmodule AshHqWeb.AppViewLive do
configured_theme = session["theme"] || "system"
all_types = AshHq.Docs.Extensions.Search.Types.types()
selected_types =
case session["selected_types"] do
nil ->
AshHq.Docs.Extensions.Search.Types.types()
types ->
types
|> String.split(",")
|> Enum.filter(&(&1 in all_types))
end
versions_query =
AshHq.Docs.LibraryVersion
|> Ash.Query.sort(version: :desc)
@ -228,12 +197,7 @@ defmodule AshHqWeb.AppViewLive do
{:ok,
socket
|> assign(:libraries, libraries)
|> assign(
:selected_types,
selected_types
)
|> assign(configured_theme: configured_theme)
|> push_event("selected_types", %{types: selected_types})}
|> assign(configured_theme: configured_theme)}
end
def toggle_search(js \\ %JS{}) do

View file

@ -39,7 +39,8 @@ defmodule AshHq.MixProject do
# Type `mix help deps` for examples and options.
defp deps do
[
{:ash, "~> 3.0.0-rc"},
# {:ash, "~> 3.0.0-rc"},
{:ash, path: "../../ash/ash", override: true},
{:ash_postgres, "~> 2.0.0-rc"},
{:ash_admin, "~> 0.10.10-rc"},
{:ash_phoenix, "~> 2.0.0-rc"},
@ -99,6 +100,7 @@ defmodule AshHq.MixProject do
{:phoenix_html, "~> 4.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.18"},
{:html_entities, "~> 0.5"},
# locked for compatibility
{:finch, "~> 0.10"},
{:floki, "~> 0.30"},

View file

@ -44,7 +44,7 @@
"equivalex": {:hex, :equivalex, "1.0.3", "170d9a82ae066e0020dfe1cf7811381669565922eb3359f6c91d7e9a1124ff74", [:mix], [], "hexpm", "46fa311adb855117d36e461b9c0ad2598f72110ad17ad73d7533c78020e045fc"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"},
"ets": {:hex, :ets, "0.8.1", "8ff9bcda5682b98493f8878fc9dbd990e48d566cba8cce59f7c2a78130da29ea", [:mix], [], "hexpm", "6be41b50adb5bc5c43626f25ea2d0af1f4a242fb3fad8d53f0c67c20b78915cc"},
"ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"},
"ex_check": {:hex, :ex_check, "0.16.0", "07615bef493c5b8d12d5119de3914274277299c6483989e52b0f6b8358a26b5f", [:mix], [], "hexpm", "4d809b72a18d405514dda4809257d8e665ae7cf37a7aee3be6b74a34dec310f5"},
"ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"},
"excoveralls": {:hex, :excoveralls, "0.18.0", "b92497e69465dc51bc37a6422226ee690ab437e4c06877e836f1c18daeb35da9", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1109bb911f3cb583401760be49c02cbbd16aed66ea9509fc5479335d284da60b"},
@ -61,6 +61,7 @@
"hackney": {:hex, :hackney, "1.17.1", "08463f93d2cc1a03817bf28d8dae6021543f773bd436c9377047224856c4422c", [:rebar3], [{:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "~> 3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "d2cba9e3c8103ad0320623e9f1c33e8d378a15eaabe2ee8ae441898f3d35a18c"},
"haystack": {:hex, :haystack, "0.1.0", "6cb9c72caf40ed4a5f9a094e6b09993c68c3fda0e01280c60c331a19860c504c", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:stemmer, "~> 1.1", [hex: :stemmer, repo: "hexpm", optional: false]}], "hexpm", "27a582513ef933c1b11345b96f8d41ee137d03b25312bd85068ffe8fec503635"},
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"json_xema": {:hex, :json_xema, "0.4.2", "85de190f597a98ce9da436b8a59c97ef561a6ab6017255df8b494babefd6fb10", [:mix], [{:conv_case, "~> 0.2", [hex: :conv_case, repo: "hexpm", optional: false]}, {:xema, "~> 0.11", [hex: :xema, repo: "hexpm", optional: false]}], "hexpm", "5516213758667d21669e0d63ea287238d277519527bac6c02140a5e34c1fda80"},