alchemy-json_api 0.10.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 566f8ea63b33d368120cc948fa2c61b4b0039957249e85718f650e8e7ca8d322
4
- data.tar.gz: 6816b1a2b645b9711a77bd8916814cd245b319e8144c6de278ec3f780c03f36f
3
+ metadata.gz: e8800ea751939b672e2361001cffe2d0da220a6ee61b25c6728c0e0a8db3ca9b
4
+ data.tar.gz: b9a7baa74ef2e0336aa91ad20f03ef26cb8f85c258caa1faf0b8a0c03d665a6f
5
5
  SHA512:
6
- metadata.gz: 6041f03d5b9264d25312b7e4e1e50d9e5aae32154a47d8e8178bf7ab1dbd5362127e683d40ecba95f1fd05bb1d503afb6444286a97e99bcf5514fa4d48ade95c
7
- data.tar.gz: 5355f10ab5dc5ae2ff7b2fb9412358467e4d351c23efbb37e8a9fbe6e300e6848d6e2b3a01c58fae8d1fbf8df00749835d55b65c43b2705dfcf2431281ef6a8a
6
+ metadata.gz: 3bbefcf52e8b02cad76158b312945bc1a818df8427b658898073bd7cdb8ef09a8d594bb33db0b0d5bcd2a4a9a7b4bd532d2d14a5c5c59f65cd8bbb880394232b
7
+ data.tar.gz: e4a795e0ac64b11930b10d1f582339326f79b9695863930aa69856e93c8ff41f76341710e13dad70074b85fc4a99453605d2725585525c1816fcda19f877c3af
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module Alchemy
3
+ module JsonApi
4
+ module Admin
5
+ class LayoutPagesController < PagesController
6
+ private
7
+
8
+ def page_scope
9
+ page_scope_with_includes(page_version: :draft_version).layoutpages
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module Alchemy
3
+ module JsonApi
4
+ module Admin
5
+ class PagesController < JsonApi::PagesController
6
+ prepend_before_action { authorize! :edit_content, Alchemy::Page }
7
+
8
+ def show
9
+ render jsonapi: Alchemy::JsonApi::Page.new(@page, page_version: :draft_version)
10
+ end
11
+
12
+ private
13
+
14
+ def page_scope
15
+ page_scope_with_includes(page_version: :draft_version).contentpages
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -9,6 +9,11 @@ module Alchemy
9
9
  include JSONAPI::Filtering
10
10
  include JSONAPI::Pagination
11
11
 
12
+ rescue_from(
13
+ CanCan::AccessDenied,
14
+ with: :render_jsonapi_unauthorized,
15
+ )
16
+
12
17
  private
13
18
 
14
19
  def render_jsonapi_internal_server_error(exception)
@@ -25,6 +30,11 @@ module Alchemy
25
30
  message << " " << exception.backtrace.join("\n ")
26
31
  logger.fatal("#{message}\n\n")
27
32
  end
33
+
34
+ def render_jsonapi_unauthorized(exception)
35
+ error = { status: "401", title: Rack::Utils::HTTP_STATUS_CODES[401] }
36
+ render jsonapi_errors: [error], status: :unauthorized
37
+ end
28
38
  end
29
39
  end
30
40
  end
@@ -4,10 +4,6 @@ module Alchemy
4
4
  class LayoutPagesController < JsonApi::PagesController
5
5
  private
6
6
 
7
- def base_page_scope
8
- Page.all
9
- end
10
-
11
7
  def page_scope
12
8
  page_scope_with_includes.layoutpages
13
9
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Alchemy
3
4
  module JsonApi
4
5
  class PagesController < JsonApi::BaseController
@@ -8,14 +9,16 @@ module Alchemy
8
9
  allowed = [:page_layout]
9
10
 
10
11
  jsonapi_filter(page_scope, allowed) do |filtered|
11
- jsonapi_paginate(filtered.result) do |paginated|
12
+ # decorate with our page model that has a eager loaded elements collection
13
+ pages = filtered.result.map { |p| Alchemy::JsonApi::Page.new(p) }
14
+ jsonapi_paginate(pages) do |paginated|
12
15
  render jsonapi: paginated
13
16
  end
14
17
  end
15
18
  end
16
19
 
17
20
  def show
18
- render jsonapi: @page
21
+ render jsonapi: Alchemy::JsonApi::Page.new(@page)
19
22
  end
20
23
 
21
24
  private
@@ -25,7 +28,7 @@ module Alchemy
25
28
 
26
29
  {
27
30
  pagination: pagination.presence,
28
- total: page_scope.count
31
+ total: page_scope.count,
29
32
  }.compact
30
33
  end
31
34
 
@@ -34,6 +37,7 @@ module Alchemy
34
37
  end
35
38
 
36
39
  def load_page_by_id
40
+ return unless params[:path] =~ /\A\d+\z/
37
41
  page_scope.find_by(id: params[:path])
38
42
  end
39
43
 
@@ -45,18 +49,31 @@ module Alchemy
45
49
  page_scope_with_includes.contentpages
46
50
  end
47
51
 
48
- def page_scope_with_includes
52
+ def page_scope_with_includes(page_version: :public_version)
49
53
  base_page_scope.
50
- with_language(Language.current).
51
- preload(language: {nodes: [:parent, :page]}, all_elements: [:parent_element, :nested_elements, { contents: { essence: :ingredient_association } }])
54
+ where(language: Language.current).
55
+ includes(
56
+ [
57
+ :legacy_urls,
58
+ { language: { nodes: [:parent, :children, { page: { language: { site: :languages } } }] } },
59
+ {
60
+ page_version => {
61
+ elements: [
62
+ :nested_elements,
63
+ { contents: { essence: :ingredient_association } },
64
+ ],
65
+ },
66
+ },
67
+ ]
68
+ )
52
69
  end
53
70
 
54
71
  def base_page_scope
55
72
  # cancancan is not able to merge our complex AR scopes for logged in users
56
- if can?(:edit_content, Page)
57
- Page.all
73
+ if can?(:edit_content, ::Alchemy::Page)
74
+ Alchemy::Page.all
58
75
  else
59
- Page.published
76
+ Alchemy::Page.published
60
77
  end
61
78
  end
62
79
 
@@ -0,0 +1,46 @@
1
+ module Alchemy
2
+ module JsonApi
3
+ class Page < SimpleDelegator
4
+ def initialize(page, page_version: :public_version)
5
+ @page_version = page.public_send(page_version)
6
+ super(page)
7
+ end
8
+
9
+ # All elements including nested and fixed elements
10
+ def all_elements
11
+ @_all_elements ||= element_repository
12
+ end
13
+
14
+ # Not nested unfixed top level elements
15
+ def elements
16
+ @_elements ||= element_repository.not_nested.unfixed
17
+ end
18
+
19
+ # Not nested fixed top level elements
20
+ def fixed_elements
21
+ @_fixed_elements ||= element_repository.not_nested.fixed
22
+ end
23
+
24
+ def all_element_ids
25
+ @_all_element_ids ||= all_elements.map(&:id)
26
+ end
27
+
28
+ def element_ids
29
+ @_element_ids ||= elements.map(&:id)
30
+ end
31
+
32
+ def fixed_element_ids
33
+ @_fixed_element_ids ||= fixed_elements.map(&:id)
34
+ end
35
+
36
+ private
37
+
38
+ def element_repository
39
+ return Alchemy::ElementsRepository.none unless @page_version
40
+
41
+ # Need to use select here, otherwise rails would not eager load the elements correctly
42
+ Alchemy::ElementsRepository.new(@page_version.elements.select(&:public))
43
+ end
44
+ end
45
+ end
46
+ end
@@ -11,22 +11,16 @@ module Alchemy
11
11
  :created_at,
12
12
  :updated_at,
13
13
  )
14
- belongs_to :parent_element, record_type: :element, serializer: self
15
14
 
16
- belongs_to :page, record_type: :page, serializer: ::Alchemy::JsonApi::PageSerializer
17
-
18
- has_many :essences, polymorphic: true do |element|
19
- element.contents.reject { |c| !!c.try(:deprecated?) }.map!(&:essence)
15
+ attribute :deprecated do |element|
16
+ !!element.definition[:deprecated]
20
17
  end
21
18
 
22
- has_many :nested_elements, record_type: :element, serializer: self do |element|
23
- element.nested_elements.reject { |c| !!c.try(:deprecated?) }
19
+ has_many :essences, polymorphic: true do |element|
20
+ element.contents.map(&:essence)
24
21
  end
25
22
 
26
- with_options if: ->(_, params) { params[:admin] == true } do
27
- attribute :tag_list
28
- attribute :display_name, &:display_name_with_preview_text
29
- end
23
+ has_many :nested_elements, record_type: :element, serializer: self
30
24
  end
31
25
  end
32
26
  end
@@ -18,12 +18,6 @@ module Alchemy
18
18
  end
19
19
  has_many :pages
20
20
  has_one :root_page, record_type: :page, serializer: ::Alchemy::JsonApi::PageSerializer
21
-
22
- with_options if: ->(_, params) { params[:admin] == true } do
23
- attribute :created_at
24
- attribute :updated_at
25
- attribute :public
26
- end
27
21
  end
28
22
  end
29
23
  end
@@ -4,6 +4,8 @@ module Alchemy
4
4
  class PageSerializer
5
5
  include JSONAPI::Serializer
6
6
 
7
+ ELEMENT_SERIALIZER = ::Alchemy::JsonApi::ElementSerializer
8
+
7
9
  attributes(
8
10
  :name,
9
11
  :urlname,
@@ -16,24 +18,23 @@ module Alchemy
16
18
  :updated_at,
17
19
  )
18
20
 
19
- belongs_to :language, record_type: :language, serializer: ::Alchemy::JsonApi::LanguageSerializer
20
-
21
- has_many :elements, record_type: :element, serializer: ::Alchemy::JsonApi::ElementSerializer do |page|
22
- page.elements.reject { |e| !!e.try(:deprecated?) }
21
+ attribute :legacy_urls do |page|
22
+ page.legacy_urls.map(&:urlname)
23
23
  end
24
24
 
25
- has_many :fixed_elements, record_type: :element, serializer: ::Alchemy::JsonApi::ElementSerializer do |page|
26
- page.fixed_elements.reject { |c| !!c.try(:deprecated?) }
27
- end
25
+ belongs_to :language, record_type: :language, serializer: ::Alchemy::JsonApi::LanguageSerializer
28
26
 
29
- has_many :all_elements, record_type: :element, serializer: ::Alchemy::JsonApi::ElementSerializer do |page|
30
- page.all_elements.select { |e| e.public? && !e.trashed? && !e.try(:deprecated?) }
31
- end
27
+ # All public elements of this page regardless of if they are fixed or nested.
28
+ # Used for eager loading and should be used as the +include+ parameter of your query
29
+ has_many :all_elements, record_type: :element, serializer: ELEMENT_SERIALIZER
32
30
 
33
- with_options if: ->(_, params) { params[:admin] == true } do
34
- attribute :tag_list
35
- attribute :status
36
- end
31
+ # The top level public, non-fixed elements of this page that - if present -
32
+ # contains their nested_elements.
33
+ has_many :elements, record_type: :element, serializer: ELEMENT_SERIALIZER
34
+
35
+ # The top level public, fixed elements of this page that - if present -
36
+ # contains their nested_elements.
37
+ has_many :fixed_elements, record_type: :element, serializer: ELEMENT_SERIALIZER
37
38
  end
38
39
  end
39
40
  end
data/config/routes.rb CHANGED
@@ -4,4 +4,10 @@ Alchemy::JsonApi::Engine.routes.draw do
4
4
  get "pages/*path" => "pages#show", as: :page
5
5
  resources :layout_pages, only: [:index]
6
6
  get "layout_pages/*path" => "layout_pages#show", as: :layout_page
7
+
8
+ namespace :admin do
9
+ get "pages/*path" => "pages#show", as: :page
10
+ resources :layout_pages, only: [:index]
11
+ get "layout_pages/*path" => "layout_pages#show", as: :layout_page
12
+ end
7
13
  end
@@ -11,6 +11,9 @@ module Alchemy
11
11
  klass.attribute :role do |essence|
12
12
  essence.content.name
13
13
  end
14
+ klass.attribute :deprecated do |essence|
15
+ !!essence.content.definition[:deprecated]
16
+ end
14
17
  end
15
18
  end
16
19
  end
@@ -5,6 +5,24 @@ RSpec.shared_examples "an essence serializer" do
5
5
 
6
6
  it "has the right keys and values" do
7
7
  expect(subject).to have_key(:ingredient)
8
+ expect(subject[:deprecated]).to be(false)
9
+ end
10
+
11
+ context "a deprecated content" do
12
+ let(:content) { FactoryBot.create(:alchemy_content, name: "intro", element: element) }
13
+
14
+ before do
15
+ expect(content).to receive(:definition).at_least(:once) do
16
+ {
17
+ name: "intro",
18
+ deprecated: true,
19
+ }
20
+ end
21
+ end
22
+
23
+ it "has deprecated attribute set to true" do
24
+ expect(subject[:deprecated]).to eq(true)
25
+ end
8
26
  end
9
27
  end
10
28
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Alchemy
3
3
  module JsonApi
4
- VERSION = "0.10.1"
4
+ VERSION = "0.13.0"
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy-json_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Meyerhoff
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-19 00:00:00.000000000 Z
11
+ date: 2021-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: alchemy_cms
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: 6.0.a
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '6.1'
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '5.0'
29
+ version: 6.0.a
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '6.1'
@@ -100,6 +100,20 @@ dependencies:
100
100
  - - ">="
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: shoulda-matchers
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
103
117
  description: A JSONAPI compliant API for AlchemyCMS
104
118
  email:
105
119
  - mamhoff@gmail.com
@@ -110,9 +124,12 @@ files:
110
124
  - MIT-LICENSE
111
125
  - README.md
112
126
  - Rakefile
127
+ - app/controllers/alchemy/json_api/admin/layout_pages_controller.rb
128
+ - app/controllers/alchemy/json_api/admin/pages_controller.rb
113
129
  - app/controllers/alchemy/json_api/base_controller.rb
114
130
  - app/controllers/alchemy/json_api/layout_pages_controller.rb
115
131
  - app/controllers/alchemy/json_api/pages_controller.rb
132
+ - app/models/alchemy/json_api/page.rb
116
133
  - app/serializers/alchemy/json_api/element_serializer.rb
117
134
  - app/serializers/alchemy/json_api/essence_boolean_serializer.rb
118
135
  - app/serializers/alchemy/json_api/essence_date_serializer.rb