21
1
Fork 0
mirror of https://github.com/Evolix/chexpire.git synced 2024-05-04 17:55:11 +02:00

Merge pull request #6 from Evolix/checks-resource

Checks resource
This commit is contained in:
Colin Darie 2018-05-31 10:22:41 +02:00 committed by GitHub
commit e2f509590b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 561 additions and 36 deletions

View file

@ -14,6 +14,7 @@ gem 'puma', '~> 3.11'
gem 'devise', '~> 4.4'
gem 'devise-i18n', '~> 1.6'
gem 'simple_form', '~> 4.0'
gem 'pundit', '~> 1.1'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'

View file

@ -162,6 +162,8 @@ GEM
pry (>= 0.10.4)
public_suffix (3.0.2)
puma (3.11.4)
pundit (1.1.0)
activesupport (>= 3.0.0)
rack (2.0.5)
rack-proxy (0.6.4)
rack
@ -296,6 +298,7 @@ DEPENDENCIES
pry-byebug
pry-rails
puma (~> 3.11)
pundit (~> 1.1)
rails (~> 5.2.0)
rails-i18n (~> 5.1)
rubocop (~> 0.56.0)

View file

@ -1,3 +1 @@
//= link_tree ../images
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css

View file

@ -1,4 +1,7 @@
class ApplicationController < ActionController::Base
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
before_action :configure_devise_parameters, if: :devise_controller?
protected
@ -7,4 +10,9 @@ class ApplicationController < ActionController::Base
devise_parameter_sanitizer.permit(:sign_up, keys: [:tos_accepted])
devise_parameter_sanitizer.permit(:account_update, keys: [:notifications_enabled])
end
def user_not_authorized
flash[:alert] = I18n.t("user_not_authorized", scope: :flashes)
redirect_to(request.referrer || root_path)
end
end

View file

@ -0,0 +1,67 @@
class ChecksController < ApplicationController
before_action :authenticate_user!
before_action :set_check, except: [:index, :new, :create]
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :index
def index
@checks = policy_scope(Check)
end
def new
@check = Check.new
authorize @check
end
def create
@check = Check.new(new_check_params)
@check.user = current_user
authorize @check
if @check.save
flash[:notice] = "Your check has been saved."
redirect_to checks_path
else
flash.now[:alert] = "An error occured."
render :new
end
end
def edit; end
def update
if @check.update(update_check_params)
flash[:notice] = "Your check has been updated."
redirect_to checks_path
else
flash.now[:alert] = "An error occured."
render :edit
end
end
def destroy
@check.destroy!
flash[:notice] = "Your check has been destroyed."
redirect_to checks_path
end
private
def set_check
@check = Check.find(params[:id])
authorize @check
end
def new_check_params
check_params(:kind)
end
def update_check_params
check_params(:active)
end
def check_params(*others)
params.require(:check).permit(:domain, :domain_created_at, :comment, :vendor, *others)
end
end

View file

@ -7,8 +7,11 @@
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
import 'bootstrap'
import './src/application.scss'
import Rails from 'rails-ujs';
import 'bootstrap/js/dist/collapse';
import 'bootstrap/js/dist/dropdown';
import '../scss';
Rails.start()

View file

@ -0,0 +1 @@
import './index.scss';

View file

@ -1,2 +1,3 @@
@import '~bootstrap/scss/bootstrap';
@import 'layout';
@import 'components/users';

View file

@ -0,0 +1,15 @@
@import '~bootstrap/scss/_functions';
@import '~bootstrap/scss/_variables';
@import '~bootstrap/scss/mixins/_breakpoints';
.navbar {
margin-bottom: 30px;
@include media-breakpoint-down(sm) {
margin-bottom: 20px;
}
}
.alert-layout {
margin-bottom: 30px;
}

View file

@ -0,0 +1,2 @@
module ChecksHelper
end

57
app/models/check.rb Normal file
View file

@ -0,0 +1,57 @@
# == Schema Information
#
# Table name: checks
#
# id :bigint(8) not null, primary key
# active :boolean default(TRUE), not null
# comment :string(255)
# domain :string(255) not null
# domain_created_at :datetime
# domain_expire_at :datetime
# domain_updated_at :datetime
# kind :integer not null
# last_run_at :datetime
# last_success_at :datetime
# vendor :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# user_id :bigint(8)
#
# Indexes
#
# index_checks_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (user_id => users.id)
#
class Check < ApplicationRecord
belongs_to :user
enum kind: [:domain, :ssl]
self.skip_time_zone_conversion_for_attributes = [
:domain_created_at,
:domain_updated_at,
:domain_expire_at,
]
validates :kind, presence: true
validates :domain, presence: true
validates :domain_created_at, presence: true
validate :domain_created_at_past
validate :domain_updated_at_past
validates :comment, length: { maximum: 255 }
validates :vendor, length: { maximum: 255 }
protected
def domain_created_at_past
errors.add(:domain_created_at, :past) if domain_created_at.present? && domain_created_at.future?
end
def domain_updated_at_past
errors.add(:domain_updated_at, :past) if domain_updated_at.present? && domain_updated_at.future?
end
end

View file

@ -35,5 +35,6 @@ class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
has_many :checks
validates :tos_accepted, acceptance: true
end

View file

@ -0,0 +1,53 @@
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def index?
false
end
def show?
scope.where(id: record.id).exists?
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope
end
end
end

View file

@ -0,0 +1,25 @@
class CheckPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.where(user: user)
end
end
def create?
true
end
def update?
owner?
end
def destroy?
owner?
end
private
def owner?
record.user == user
end
end

View file

@ -0,0 +1,17 @@
<%= simple_form_for(check) do |f| %>
<%= f.input :domain, autofocus: true, input_html: { autocapitalize: :none, autocorrect: :off } %>
<% if check.new_record? %>
<%= f.input :kind, as: :radio_buttons, collection: Check.kinds.keys if check.new_record? %>
<% end %>
<%= f.input :domain_created_at, as: :date, start_year: 1990, end_year: Date.today.year %>
<%= f.input :comment %>
<%= f.input :vendor %>
<% if check.persisted? %>
<%= f.input :active %>
<% end %>
<%= f.button :submit, "Validate", class: "btn-primary" %>
<% end %>

View file

@ -0,0 +1,18 @@
<% checks.each do |check| %>
<div class="mb-4">
<div>Domain: <%= check.domain %></div>
<div>Kind: <%= check.kind %></div>
<div>Date de création: <%= check.domain_created_at.to_date %></div>
<% if check.comment? %>
<div>Comment: <%= check.comment %></div>
<% end %>
<% if check.vendor? %>
<div>Vendor: <%= check.vendor %></div>
<% end %>
<div>Active: <%= check.active %></div>
<%= link_to "Edit", edit_check_path(check) %>
</div>
<% end %>

View file

@ -0,0 +1,16 @@
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<h1>Edit your check</h1>
<%= render "form", check: @check %>
</div>
</div>
<div class="row mt-5 justify-content-center">
<div class="col-12 col-lg-8">
<%= button_to("Delete", check_path(@check), class: "btn btn-danger", method: :delete,
data: { confirm: "Are you sure ?" }) %>
</div>
</div>
</div>

View file

@ -0,0 +1,14 @@
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<% if @checks.empty? %>
<div class="alert alert-info">
<%= t(".no_check_yet_html", new_domain_path: new_check_path, new_ssl_path: new_check_path) %>
</div>
<% else %>
<h1>List of your checks</h1>
<%= render "table", checks: @checks %>
<% end %>
</div>
</div>
</div>

View file

@ -0,0 +1,9 @@
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<h1>Create a new check</h1>
<%= render "form", check: @check %>
</div>
</div>
</div>

View file

@ -1,6 +1,6 @@
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-md-6">
<div class="col-12 col-lg-10">
<%= yield :devise_form_content %>
</div>
</div>

View file

@ -1,31 +1,47 @@
<nav class="navbar navbar-expand-lg navbar-light bg-light justify-content-between">
<%= link_to "Chexpire", root_path, class: "navbar-brand" %>
<%= link_to "Chexpire", root_path , class: "navbar-brand" %>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<% if user_signed_in? %>
<li class="nav-item">
<%= link_to("My checks", checks_path, class: "nav-link") %>
</li>
<li class="nav-item">
<%= link_to("Add a check", new_check_path, class: "nav-link") %>
</li>
<% end %>
</ul>
<div class="my-2 my-lg-0">
<% if user_signed_in? %>
<div class="navbar-item">
<div class="dropdown">
<a class="nav-link dropdown-toggle" href="#"
id="navbarDropdown" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<%= current_user.email %>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<%= link_to edit_user_registration_path, class: "dropdown-item" do %>
<i class="fa fa-user"></i> <%= t(".profile", default: "Profile") %>
<% end %>
<div class="my-2 my-lg-0">
<% if user_signed_in? %>
<%= link_to destroy_user_session_path, method: :delete, class: "dropdown-item" do %>
<i class="fa fa-sign-out"></i> <%= t(".sign_out", default: "Log out") %>
<% end %>
<div class="navbar-item">
<div class="dropdown">
<a class="nav-link dropdown-toggle" href="#"
id="navbarDropdown" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<%= current_user.email %>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<%= link_to edit_user_registration_path, class: "dropdown-item" do %>
<i class="fa fa-user"></i> <%= t(".profile", default: "Profile") %>
<% end %>
<%= link_to destroy_user_session_path, method: :delete, class: "dropdown-item" do %>
<i class="fa fa-sign-out"></i> <%= t(".sign_out", default: "Log out") %>
<% end %>
</div>
</div>
</div>
</div>
<% else %>
<!-- Login link (when logged out) -->
<%= link_to t(".sign_in"), new_user_session_path, class: "navbar-item navbar-link" %>
<%= link_to t(".sign_up"), new_user_registration_path, class: "navbar-item navbar-link" %>
<% end %>
<% else %>
<!-- Login link (when logged out) -->
<%= link_to t(".sign_in"), new_user_session_path, class: "navbar-item navbar-link" %>
<%= link_to t(".sign_up"), new_user_registration_path, class: "navbar-item navbar-link" %>
<% end %>
</div>
</div>
</nav>

View file

@ -1,7 +1,13 @@
<% if notice.present? %>
<div class="alert alert-success" role="alert"><%= notice %></div>
<% end %>
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-lg-10">
<% if notice.present? %>
<div class="alert alert-success alert-layout" role="alert"><%= notice %></div>
<% end %>
<% if alert.present? %>
<div class="alert alert-danger" role="alert"><%= alert %></div>
<% end %>
<% if alert.present? %>
<div class="alert alert-danger alert-layout" role="alert"><%= alert %></div>
<% end %>
</div>
</div>
</div>

View file

@ -1,18 +1,34 @@
en:
activerecord:
attributes:
check:
domain_created_at: "Creation date"
domain_updated_at: "Update date"
user:
tos_accepted: "Terms of service"
notifications_enabled: "Notifications enabled"
errors:
models:
check:
past: "can't be in the future"
devise:
registrations:
new:
tos_acceptance_html: "You must accept our Terms of service"
flashes:
user_not_authorized: "You are not authorized to access to this resource."
shared:
navbar:
sign_up: "Sign up"
sign_in: "Sign in"
sign_out: "Sign out"
profile: "Profile"
checks:
index:
no_check_yet_html: |
You have not set up a check yet.
Please add a <a href="%{new_domain_path}">domain</a>
or a <a href="%{new_ssl_path}">ssl</a> !

View file

@ -1,6 +1,13 @@
# == Route Map
#
# Prefix Verb URI Pattern Controller#Action
# checks GET /checks(.:format) checks#index
# POST /checks(.:format) checks#create
# new_check GET /checks/new(.:format) checks#new
# edit_check GET /checks/:id/edit(.:format) checks#edit
# check PATCH /checks/:id(.:format) checks#update
# PUT /checks/:id(.:format) checks#update
# DELETE /checks/:id(.:format) checks#destroy
# new_user_session GET /users/sign_in(.:format) devise/sessions#new
# user_session POST /users/sign_in(.:format) devise/sessions#create
# destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
@ -38,6 +45,9 @@
# run `bundle exec annotate -r` after modifying this file
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :checks, except: [:show]
devise_for :users
root to: "pages#home"

View file

@ -1,3 +1,12 @@
const webpack = require('webpack')
const { environment } = require('@rails/webpacker')
environment.plugins.prepend('Provide', new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
Popper: ['popper.js', 'default'],
Util: "exports-loader?Util!bootstrap/js/dist/util",
}))
module.exports = environment

View file

@ -1,7 +1,7 @@
# Note: You must restart bin/webpack-dev-server for changes to take effect
default: &default
source_path: app/javascript
source_path: app/frontend
source_entry_path: packs
public_output_path: packs
cache_path: tmp/cache/webpacker

View file

@ -0,0 +1,19 @@
class CreateChecks < ActiveRecord::Migration[5.2]
def change
create_table :checks do |t|
t.references :user, foreign_key: true
t.integer :kind, null: false
t.string :domain, null: false
t.datetime :domain_created_at
t.datetime :domain_updated_at
t.datetime :domain_expire_at
t.datetime :last_run_at
t.datetime :last_success_at
t.string :vendor
t.string :comment
t.boolean :active, default: true, null: false
t.timestamps
end
end
end

View file

@ -10,7 +10,24 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2018_05_24_205809) do
ActiveRecord::Schema.define(version: 2018_05_29_092950) do
create_table "checks", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.bigint "user_id"
t.integer "kind", null: false
t.string "domain", null: false
t.datetime "domain_created_at"
t.datetime "domain_updated_at"
t.datetime "domain_expire_at"
t.datetime "last_run_at"
t.datetime "last_success_at"
t.string "vendor"
t.string "comment"
t.boolean "active", default: true, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_checks_on_user_id"
end
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "email", default: "", null: false
@ -36,4 +53,5 @@ ActiveRecord::Schema.define(version: 2018_05_24_205809) do
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "checks", "users"
end

View file

@ -9,6 +9,7 @@
"rails-ujs": "^5.2.0"
},
"devDependencies": {
"exports-loader": "^0.7.0",
"webpack-dev-server": "2.11.2"
}
}

View file

@ -0,0 +1,7 @@
require "test_helper"
class ChecksControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end

55
test/fixtures/checks.yml vendored Normal file
View file

@ -0,0 +1,55 @@
# == Schema Information
#
# Table name: checks
#
# id :bigint(8) not null, primary key
# active :boolean default(TRUE), not null
# comment :string(255)
# domain :string(255) not null
# domain_created_at :datetime
# domain_expire_at :datetime
# domain_updated_at :datetime
# kind :integer not null
# last_run_at :datetime
# last_success_at :datetime
# vendor :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# user_id :bigint(8)
#
# Indexes
#
# index_checks_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (user_id => users.id)
#
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
domain_example_org:
user: user1
kind: domain
domain: example.org
domain_created_at: 2017-03-01 17:29:50
domain_updated_at: 2018-02-15 12:10:00
domain_expire_at: 2019-03-01 17:29:49
last_run_at: 2018-05-24 17:29:50
last_success_at: 2018-05-24 17:29:50
vendor: ""
comment: ""
active: true
ssl_www_example_org:
user: user1
kind: ssl
domain: www.example.org
domain_created_at: 2018-05-24 17:29:50
domain_updated_at: 2018-05-24 17:29:50
domain_expire_at: 2019-05-24 17:29:49
last_run_at: 2018-05-24 17:29:50
last_success_at: 2018-05-24 17:29:50
vendor: ""
comment: "MyString"
active: true

35
test/models/check_test.rb Normal file
View file

@ -0,0 +1,35 @@
# == Schema Information
#
# Table name: checks
#
# id :bigint(8) not null, primary key
# active :boolean default(TRUE), not null
# comment :string(255)
# domain :string(255) not null
# domain_created_at :datetime
# domain_expire_at :datetime
# domain_updated_at :datetime
# kind :integer not null
# last_run_at :datetime
# last_success_at :datetime
# vendor :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# user_id :bigint(8)
#
# Indexes
#
# index_checks_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (user_id => users.id)
#
require "test_helper"
class CheckTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

View file

@ -0,0 +1,13 @@
require "test_helper"
class CheckPolicyTest < ActiveSupport::TestCase
def test_scope; end
def test_show; end
def test_create; end
def test_update; end
def test_destroy; end
end

View file

@ -1997,6 +1997,13 @@ expand-range@^1.8.1:
dependencies:
fill-range "^2.1.0"
exports-loader@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.7.0.tgz#84881c784dea6036b8e1cd1dac3da9b6409e21a5"
dependencies:
loader-utils "^1.1.0"
source-map "0.5.0"
express@^4.16.2:
version "4.16.3"
resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
@ -5234,6 +5241,10 @@ source-map-url@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
source-map@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.0.tgz#0fe96503ac86a5adb5de63f4e412ae4872cdbe86"
source-map@^0.4.2:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"