diff --git a/.travis.yml b/.travis.yml index d8e636a8a4..22a2ea22c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,11 +27,13 @@ env: - DB=postgresql EXTENSION=pages - DB=postgresql EXTENSION=images - DB=postgresql EXTENSION=resources + - DB=postgresql EXTENSION=api - DB=mysql EXTENSION=core - DB=mysql EXTENSION=dragonfly - DB=mysql EXTENSION=pages - DB=mysql EXTENSION=images - DB=mysql EXTENSION=resources + - DB=mysql EXTENSION=api notifications: email: true webhooks: diff --git a/Gemfile b/Gemfile index 20880a9fc0..d1625db34e 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,7 @@ path "./" do gem "refinerycms-images" gem "refinerycms-pages" gem "refinerycms-resources" + gem "refinerycms-api" end gem 'bootsnap', require: false diff --git a/api/app/controllers/refinery/api/graphql_controller.rb b/api/app/controllers/refinery/api/graphql_controller.rb new file mode 100644 index 0000000000..2b5da9a3f3 --- /dev/null +++ b/api/app/controllers/refinery/api/graphql_controller.rb @@ -0,0 +1,55 @@ +module Refinery + module Api + class GraphqlController < ::ApplicationController + include Refinery::Admin::BaseController + + def execute + variables = ensure_hash(params[:variables]) + query = params[:query] + operation_name = params[:operationName] + + result = Refinery::Api::GraphqlSchema.execute( + query, variables: variables, context: context, operation_name: operation_name + ) + + render json: result + rescue => e + raise e unless Rails.env.development? + handle_error_in_development e + end + + private + + def context + { + current_user: current_refinery_user + } + end + + # Handle form data, JSON body, or a blank value + def ensure_hash(ambiguous_param) + case ambiguous_param + when String + if ambiguous_param.present? + ensure_hash(JSON.parse(ambiguous_param)) + else + {} + end + when Hash, ActionController::Parameters + ambiguous_param + when nil + {} + else + raise ArgumentError, "Unexpected parameter: #{ambiguous_param}" + end + end + + def handle_error_in_development(e) + logger.error e.message + logger.error e.backtrace.join("\n") + + render json: { error: { message: e.message, backtrace: e.backtrace }, data: {} }, status: 500 + end + end + end +end diff --git a/api/app/graph/refinery/api/graphql_schema.rb b/api/app/graph/refinery/api/graphql_schema.rb new file mode 100644 index 0000000000..fdbf27c5e8 --- /dev/null +++ b/api/app/graph/refinery/api/graphql_schema.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Refinery + module Api + class GraphqlSchema < GraphQL::Schema + mutation Types::MutationType + query Types::QueryType + use GraphQL::Guard.new + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/mutations/base_mutation.rb b/api/app/graph/refinery/api/mutations/base_mutation.rb new file mode 100644 index 0000000000..0bf8d42c63 --- /dev/null +++ b/api/app/graph/refinery/api/mutations/base_mutation.rb @@ -0,0 +1,8 @@ +module Refinery + module Api + module Mutations + class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/mutations/pages/create.rb b/api/app/graph/refinery/api/mutations/pages/create.rb new file mode 100644 index 0000000000..701bced284 --- /dev/null +++ b/api/app/graph/refinery/api/mutations/pages/create.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Mutations + module Pages + class Create < Mutations::BaseMutation + graphql_name 'CreatePage' + description 'Create a Page' + + guard ->(_obj, _args, ctx) { ctx[:current_user].has_role?(:refinery) } + + argument :page, Types::Pages::PageAttributes, required: true + + field :page, Types::Pages::PageType, null: true + field :errors, [String], null: false + + def resolve(page:) + page = Refinery::Page.create!(page.to_h) + + if page.errors.empty? + { + page: page, + errors: [] + } + else + { + page: nil, + errors: page.errors.full_messages + } + end + end + end + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/mutations/pages/delete.rb b/api/app/graph/refinery/api/mutations/pages/delete.rb new file mode 100644 index 0000000000..62a86f0e62 --- /dev/null +++ b/api/app/graph/refinery/api/mutations/pages/delete.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Mutations + module Pages + class Delete < Mutations::BaseMutation + graphql_name 'DeletePage' + description 'Delete a Page' + + guard ->(_obj, _args, ctx) { ctx[:current_user].has_role?(:refinery) } + + argument :id, ID, required: true + + field :page, Types::Pages::PageType, null: true + field :errors, [String], null: false + + def resolve(id:) + page = Refinery::Page.destroy(id) + + if page.errors.empty? + { + page: page, + errors: [] + } + else + { + page: nil, + errors: page.errors.full_messages + } + end + end + end + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/mutations/pages/update.rb b/api/app/graph/refinery/api/mutations/pages/update.rb new file mode 100644 index 0000000000..66d9931644 --- /dev/null +++ b/api/app/graph/refinery/api/mutations/pages/update.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Mutations + module Pages + class Update < Mutations::BaseMutation + graphql_name 'UpdatePage' + description 'Update a Page' + + guard ->(_obj, _args, ctx) { ctx[:current_user].has_role?(:refinery) } + + argument :id, ID, required: true + argument :page, Types::Pages::PageAttributes, required: true + + field :page, Types::Pages::PageType, null: false + field :errors, [String], null: false + + def resolve(id:, page:) + page = Refinery::Page.update(id, page.to_h) + + if page.errors.empty? + { + page: page, + errors: [] + } + else + { + page: nil, + errors: page.errors.full_messages + } + end + end + end + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/queries/pages/page_query.rb b/api/app/graph/refinery/api/queries/pages/page_query.rb new file mode 100644 index 0000000000..c072acba6d --- /dev/null +++ b/api/app/graph/refinery/api/queries/pages/page_query.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Queries + module Pages + class PageQuery < GraphQL::Field.define do + name 'Page' + description 'Find a page by ID' + + type Types::Pages::PageType + argument :id, !types.ID + + resolve -> (obj, args, ctx) { + Refinery::Page.find_by_id(args[:id]) + } + end + end + end + end +end + +# frozen_string_literal: true + +field :page, Types::Pages::PageType, + null: true, resolve: Resolvers::Pages::PageResolver::ById do + description "Find page by id" + guard ->(_obj, _args, ctx) { ctx[:current_user].has_plugin?('refinery_pages') } + argument :id, ID, required: true + end \ No newline at end of file diff --git a/api/app/graph/refinery/api/queries/pages/pages_query.rb b/api/app/graph/refinery/api/queries/pages/pages_query.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/app/graph/refinery/api/resolvers/pages/page_resolver.rb b/api/app/graph/refinery/api/resolvers/pages/page_resolver.rb new file mode 100644 index 0000000000..1cb282d383 --- /dev/null +++ b/api/app/graph/refinery/api/resolvers/pages/page_resolver.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Resolvers + module Pages + class PageResolver + class All + def self.call(_obj, args, ctx) + if ctx[:current_user].has_role?(:refinery) + Refinery::Page.all + else + Refinery::Page.live + end + end + end + + class ById + def self.call(_obj, args, ctx) + if ctx[:current_user].has_role?(:refinery) + Refinery::Page.find_by_id(args[:id]) + else + Refinery::Page.live.find_by_id(args[:id]) + end + end + end + end + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/types/base_enum.rb b/api/app/graph/refinery/api/types/base_enum.rb new file mode 100644 index 0000000000..dc22a9603a --- /dev/null +++ b/api/app/graph/refinery/api/types/base_enum.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + class BaseEnum < GraphQL::Schema::Enum + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/types/base_input_object.rb b/api/app/graph/refinery/api/types/base_input_object.rb new file mode 100644 index 0000000000..101f8e0f9d --- /dev/null +++ b/api/app/graph/refinery/api/types/base_input_object.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + class BaseInputObject < GraphQL::Schema::InputObject + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/types/base_interface.rb b/api/app/graph/refinery/api/types/base_interface.rb new file mode 100644 index 0000000000..d1aab507aa --- /dev/null +++ b/api/app/graph/refinery/api/types/base_interface.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + class BaseInterface + include GraphQL::Schema::Interface + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/types/base_object.rb b/api/app/graph/refinery/api/types/base_object.rb new file mode 100644 index 0000000000..12d5d42972 --- /dev/null +++ b/api/app/graph/refinery/api/types/base_object.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + class BaseObject < GraphQL::Schema::Object + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/types/base_union.rb b/api/app/graph/refinery/api/types/base_union.rb new file mode 100644 index 0000000000..6491713e51 --- /dev/null +++ b/api/app/graph/refinery/api/types/base_union.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + class BaseUnion < GraphQL::Schema::Union + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/types/mutation_type.rb b/api/app/graph/refinery/api/types/mutation_type.rb new file mode 100644 index 0000000000..2271cb4a8e --- /dev/null +++ b/api/app/graph/refinery/api/types/mutation_type.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + class MutationType < Types::BaseObject + graphql_name 'Mutation' + description 'The mutation root for this schema' + + field :createPage, mutation: Mutations::Pages::Create + field :updatePage, mutation: Mutations::Pages::Update + field :deletePage, mutation: Mutations::Pages::Delete + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/types/pages/page_attributes.rb b/api/app/graph/refinery/api/types/pages/page_attributes.rb new file mode 100644 index 0000000000..c4ecf93426 --- /dev/null +++ b/api/app/graph/refinery/api/types/pages/page_attributes.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + module Pages + class PageAttributes < Types::BaseInputObject + description "Attributes for creating or updating a page" + + argument :parent_id, Integer, required: false + argument :path, String, required: false + argument :show_in_menu, Boolean, required: false + argument :link_url, String, required: false + argument :menu_match, String, required: false + argument :deletable, Boolean, required: false + argument :draft, Boolean, required: false + argument :skip_to_first_child, Boolean, required: false + argument :lft, Integer, required: false + argument :rgt, Integer, required: false + argument :depth, Integer, required: false + argument :view_template, String, required: false + argument :layout_template, String, required: false + argument :locale, String, required: false + argument :title, String, required: false + argument :custom_slug, String, required: false + argument :menu_title, String, required: false + argument :slug, String, required: false + argument :meta_description, String, required: false + argument :browser_title, String, required: false + + argument :parts, [PagePartAttributes], required: false + end + end + end + end +end + + diff --git a/api/app/graph/refinery/api/types/pages/page_part_attributes.rb b/api/app/graph/refinery/api/types/pages/page_part_attributes.rb new file mode 100644 index 0000000000..10e0bc7098 --- /dev/null +++ b/api/app/graph/refinery/api/types/pages/page_part_attributes.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + module Pages + class PagePartAttributes < Types::BaseInputObject + description "Attributes for creating or updating a page part" + + argument :slug, String, required: false + argument :position, Integer, required: false + argument :title, String, required: false + + argument :locale, String, required: false + argument :body, String, required: false + end + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/types/pages/page_part_type.rb b/api/app/graph/refinery/api/types/pages/page_part_type.rb new file mode 100644 index 0000000000..4f074bb137 --- /dev/null +++ b/api/app/graph/refinery/api/types/pages/page_part_type.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + module Pages + class PagePartType < GraphQL::Schema::Object + graphql_name "PagePart" + description "A PagePart" + + field :slug, String, null: true + field :position, Integer, null: true + field :title, String, null: true + + field :locale, String, null: true + field :body, String, null: true + end + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/types/pages/page_type.rb b/api/app/graph/refinery/api/types/pages/page_type.rb new file mode 100644 index 0000000000..e698702383 --- /dev/null +++ b/api/app/graph/refinery/api/types/pages/page_type.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + module Pages + class PageType < GraphQL::Schema::Object + graphql_name "Page" + description "A Page" + + field :id, Integer, null: false + field :parent_id, Integer, null: true + field :path, String, null: true + field :show_in_menu, Boolean, null: true + field :link_url, String, null: true + field :menu_match, String, null: true + field :deletable, Boolean, null: true + field :draft, Boolean, null: true + field :skip_to_first_child, Boolean, null: true + field :lft, Integer, null: true + field :rgt, Integer, null: true + field :depth, Integer, null: true + field :view_template, String, null: true + field :layout_template, String, null: true + field :locale, String, null: true + field :title, String, null: true + field :custom_slug, String, null: true + field :menu_title, String, null: true + field :slug, String, null: true + field :meta_description, String, null: true + field :browser_title, String, null: true + + field :parts, [PagePartType], null: true + end + end + end + end +end \ No newline at end of file diff --git a/api/app/graph/refinery/api/types/query_type.rb b/api/app/graph/refinery/api/types/query_type.rb new file mode 100644 index 0000000000..b12b0b5fe1 --- /dev/null +++ b/api/app/graph/refinery/api/types/query_type.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Refinery + module Api + module Types + class QueryType < Types::BaseObject + graphql_name 'Query' + description 'The query root of this schema' + + # field :pages, [Types::Pages::PageType], + # null: true, resolve: Resolvers::Pages::PageResolver::All do + # description "All pages" + # guard ->(_obj, _args, ctx) { ctx[:current_user].has_plugin?('refinery_pages') } + # end + + field :page, field: Queries::Pages::PageQuery + field :pages, field: Queries::Pages::PagesQuery + + + # field :page, Types::Pages::PageType, + # null: true, resolve: Resolvers::Pages::PageResolver::ById do + # description "Find page by id" + # guard ->(_obj, _args, ctx) { ctx[:current_user].has_plugin?('refinery_pages') } + # argument :id, ID, required: true + # end + end + end + end +end \ No newline at end of file diff --git a/api/config/routes.rb b/api/config/routes.rb new file mode 100644 index 0000000000..91c120ba14 --- /dev/null +++ b/api/config/routes.rb @@ -0,0 +1,5 @@ +Refinery::Core::Engine.routes.draw do + namespace :api do + post '/api/graphql', to: 'graphql#execute' + end +end diff --git a/api/lib/refinery/api.rb b/api/lib/refinery/api.rb new file mode 100644 index 0000000000..98439f4a25 --- /dev/null +++ b/api/lib/refinery/api.rb @@ -0,0 +1,21 @@ +require 'refinerycms-core' +require 'graphql' +require 'graphql/guard' + +module Refinery + module Api + require 'refinery/api/engine' + + class << self + attr_writer :root + + def root + @root ||= Pathname.new(File.expand_path('../../../', __FILE__)) + end + + def factory_paths + @factory_paths ||= [ root.join('spec', 'factories').to_s ] + end + end + end +end diff --git a/api/lib/refinery/api/engine.rb b/api/lib/refinery/api/engine.rb new file mode 100644 index 0000000000..14ea3555d0 --- /dev/null +++ b/api/lib/refinery/api/engine.rb @@ -0,0 +1,22 @@ +module Refinery + module Api + class Engine < Rails::Engine + extend Refinery::Engine + isolate_namespace Refinery::Api + + engine_name :refinery_api + + before_inclusion do + Refinery::Plugin.register do |plugin| + plugin.name = "refinerycms_api" + plugin.hide_from_menu = true + plugin.pathname = root + end + end + + config.after_initialize do + Refinery.register_extension(Refinery::Api) + end + end + end +end diff --git a/api/lib/refinerycms-api.rb b/api/lib/refinerycms-api.rb new file mode 100644 index 0000000000..c8f56017b7 --- /dev/null +++ b/api/lib/refinerycms-api.rb @@ -0,0 +1 @@ +require 'refinery/api' diff --git a/api/license.md b/api/license.md new file mode 100644 index 0000000000..1db26bcfc7 --- /dev/null +++ b/api/license.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2017 [Brice Sanchez](http://www.brice-sanchez.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/api/refinerycms-api.gemspec b/api/refinerycms-api.gemspec new file mode 100644 index 0000000000..ed24df5b9b --- /dev/null +++ b/api/refinerycms-api.gemspec @@ -0,0 +1,36 @@ +# Encoding: UTF-8 +require File.expand_path('../../core/lib/refinery/version', __FILE__) + +version = Refinery::Version.to_s + +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = %q{refinerycms-api} + s.version = version + s.summary = %q{Api extension for Refinery CMS} + s.description = %q{Api extension for Refinery CMS} + s.email = %q{} + s.homepage = %q{http://refinerycms.com} + s.rubyforge_project = %q{refinerycms} + s.authors = ['Brice Sanchez'] + s.license = %q{MIT} + s.require_paths = %w(lib) + + s.files = `git ls-files`.split("\n") + s.test_files = `git ls-files -- spec/*`.split("\n") + + # Runtime dependencies + s.add_dependency 'refinerycms-core', version + s.add_dependency 'graphql', '~> 1.9', '>= 1.9.17' + s.add_dependency 'graphql-guard', '~> 1.2' + + # Development dependencies (usually used for testing) + s.add_development_dependency 'refinerycms-testing', '~> 4.0.0' + + s.required_ruby_version = Refinery::Version.required_ruby_version + + s.cert_chain = [File.expand_path("../../certs/parndt.pem", __FILE__)] + if $0 =~ /gem\z/ && ARGV.include?("build") && ARGV.include?(__FILE__) + s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") + end +end \ No newline at end of file diff --git a/api/spec/graph/refinery/api/fields/pages/page_field_spec.rb b/api/spec/graph/refinery/api/fields/pages/page_field_spec.rb new file mode 100644 index 0000000000..4b85c3f197 --- /dev/null +++ b/api/spec/graph/refinery/api/fields/pages/page_field_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +module Refinery + module Api + module Fields + module Pages + describe 'PageField' do + + let!(:page) { FactoryBot.create(:page) } + + let(:context) { { } } + let(:variables) { { } } + + let(:result) do + GraphqlSchema.execute( + query_string, + context: context, + variables: variables + ) + end + + let(:query_string) do + <<-GRAPHQL + query($id: ID!) { + page(id: $id) { + title + } + } + GRAPHQL + end + + subject { result } + + context "as a visitor" do + let(:variables) do + {'query' => '', 'id' => page.id } + end + + it 'returns a page' do + subject + expect(result['data']['page']['title']).to eq(page.title) + end + end + + context "as an admin user" do + let(:variables) do + {'query' => '', 'id' => page.id } + end + + it 'returns a page' do + subject + expect(result['data']['page']['title']).to eq(page.title) + end + end + end + end + end + end +end diff --git a/api/spec/graph/refinery/api/fields/pages/pages_field_spec.rb b/api/spec/graph/refinery/api/fields/pages/pages_field_spec.rb new file mode 100644 index 0000000000..83999c2c51 --- /dev/null +++ b/api/spec/graph/refinery/api/fields/pages/pages_field_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +module Refinery + module Api + module Fields + module Pages + describe 'PagesField' do + + let!(:pages) { FactoryBot.create_list(:page, 5) } + + let(:context) { { } } + let(:variables) { { } } + + let(:result) do + GraphqlSchema.execute( + query_string, + context: context, + variables: variables + ) + end + + let(:query_string) do + <<-GRAPHQL + query { + pages { + title + } + } + GRAPHQL + end + + subject { result } + + context "as a normal user" do + it 'returns the pages' do + subject + expect(result['data']['pages'].length).to eq(5) + end + end + end + end + end + end + end diff --git a/api/spec/graph/refinery/api/mutations/pages/create_page_mutation_spec.rb b/api/spec/graph/refinery/api/mutations/pages/create_page_mutation_spec.rb new file mode 100644 index 0000000000..6153518482 --- /dev/null +++ b/api/spec/graph/refinery/api/mutations/pages/create_page_mutation_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'spec_helper' + +module Refinery + module Api + module Mutations + module Pages + describe 'CreatePageMutation' do + let(:logged_in_user) { Refinery::Core::NilUser.new } + + let(:context) { {current_user: logged_in_user} } + + let(:result) do + GraphqlSchema.execute( + query_string, + context: context, + variables: variables + ) + end + + let(:query_string) do + <<-GRAPHQL + mutation createPage($input: CreatePageInput!) { + createPage(input: $input) { + page { + title + } + } + } + GRAPHQL + end + + subject { result } + + context 'as an admin' do + context 'create a page' do + let(:variables) do + { + "input": { + "page": { + "title": "Test page" + } + } + } + end + + it 'returns the page title of the newly created page' do + subject + expect(result['data']['createPage']['page']['title']).to eq('Test page') + end + end + end + end + end + end + end +end diff --git a/api/spec/graph/refinery/api/mutations/pages/delete_page_mutation_spec.rb b/api/spec/graph/refinery/api/mutations/pages/delete_page_mutation_spec.rb new file mode 100644 index 0000000000..5751d6efe0 --- /dev/null +++ b/api/spec/graph/refinery/api/mutations/pages/delete_page_mutation_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +module Refinery + module Api + module Mutations + module Pages + describe 'DeletePageMutation' do + let(:logged_in_user) { Refinery::Core::NilUser.new } + + let!(:page) { FactoryBot.create(:page) } + + let(:context) { {current_user: logged_in_user} } + + let(:result) do + GraphqlSchema.execute( + query_string, + context: context, + variables: variables + ) + end + + let(:query_string) do + <<-GRAPHQL + mutation deletePage($input: DeletePageInput!) { + deletePage(input: $input) { + page { + id + } + } + } + GRAPHQL + end + + subject { result } + + context 'Correct page id' do + let(:variables) do + { + "input": { + "id": page.id + } + } + end + + it 'deletes the page' do + subject + expect(Refinery::Page.find_by_id(page.id)).to be(nil) + end + end + end + end + end + end +end diff --git a/api/spec/graph/refinery/api/mutations/pages/update_page_mutation_spec.rb b/api/spec/graph/refinery/api/mutations/pages/update_page_mutation_spec.rb new file mode 100644 index 0000000000..40e37833f8 --- /dev/null +++ b/api/spec/graph/refinery/api/mutations/pages/update_page_mutation_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +module Refinery + module Api + module Mutations + module Pages + describe 'UpdatePageMutation' do + let(:logged_in_user) { Refinery::Core::NilUser.new } + + let!(:page) { FactoryBot.create(:page) } + + let(:context) { {current_user: logged_in_user} } + + let(:result) do + GraphqlSchema.execute( + query_string, + context: context, + variables: variables + ) + end + + let(:query_string) do + <<-GRAPHQL + mutation updatePage($input: UpdatePageInput!) { + updatePage(input: $input) { + page { + id + title + } + } + } + GRAPHQL + end + + subject { result } + + context 'as an admin' do + context 'update a page' do + let(:variables) do + { + "input": { + "id": page.id, + "page": { + "title": "Updated test page" + } + } + } + end + + it 'returns the page id and title of the newly created page' do + subject + expect(result['data']['updatePage']['page']['id']).to eq(page.id) + expect(result['data']['updatePage']['page']['title']).to eq('Updated test page') + end + end + end + end + end + end + end +end diff --git a/api/spec/spec_helper.rb b/api/spec/spec_helper.rb new file mode 100644 index 0000000000..ecf8a46790 --- /dev/null +++ b/api/spec/spec_helper.rb @@ -0,0 +1,30 @@ +# Configure Rails Environment +ENV["RAILS_ENV"] ||= 'test' + +if File.exist?(dummy_path = File.expand_path('../dummy/config/environment.rb', __FILE__)) + require dummy_path +elsif File.dirname(__FILE__) =~ %r{vendor/extensions} + # Require the path to the refinerycms application this is vendored inside. + require File.expand_path('../../../../../config/environment', __FILE__) +else + puts "Could not find a config/environment.rb file to require. Please specify this in #{File.expand_path(__FILE__)}" +end + +require 'rspec/rails' +require 'capybara/rspec' + +Rails.backtrace_cleaner.remove_silencers! + +RSpec.configure do |config| + config.mock_with :rspec + config.filter_run :focus => true + config.run_all_when_everything_filtered = true +end + +# Requires supporting files with custom matchers and macros, etc, +# in ./support/ and its subdirectories including factories. +([Rails.root.to_s] | ::Refinery::Plugins.registered.pathnames).map{ |p| + Dir[File.join(p, 'spec', 'support', '**', '*.rb').to_s] +}.flatten.sort.each do |support_file| + require support_file +end diff --git a/lib/refinery/all.rb b/lib/refinery/all.rb index 721cd018de..5a7f0fd298 100644 --- a/lib/refinery/all.rb +++ b/lib/refinery/all.rb @@ -1,3 +1,3 @@ -%w(core images resources pages).each do |extension| +%w(core images resources pages api).each do |extension| require "refinerycms-#{extension}" end diff --git a/pages/lib/refinery/pages/configuration.rb b/pages/lib/refinery/pages/configuration.rb index b4af6658ae..39e47e8f22 100644 --- a/pages/lib/refinery/pages/configuration.rb +++ b/pages/lib/refinery/pages/configuration.rb @@ -63,7 +63,7 @@ def layout_template_whitelist self.auto_expand_admin_tree = true self.reserved_paths = %w(/rails/active_storage) self.friendly_id_reserved_words = %w( - index new session login logout users refinery admin images + index new session login logout users refinery admin images api ) | self.reserved_paths.map { |path| path.split('/').reject(&:blank?).first}.flatten.uniq self.layout_templates_pattern = 'app', 'views', '{layouts,refinery/layouts}', '*html*' self.view_templates_pattern = 'app', 'views', '{pages,refinery/pages}', '*html*'