Skip to content

Commit

Permalink
Merge branch 'chore/search_endpoint' into 'main'
Browse files Browse the repository at this point in the history
feat: list spaces and components

See merge request decidim/decidim-chatbot/decidim-module-rest_full!2
  • Loading branch information
Hadrien Froger committed Nov 21, 2024
2 parents c52553c + 4c3cd25 commit c7d4d08
Show file tree
Hide file tree
Showing 73 changed files with 6,936 additions and 1,407 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
/bin/rails
.DS_Store
.migrations
.bash_history
# rspec failure tracking
.rspec-failures

Expand All @@ -23,4 +24,6 @@ development_app

.circleci
node_modules
.yarn
.yarn
.local
.npm
55 changes: 29 additions & 26 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ stages:
- translate
- publish
variables:
POSTGRES_DB: decidim_test
POSTGRES_DB: decidim_module_rest_full_test
POSTGRES_USER: decidim
POSTGRES_PASSWORD: "TEST-insecure_password"

Expand All @@ -37,7 +37,7 @@ services:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: never
- if: '$CI_COMMIT_BRANCH == "main"'
when: always
when: manual
- if: '$CI_COMMIT_TAG'
when: always
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
Expand All @@ -46,25 +46,16 @@ services:
# Lint ruby code
ruby::rubocop:
extends: .development_rules
image: ruby:3.0.7
image: ruby:3.2.5
stage: lint
script:
- bundle
- bundle exec rubocop .
##
# Lint crowdin.yml configuration file
crowdin::lint:
extends: .development_rules
image: node:18.20
stage: lint
script:
- npm install -g @crowdin/cli
- crowdin config lint --no-progress || exit 1
- crowdin status -T $CROWDIN_PERSONAL_TOKEN -i $CROWDIN_PROJECT_ID --no-progress || exit 1

##
# Publish the gem un ruby gem.
ruby::rubygem:
image: ruby:3.0.7
image: ruby:3.2.5
stage: publish
script:
- echo "---" > .credentials
Expand Down Expand Up @@ -112,10 +103,11 @@ crowdin::merge_request:
- git add config/locales
- git commit -m "Crowdin Updates"
- git push $ORIGIN HEAD:$BRANCH_NAME -o merge_request.create -o merge_request.target=main

##
# Execute rspec and expose a spec report
ruby::rspec:
image: ruby:3.0.7
image: ruby:3.2.5
stage: test
extends: .development_rules
artifacts:
Expand All @@ -133,16 +125,27 @@ ruby::rspec:
SIMPLECOV: 1
CI: 1
before_script:
- apt-get update -qq && apt-get install -y ca-certificates curl gnupg
- mkdir -p /etc/apt/keyrings
- curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
- echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
- apt-get update && apt-get install -y -qq nodejs
- npm install -g yarn --force
- gem install bundler
- bundle config set path 'vendor'
- RAILS_ENV=development bundle install -j $(nproc)
- bundle exec rake test_app
- bin/setup-tests
script:
- RAILS_ENV=test bundle exec rspec spec/decidim
- RAILS_ENV=test bundle exec rspec spec/decidim/

# Publish decidim rest full api package
decidim-client::publish:
image: node:18.20
before_script:
- cd contrib/decidim-node-client
- corepack enable
- yarn
- |
{
echo "@${CI_PROJECT_ROOT_NAMESPACE}:registry=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/npm/"
echo "${CI_API_V4_URL#https?}/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=\${CI_JOB_TOKEN}"
} | tee -a .npmrc
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .yarn/
script:
- yarn build
- yarn publish --non-interactive
extends: .publication_rules
3 changes: 2 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ inherit_gem:
RSpec/VariableName:
EnforcedStyle: snake_case
AllowedPatterns:
- '^[a-z_]+\[\]$' # Allows symbols like locales[] and host__in
- '^[a-z_]+\[[a-z_]*\]\[\]$' # Allows symbols like locales[][] and filter[name_in][]
- '^[a-z_]+\[[a-z_]*\]$' # Allows symbols like locales[] and filter[name_in]
- 'Authorization' # Authorization header
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.0.2
3.2.5
5 changes: 3 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ gem "decidim", DECIDIM_VERSION
gem "decidim-rest_full", path: base_path

gem "bootsnap", "~> 1.4"
gem "deface", ">= 1.9.0"
gem "pg"
gem "puma", ">= 5.5.1"
gem "uglifier", "~> 4.1"

gem "deface", ">= 1.9.0"

group :development, :test do
gem "byebug", "~> 11.0", platform: :mri
gem "decidim-dev", Decidim::RestFull.decidim_version
gem "decidim-meetings", Decidim::RestFull.decidim_version
gem "rswag-specs"
gem "rubocop-rspec"
end
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -811,11 +811,13 @@ DEPENDENCIES
capybara (~> 3.40)
decidim (>= 0.28, < 0.30)
decidim-dev (>= 0.28, < 0.30)
decidim-meetings (>= 0.28, < 0.30)
decidim-rest_full!
deface (>= 1.9.0)
faker (~> 3.2)
letter_opener_web (~> 1.3)
listen (~> 3.1)
pg
puma (>= 5.5.1)
rspec-rails (~> 6.0)
rswag-specs
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ Start a docusaurus website
**yarn docs:update**<br />
Generates again the openapi spec, add it to docusaurus, and compile again.

## Run tests
You can run tests on the same image used by the pipeline, to be confident to push:
```
docker compose -f docker-compose.yml -f docker-compose.test.yml up
```

## Update Versions
> Release a version is up to the maintainer of this repo.
Expand Down
11 changes: 7 additions & 4 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ require "rspec/core/rake_task"

def install_module(path)
Dir.chdir(path) do
system("bundle exec rake decidim_rest_full:install:migrations")
system("bundle check || bundle install")
# system("bundle exec rake decidim_rest_full:install:migrations")
system("bundle exec rails db:migrate")
end
end
Expand All @@ -24,7 +25,7 @@ def database_yml

"development" => {
primary: {
"adapter" => "postgres",
"adapter" => "postgresql",
"encoding" => "unicode",
"host" => ENV.fetch("DATABASE_HOST", "localhost"),
"port" => ENV.fetch("DATABASE_PORT", "5432").to_i,
Expand All @@ -35,7 +36,7 @@ def database_yml
},
"test" => {
primary: {
"adapter" => "postgres",
"adapter" => "postgresql",
"encoding" => "unicode",
"host" => ENV.fetch("DATABASE_HOST", "localhost"),
"port" => ENV.fetch("DATABASE_PORT", "5432").to_i,
Expand All @@ -53,6 +54,7 @@ task :prepare_tests do
File.open(config_file, "w") { |f| YAML.dump(database_yml, f) }
Dir.chdir("spec/decidim_dummy_app") do
system("sed -i 's/config.cache_classes = true/config.cache_classes = false/' ./config/environments/test.rb")
system("bundle exec rails db:create")
system("bundle exec rails db:migrate")
end
end
Expand All @@ -69,13 +71,14 @@ task :test_app do
"../..",
"--skip_spring",
"--demo",
"--recreate_db",
"--force_ssl",
"false",
"--locales",
"en,fr"
)
end

File.open("spec/decidim_dummy_app/Gemfile", "a") { |f| f.puts "gem 'pg'\n" }
puts "Setup DB config"
Rake::Task["prepare_tests"].invoke
puts "Install module"
Expand Down
2 changes: 1 addition & 1 deletion app/commands/decidim/rest_full/create_api_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def api_client_attributes
organization: @form.current_organization,
name: @form.name,
decidim_organization_id: @form.decidim_organization_id,
scopes: @form.scopes
scopes: @form.scopes.push("public").uniq
}
end
end
Expand Down
31 changes: 28 additions & 3 deletions app/controllers/decidim/api/rest_full/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,43 @@ class ApplicationController < ActionController::API

protected

##
# Give the current API actor.
# if acting as a service (machine-to-machine): Act as you where the first available admin
# if having a resource owner: as the resource owner
# else act as you are a public agent
def act_as
@act_as ||= if service_token?
Decidim::User.where(admin: true, blocked_at: nil, organization: current_organization).where.not(confirmed_at: nil).first
elsif current_user
current_user
end
end

def service_token?
doorkeeper_token.valid? && !doorkeeper_token.resource_owner_id
end

def current_user
Decidim::User.find_by(id: doorkeeper_token.resource_owner_id, organization: current_organization)
end

def current_organization
request.env["decidim.current_organization"]
end

def populated_fields(default_fields, allowed_fields)
fields = populate_params || default_fields
raise Decidim::RestFull::ApiException::BadRequest, "Not allowed populate param: #{fields.join(", ")}" if allowed_fields.empty?

unallowed_params = fields.reject { |f| allowed_fields.include?(f) }
raise Decidim::RestFull::ApiException::BadRequest, "Not allowed populate param: #{unallowed_params.join(", ")}" unless unallowed_params.empty?

fields.push(:id) unless fields.include? :id
# Always add timestamping
fields.push(:created_at) unless fields.include? :created_at
fields.push(:updated_at) unless fields.include? :updated_at

unallowed_params = fields.reject { |f| allowed_fields.include?(f) }
raise Decidim::RestFull::ApiException::BadRequest, "Not allowed populate param: #{unallowed_params.join(", ")}" unless unallowed_params.empty?

fields
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# frozen_string_literal: true

module Decidim
module Api
module RestFull
module Public
class ComponentsController < ApplicationController
before_action { doorkeeper_authorize! :public }

# Index all components
def index
query = find_components(Decidim::Component.all)
query = query.reorder(nil).ransack(params[:filter])
data = paginate(ActiveRecord::Base.connection.exec_query(query.result.to_sql).map do |result|
result = Struct.new(*result.keys.map(&:to_sym)).new(*result.values)
serializer = "Decidim::Api::RestFull::#{result.manifest_name.singularize.camelize}ComponentSerializer".constantize
serializer.new(result, params: { only: [], locales: available_locales, host: current_organization.host, act_as: act_as }).serializable_hash[:data]
end)

render json: { data: data }
end

def show
component_id = params.require(:id).to_i
component = find_components(Decidim::Component.where(id: component_id)).first!
serializer = "Decidim::Api::RestFull::#{component.manifest_name.singularize.camelize}ComponentSerializer".constantize

render json: serializer.new(
component,
params: { only: [], locales: available_locales, host: current_organization.host, act_as: act_as }
).serializable_hash
end

private

##
# Find components that are published in a visible space.
# exemple: if the user has no view on Decidim::Assembly#2
# THEN should not be able to query any Decidim::Assembly#2 components
def find_components(context = Decidim::Component.all)
if visible_spaces.size.positive?
first_visible_space = visible_spaces.first
query_manifest = context
query = query_manifest.where(**first_visible_space)
visible_spaces[1..].each do |visible_space|
query = query.or(query_manifest.where(**visible_space))
end
query
else
context.where("1=0")
end
end

##
# All the spaces (assembly, participatory process) visible
# for the current actor.
# @returns participatory_space_type, participatory_space_id values
def visible_spaces
@visible_spaces ||= begin
spaces = space_manifest_names.map do |space|
data = manifest_data(space)
query = data[:model].constantize.visible_for(act_as).where(organization: current_organization)
{
participatory_space_type: data[:model],
participatory_space_id: query.ids
}
end
spaces.reject do |space_params|
space_params[:participatory_space_id].empty?
end
end
end

def manifest_data(manifest)
case manifest
when :participatory_processes
{ model: "Decidim::ParticipatoryProcess", manifest: manifest }
when :assemblies
{ model: "Decidim::Assembly", manifest: manifest }
else
raise Decidim::RestFull::ApiException::BadRequest, "manifest not found: #{manifest}"
end
end

def space_manifest_names
@space_manifest_names ||= Decidim.participatory_space_registry.manifests.map(&:name)
end

def component_manifest_names
@component_manifest_names ||= Decidim.component_registry.manifests.map(&:name).reject { |manifest_name| manifest_name == :dummy }
end
end
end
end
end
end
Loading

0 comments on commit c7d4d08

Please sign in to comment.