Skip to content

Commit dac6587

Browse files
committed
minimal base place and place extension
1 parent 5c4226e commit dac6587

4 files changed

Lines changed: 223 additions & 123 deletions

File tree

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# SPDX-FileCopyrightText: 2025 diffo contributors <https://github.com/diffo-dev/diffo/graphs.contributors>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
defmodule Diffo.Provider.BasePlace do
6+
@moduledoc """
7+
Ash Resource Fragment which is the point of extension for your TMF Place.
8+
9+
`BasePlace` is the foundation for domain-specific Place kinds.
10+
Include it as a fragment on an `Ash.Resource` to get common Place attributes, Neo4j graph
11+
wiring, and the `Diffo.Provider.Place.Extension` DSL.
12+
13+
`Diffo.Provider.Place` uses `BasePlace` directly as the out-of-the-box TMF Place resource.
14+
Domain-specific resources extend it for richer domain identity.
15+
16+
## Attributes
17+
18+
- `id` — string primary key (required, no default — set by domain).
19+
- `href` — optional URI for the place.
20+
- `name` — the place name.
21+
- `type` — TMF `@type`. Defaults to `:PlaceRef`. One of `:PlaceRef`, `:GeographicSite`,
22+
`:GeographicLocation`, `:GeographicAddress`. When `referred_type` is present, `type` must
23+
be `:PlaceRef`.
24+
- `referred_type` — TMF `@referredType`. One of `:GeographicSite`, `:GeographicLocation`,
25+
`:GeographicAddress`. When present, indicates this is a reference to a place of that kind;
26+
`type` must be `:PlaceRef`.
27+
28+
## Usage
29+
30+
defmodule MyApp.GeographicSite do
31+
use Ash.Resource, fragments: [BasePlace], domain: MyApp.Domain
32+
33+
resource do
34+
description "A Geographic Site"
35+
plural_name :geographic_sites
36+
end
37+
38+
jason do
39+
pick [:id, :href, :name, :referred_type, :type]
40+
compact true
41+
rename referred_type: "@referredType", type: "@type"
42+
end
43+
44+
outstanding do
45+
expect [:id, :name, :referred_type, :type]
46+
end
47+
48+
actions do
49+
create :build do
50+
accept [:id, :href, :name]
51+
change set_attribute(:type, :GeographicSite)
52+
end
53+
end
54+
end
55+
56+
## TMF type and referred_type
57+
58+
The `type` and `referred_type` attributes map to the TMF `@type` and `@referredType` JSON
59+
fields via the jason layer. When `referred_type` is present, `type` must be `:PlaceRef`;
60+
otherwise `type` must not be `:PlaceRef`.
61+
"""
62+
use Spark.Dsl.Fragment,
63+
of: Ash.Resource,
64+
otp_app: :diffo,
65+
domain: Diffo.Provider,
66+
data_layer: AshNeo4j.DataLayer,
67+
extensions: [
68+
AshOutstanding.Resource,
69+
AshJason.Resource,
70+
Diffo.Provider.Place.Extension
71+
]
72+
73+
neo4j do
74+
relate [
75+
{:place_refs, :RELATES, :incoming, :PlaceRef}
76+
]
77+
end
78+
79+
attributes do
80+
attribute :id, :string do
81+
description "the unique id of the place"
82+
primary_key? true
83+
allow_nil? false
84+
public? true
85+
source :key
86+
end
87+
88+
attribute :href, :string do
89+
description "the href of the place"
90+
allow_nil? true
91+
public? true
92+
end
93+
94+
attribute :name, :string do
95+
description "the name of the place"
96+
allow_nil? true
97+
public? true
98+
constraints match: ~r/^[a-zA-Z0-9\s._-]+$/
99+
end
100+
101+
attribute :type, :atom do
102+
description "the type of the place"
103+
allow_nil? false
104+
public? true
105+
default :PlaceRef
106+
constraints one_of: [:PlaceRef, :GeographicSite, :GeographicLocation, :GeographicAddress]
107+
end
108+
109+
attribute :referred_type, :atom do
110+
description "the referred type of the place"
111+
allow_nil? true
112+
public? true
113+
constraints one_of: [:GeographicSite, :GeographicLocation, :GeographicAddress]
114+
end
115+
116+
create_timestamp :created_at
117+
118+
update_timestamp :updated_at
119+
end
120+
121+
relationships do
122+
has_many :place_refs, Diffo.Provider.PlaceRef do
123+
description "the place refs relating this place to instances"
124+
destination_attribute :place_id
125+
public? true
126+
end
127+
end
128+
129+
actions do
130+
defaults [:read, :destroy]
131+
132+
create :create do
133+
description "creates a place"
134+
accept [:id, :href, :name, :type, :referred_type]
135+
upsert? true
136+
end
137+
138+
update :update do
139+
description "updates the place"
140+
accept [:href, :name, :type, :referred_type]
141+
end
142+
143+
read :list do
144+
description "lists all places"
145+
end
146+
147+
read :find_by_id do
148+
description "finds place by id"
149+
get? false
150+
151+
argument :query, :ci_string do
152+
description "Return only places with id's including the given value."
153+
end
154+
155+
filter expr(contains(id, ^arg(:query)))
156+
end
157+
158+
read :find_by_name do
159+
description "finds place by name"
160+
get? false
161+
162+
argument :query, :ci_string do
163+
description "Return only places with names including the given value."
164+
end
165+
166+
filter expr(contains(name, ^arg(:query)))
167+
end
168+
end
169+
170+
validations do
171+
validate {Diffo.Validations.HrefEndsWithId, id: :id, href: :href} do
172+
where [present(:id), present(:href)]
173+
end
174+
175+
validate attribute_equals(:type, :PlaceRef) do
176+
where present(:referred_type)
177+
message "when referred_type is present, type must be PlaceRef"
178+
end
179+
180+
validate attribute_does_not_equal(:type, :PlaceRef) do
181+
where absent(:referred_type)
182+
message "when referred_type is absent, type must be not be PlaceRef"
183+
end
184+
end
185+
186+
preparations do
187+
prepare build(sort: [id: :asc, name: :asc])
188+
end
189+
end

lib/diffo/provider/components/place.ex

Lines changed: 1 addition & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,13 @@ defmodule Diffo.Provider.Place do
66
@moduledoc """
77
Ash Resource for a TMF Place
88
"""
9-
use Ash.Resource,
10-
otp_app: :diffo,
11-
domain: Diffo.Provider,
12-
data_layer: AshNeo4j.DataLayer,
13-
extensions: [AshOutstanding.Resource, AshJason.Resource]
9+
use Ash.Resource, fragments: [Diffo.Provider.BasePlace], domain: Diffo.Provider
1410

1511
resource do
1612
description "An Ash Resource for a TMF Place"
1713
plural_name :places
1814
end
1915

20-
neo4j do
21-
relate [
22-
{:place_refs, :RELATES, :incoming, :PlaceRef}
23-
]
24-
end
25-
2616
jason do
2717
pick [:id, :href, :name, :referred_type, :type]
2818
compact true
@@ -32,116 +22,4 @@ defmodule Diffo.Provider.Place do
3222
outstanding do
3323
expect [:id, :name, :referred_type, :type]
3424
end
35-
36-
actions do
37-
defaults [:read, :destroy]
38-
39-
create :create do
40-
description "creates a place"
41-
accept [:id, :href, :name, :type, :referred_type]
42-
upsert? true
43-
end
44-
45-
read :find_by_id do
46-
description "finds place by id"
47-
get? false
48-
49-
argument :query, :ci_string do
50-
description "Return only places with id's including the given value."
51-
end
52-
53-
filter expr(contains(id, ^arg(:query)))
54-
end
55-
56-
read :find_by_name do
57-
description "finds place by name"
58-
get? false
59-
60-
argument :query, :ci_string do
61-
description "Return only places with names including the given value."
62-
end
63-
64-
filter expr(contains(name, ^arg(:query)))
65-
end
66-
67-
read :list do
68-
description "lists all places"
69-
end
70-
71-
update :update do
72-
description "updates the place"
73-
accept [:href, :name, :type, :referred_type]
74-
end
75-
end
76-
77-
attributes do
78-
attribute :id, :string do
79-
description "the unique id of the place"
80-
primary_key? true
81-
allow_nil? false
82-
public? true
83-
source :key
84-
end
85-
86-
attribute :href, :string do
87-
description "the href of the place"
88-
allow_nil? true
89-
public? true
90-
end
91-
92-
attribute :name, :string do
93-
description "the name of the place"
94-
allow_nil? true
95-
public? true
96-
constraints match: ~r/^[a-zA-Z0-9\s._-]+$/
97-
end
98-
99-
attribute :type, :atom do
100-
description "the type of the place"
101-
allow_nil? false
102-
public? true
103-
default :PlaceRef
104-
constraints one_of: [:PlaceRef, :GeographicSite, :GeographicLocation, :GeographicAddress]
105-
end
106-
107-
attribute :referred_type, :atom do
108-
description "the type of the place"
109-
allow_nil? true
110-
public? true
111-
constraints one_of: [:GeographicSite, :GeographicLocation, :GeographicAddress]
112-
end
113-
114-
create_timestamp :created_at
115-
116-
update_timestamp :updated_at
117-
end
118-
119-
relationships do
120-
has_many :place_refs, Diffo.Provider.PlaceRef do
121-
description "the place refs relating this place to instances"
122-
destination_attribute :place_id
123-
public? true
124-
end
125-
end
126-
127-
validations do
128-
validate {Diffo.Validations.HrefEndsWithId, id: :id, href: :href} do
129-
where [present(:id), present(:href)]
130-
end
131-
132-
validate attribute_equals(:type, :PlaceRef) do
133-
where present(:referred_type)
134-
message "when referred_type is present, type must be PlaceRef"
135-
end
136-
137-
validate attribute_does_not_equal(:type, :PlaceRef) do
138-
where absent(:referred_type)
139-
message "when referred_type is absent, type must be not be PlaceRef"
140-
end
141-
end
142-
143-
preparations do
144-
prepare build(sort: [id: :asc, name: :asc])
145-
end
146-
14725
end
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# SPDX-FileCopyrightText: 2025 diffo contributors <https://github.com/diffo-dev/diffo/graphs.contributors>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
defmodule Diffo.Provider.Place.Extension do
6+
@moduledoc """
7+
DSL Extension customising a Place.
8+
9+
Provides compile-time declaration blocks for domain-specific Place kinds
10+
built on `Diffo.Provider.BasePlace`. All declarations are introspectable via
11+
`Diffo.Provider.Place.Extension.Info`.
12+
13+
See the [DSL cheat sheet](DSL-Diffo.Provider.Place.Extension.html) for the full DSL reference.
14+
See `Diffo.Provider.BasePlace` for full usage documentation.
15+
"""
16+
use Spark.Dsl.Extension, sections: []
17+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# SPDX-FileCopyrightText: 2025 diffo contributors <https://github.com/diffo-dev/diffo/graphs.contributors>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
defmodule Diffo.Provider.Place.Extension.Info do
6+
use Spark.InfoGenerator,
7+
extension: Diffo.Provider.Place.Extension,
8+
sections: []
9+
10+
@doc "Returns true if the module is a BasePlace-derived resource"
11+
@spec place?(module()) :: boolean()
12+
def place?(module) do
13+
Code.ensure_loaded?(module) and
14+
Diffo.Provider.Place.Extension in Ash.Resource.Info.extensions(module)
15+
end
16+
end

0 commit comments

Comments
 (0)