added 260907 version of worker toolkit

This commit is contained in:
2026-09-08 20:30:51 -04:00
parent cbd3f0f8ca
commit 97aca37663
1283 changed files with 142951 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
module Api
module V1
class AwsSesController < ApplicationController
skip_authorize_resource only: [:mail_it, :notification]
skip_before_action :authenticate_user!, only: [:mail_it, :notification]
def notification
message_type = request.headers["x-amz-sns-message-type"]
# sns_topic = request.headers['x-amz-sns-topic-arn']
raw_post = request.raw_post
if message_type.include? "Confirmation"
send_subscription_confirmation(raw_post)
elsif message_type.include? "Notification"
EmailRejectDispatcher.perform_async(raw_post)
end
render nothing: true, status: 200
end
def send_subscription_confirmation(raw_post)
json = JSON.parse(raw_post)
open(json["SubscribeURL"])
end
end
end
end

View File

@@ -0,0 +1,9 @@
module Api
module V1
class ChartListsController < ApplicationController
def show
render json: ChartListService.new(current_user: current_user).as_json
end
end
end
end

View File

@@ -0,0 +1,32 @@
module Api
module V1
class ChartsController < ApplicationController
def show
chart = Chart.new(chart_params)
# FIXME
# rubocop:disable Style/SignalException
fail(ActiveRecord::RecordInvalid, chart) if chart.invalid?
# rubocop:enable Style/SignalException
render json: chart
end
def chart_params
includes_params = {
tags: [],
foods: [],
symptoms: [],
conditions: [],
treatments: [],
weathersMeasures: [],
harveyBradshawIndices: []
}
params.permit(:id, :start_at, :end_at, includes: includes_params).tap do |whitelist|
whitelist[:user] = current_user
end
end
end
end
end

View File

@@ -0,0 +1,31 @@
module Api
module V1
class ChartsPatternController < ApplicationController
skip_before_action :authenticate_user!, only: [:index]
def index
offset = charts_pattern_params[:offset].to_i
start_at = (charts_pattern_params[:start_at].to_date - offset.days).to_s
end_date = charts_pattern_params[:end_at].to_date
end_at = ((Time.current.to_date == end_date) ? end_date : (end_date + offset.days)).to_s
@patterns = Pattern.where(id: {"$in": charts_pattern_params[:pattern_ids] || []})
@extended_patterns = @patterns.map do |pattern|
pattern.extend(PatternExtender).form_chart_data(start_at: start_at,
end_at: end_at,
pattern: pattern)
end
render json: @extended_patterns, meta: {color_ids: Flaredown::Colorable::IDS}
end
private
def charts_pattern_params
params.permit(:start_at, :end_at, :offset, pattern_ids: [])
end
end
end
end

View File

@@ -0,0 +1,40 @@
module Api
module V1
class CheckinsController < ApplicationController
def index
date = params[:date]
if date.blank? && params.require(:page)
render json: current_user.checkins.where(:note.nin => [nil, ""]).order_by(date: :desc).page(params[:page]).per(10)
else
render json: current_user.checkins.includes([:harvey_bradshaw_index, :promotion_rate, :conditions, :symptoms, :treatments]).select { |x|
x.date.to_date == Date.parse(date)
}
end
end
def show
render json: Checkin.find(id)
end
def create
date = params.require(:checkin).require(:date)
parsed = DateTime.parse(date)
now = DateTime.current
save_date = DateTime.new(parsed.year, parsed.month, parsed.day, now.hour, now.minute, now.second)
checkin = Checkin::Creator.new(current_user.id, save_date).create!
render json: checkin
end
def update
render json: Checkin::Updater.new(current_user, params).update!
end
private
def id
params.require(:id)
end
end
end
end

View File

@@ -0,0 +1,45 @@
module Api
module V1
class CommentsController < ApplicationController
load_and_authorize_resource
skip_before_action :authenticate_user!, only: [:index]
def index
render json: @comments.where(:id.in => params[:ids]).order_by(created_at: :asc)
end
def show
render json: @comment
end
def create
@comment.encrypted_user_id = current_user.encrypted_id
if @comment.save
UpdatePostCountersJob.perform_async(parent_id: create_params[:post_id], parent_type: "Post")
unless @comment.encrypted_user_id == @comment.post.encrypted_user_id
Notification.create(
kind: :comment,
notificateable: @comment,
encrypted_user_id: @comment.encrypted_user_id,
encrypted_notify_user_id: @comment.post.encrypted_user_id
)
end
DiscussionMention.perform_async(current_user.encrypted_id, @comment.id.to_s)
render json: @comment, status: :created
else
render json: {errors: @comment.errors}, status: :unprocessable_entity
end
end
private
def create_params
params.require(:comment).permit(:body, :post_id)
end
end
end
end

View File

@@ -0,0 +1,33 @@
module Api
module V1
class ConditionsController < ApplicationController
load_and_authorize_resource
skip_before_action :authenticate_user!, only: [:show]
def index
@conditions = @conditions.includes(:translations)
@conditions = ids.present? ? @conditions.where(id: ids) : @conditions.order(:name).limit(50)
render json: @conditions
end
def show
render json: @condition
end
def create
render json: TrackableCreator.new(@condition, current_user).create!
end
private
def create_params
params.require(:condition).permit(:name)
end
def ids
@ids ||= params[:ids] if params[:ids].is_a?(Array)
end
end
end
end

View File

@@ -0,0 +1,32 @@
module Api
module V1
class CountriesController < ApplicationController
skip_before_action :authenticate_user!
def index
render json: Country.all, each_serializer: CountrySerializer
end
def show
country = Country.find_country_by_alpha2(alpha2)
# FIXME
# rubocop:disable Style/SignalException
fail ActiveRecord::RecordNotFound if country.nil?
# rubocop:enable Style/SignalException
render json: country, serializer: CountrySerializer
end
private
def alpha2
id = params.require(:id)
match_data = /^[[:alpha:]]{2}$/.match(id)
# FIXME
# rubocop:disable Style/SignalException
fail(ActionController::BadRequest, "id param must be a 2 alphabetic characters string") if match_data.nil?
# rubocop:enable Style/SignalException
match_data[0]
end
end
end
end

View File

@@ -0,0 +1,11 @@
module Api
module V1
class DataExportSchedulesController < ApplicationController
def create
DataExportJob.perform_later(current_user.id)
head :created
end
end
end
end

View File

@@ -0,0 +1,27 @@
module Api
module V1
class DayHabitsController < ApplicationController
skip_before_action :authenticate_user!
def index
render json: DayHabit.all
end
def show
day_habit = DayHabit.find(day_habit_id)
render json: day_habit
end
private
def day_habit_id
id = params.require(:id)
# FIXME
# rubocop:disable Style/SignalException
fail(ActionController::BadRequest, "id param is not a valid day_habit id") unless DayHabit.all_ids.include?(id)
# rubocop:enable Style/SignalException
id
end
end
end
end

View File

@@ -0,0 +1,9 @@
module Api
module V1
class DiscoursesController < ApplicationController
def create
render json: {url: DiscourseClient.new(current_user, params).generate_url}
end
end
end
end

View File

@@ -0,0 +1,29 @@
module Api
module V1
class EducationLevelsController < ApplicationController
skip_before_action :authenticate_user!
def index
render json: EducationLevel.all
end
def show
education_level = EducationLevel.find(education_level_id)
render json: education_level
end
private
def education_level_id
id = params.require(:id)
# FIXME
# rubocop:disable Style/SignalException
unless EducationLevel.all_ids.include?(id)
fail(ActionController::BadRequest, "id param is not a valid education_level id")
end
# rubocop:enable Style/SignalException
id
end
end
end
end

View File

@@ -0,0 +1,27 @@
module Api
module V1
class EthnicitiesController < ApplicationController
skip_before_action :authenticate_user!
def index
render json: Ethnicity.all
end
def show
ethnicity = Ethnicity.find(ethnicity_id)
render json: ethnicity
end
private
def ethnicity_id
id = params.require(:id)
# FIXME
# rubocop:disable Style/SignalException
fail(ActionController::BadRequest, "id param is not a valid ethnicity id") unless Ethnicity.all_ids.include?(id)
# rubocop:enable Style/SignalException
id
end
end
end
end

View File

@@ -0,0 +1,42 @@
module Api
module V1
class FoodsController < ApplicationController
load_and_authorize_resource
def index
@foods = @foods.includes(:translations)
foods =
if ids.present?
@foods.where(id: ids)
elsif scope.present?
CollectionRetriever.new(Food, scope, current_user).retrieve
end
render json: foods
end
def show
render json: @food
end
def create
render json: TrackableCreator.new(@food, current_user).create!
end
private
def create_params
{long_desc: params.require(:food).require(:name)}
end
def ids
@ids ||= params[:ids]
end
def scope
@scope ||= params[:scope]&.to_sym
end
end
end
end

View File

@@ -0,0 +1,28 @@
module Api
module V1
class HarveyBradshawIndicesController < ApplicationController
load_and_authorize_resource
def show
render json: @harvey_bradshaw_index
end
def create
@harvey_bradshaw_index.save
render json: @harvey_bradshaw_index
end
private
def create_params
params.require(:harvey_bradshaw_index).permit(
:abdominal_mass, :abdominal_pain, :abscess,
:anal_fissure, :aphthous_ulcers, :arthralgia,
:checkin_id, :erythema_nodosum, :new_fistula,
:pyoderma_gangrenosum, :stools, :uveitis, :well_being
)
end
end
end
end

View File

@@ -0,0 +1,19 @@
module Api
module V1
class InvitationsController < ApplicationController
skip_before_action :authenticate_user!
def show
render json: Invitation.find(params[:id])
end
def update
invitation = Invitation.find(params[:id])
invitation.accept!(
params.require(:invitation).permit(:email, :password, :password_confirmation)
)
render json: invitation
end
end
end
end

View File

@@ -0,0 +1,52 @@
module Api
module V1
class NotificationsController < ApplicationController
def index
notifications = Notification.where(encrypted_notify_user_id: current_user.encrypted_id)
authorize_collection :index, notifications
render json: {notifications: notifications.aggregated_by_kind_and_subject}
end
def update
notifications = Notification.where(notification_params)
authorize_collection :update, notifications
if notifications.update_all(unread: false)
render json: {notifications: notifications.aggregated_by_kind_and_subject}
else
render json: {errors: notifications.map(&:errors).compact}, status: :unprocessable_entity
end
end
def destroy
notifications = Notification.where(notification_params)
authorize_collection :destroy, notifications
if notifications.destroy
head :no_content
else
render json: {errors: notifications.map(&:errors).compact}, status: :unprocessable_entity
end
end
private
def notification_params
parameters = params.permit(:notificateable_id, :notificateable_type)
parameters[:notificateable_type] = parameters[:notificateable_type].titleize
parameters[:encrypted_notify_user_id] = current_user.encrypted_id
parameters
end
def authorize_collection(name, collection)
collection.each { |element| authorize! name, element }
end
end
end
end

View File

@@ -0,0 +1,35 @@
module Api
module V1
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
Devise.omniauth_providers.each do |provider|
define_method provider do
handle_omniauth
end
end
def failure
Rails.logger.warn("Api::V1::OmniauthCallbacksController#failure: #{failure_message}".yellow)
render json: {errors: failure_message}, status: 401
end
private
def handle_omniauth
user = User.find_for_database_authentication(email: email_param)
if user && user.invitation_token.nil?
render json: user, root: false, serializer: SessionSerializer
else
render json: {errors: "User not found"}, status: 401
end
end
def oauth_params
@oauth_params ||= ActionController::Parameters.new(request.env["omniauth.auth"])
end
def email_param
oauth_params.fetch(:info).fetch(:email)
end
end
end
end

View File

@@ -0,0 +1,60 @@
module Api
module V1
class OracleRequestsController < ApplicationController
skip_before_action :authenticate_user!
serialization_scope :oracle_token
load_resource
def show
render json: @oracle_request
end
def create
if oracle_token.present?
@oracle_request.token = oracle_token
else
loop do
@oracle_request.token = SecureRandom.uuid
break unless OracleRequest.where(token: @oracle_request.token).exists?
end
end
@oracle_request.save
render json: @oracle_request, serializer: OracleRequestWithTokenSerializer
end
def update
if @oracle_request.can_edit?(oracle_token)
@oracle_request.update!(create_params)
render json: @oracle_request
else
render json: {errors: "Unauthorized"}, status: :unauthorised
end
end
private
def create_params
params.require(:oracle_request).permit(
:age,
:sex_id,
responce: [
:name,
:confidence,
:correction
],
symptom_ids: []
)
end
def oracle_token
request.headers["X-Oracle-Token"]
end
end
end
end

View File

@@ -0,0 +1,56 @@
module Api
module V1
class PasswordsController < ApplicationController
skip_before_action :authenticate_user!
def show
user = user_signed_in? ? current_user : User.with_reset_password_token(params[:id])
if user.blank?
raise ActiveRecord::RecordNotFound, "User not found"
else
render json: user, token: params[:id], serializer: PasswordSerializer
end
end
def create
user = User.find_by!(email: email_param.downcase)
return unless user.send_reset_password_instructions
render json: user, serializer: PasswordSerializer
end
def update
if user_signed_in?
if current_user.update_with_password(update_password_params)
render json: current_user, token: params[:id], serializer: PasswordSerializer
else
render json: {errors: current_user.errors}, status: :unprocessable_entity
end
else
user = User.reset_password_by_token(update_password_by_token_params)
if user.errors.empty?
render json: user, token: params[:id], serializer: PasswordSerializer
else
render json: {errors: user.errors}, status: :unprocessable_entity
end
end
end
private
def email_param
params.require(:password).fetch(:email)
end
def update_password_params
params.require(:password).permit(:current_password, :password, :password_confirmation)
end
def update_password_by_token_params
params.require(:password).permit(:reset_password_token, :password, :password_confirmation)
end
end
end
end

View File

@@ -0,0 +1,68 @@
module Api
module V1
class PatternsController < ApplicationController
load_and_authorize_resource
skip_before_action :authenticate_user!, only: [:index]
def index
page = params[:page] || 1
pattern_ids = params[:pattern_ids]
@patterns =
if pattern_ids.present?
Pattern.where(id: {"$in" => pattern_ids})
else
Pattern.accessible_by(current_ability).where(encrypted_user_id: encrypted_user_id)
end
render json: @patterns.page(page).per(10)
end
def show
pattern = Pattern.find_by(id: pattern_params[:id])
render json: pattern
end
def create
@pattern = PatternCreator.new(pattern_params.to_h).create
render json: @pattern
end
def update
@pattern.update(pattern_params)
render json: @pattern
end
def destroy
pattern = Pattern.find_by(id: params[:id])
authorize! :destroy, pattern
if pattern.destroy
head :no_content
else
render json: {errors: pattern.errors}, status: :unprocessable_entity
end
end
private
def pattern_params
params.require(:pattern)
.permit(:name, :start_at, :end_at, includes: [:id, :category, :label])
.merge(user_id: current_user.id)
end
def current_ability
@current_ability ||= Ability.new(current_user)
end
def encrypted_user_id
@encrypted_user_id ||= SymmetricEncryption.encrypt(current_user.id)
end
end
end
end

View File

@@ -0,0 +1,18 @@
module Api
module V1
class PostablesController < ApplicationController
load_and_authorize_resource
def index
render json: PostableSerializer.new(
@postables
.where(encrypted_user_id: current_user.encrypted_id)
.order_by(created_at: :desc)
.page(params[:page])
.per(20),
current_user
)
end
end
end
end

View File

@@ -0,0 +1,46 @@
module Api
module V1
class PostsController < ApplicationController
load_and_authorize_resource
skip_before_action :authenticate_user!, only: [:index, :show]
def index
if params[:summary]
render json: SummaryPosts.new(current_user).show_list
else
@posts = DiscussionPosts.new(params, current_user).show_list
results = @posts
.includes([:comments, :notifications, :reactions])
.order(last_commented: :desc, created_at: :desc)
.page(params[:page])
.per(10)
render json: results
end
end
def show
render json: @post
end
def create
@post.encrypted_user_id = current_user.encrypted_id
if @post.save
render json: @post, status: :created
else
render json: {errors: @post.errors}, status: :unprocessable_entity
end
end
private
def create_params
params.require(:post).permit(
:title, :body,
tag_ids: [], symptom_ids: [], condition_ids: [], treatment_ids: []
)
end
end
end
end

View File

@@ -0,0 +1,75 @@
module Api
module V1
class ProfilesController < ApplicationController
require "sidekiq/api"
load_and_authorize_resource
skip_before_action :authenticate_user!, only: [:index]
def index
post = Post.find(params[:post_id])
encrypted_user_ids =
(post.comments.distinct(:encrypted_user_id) << post.encrypted_user_id).uniq.map do |encrypted_id|
SymmetricEncryption.decrypt(encrypted_id)
end
@profiles = Profile.where(user_id: encrypted_user_ids).where.not(slug_name: nil)
render json: @profiles.map { |profile| profile.attributes.slice("screen_name", "slug_name") }
end
def show
render json: @profile
end
def update
initial_onboarding_reminder = params.dig(:profile, :onboarding_reminder)
@profile.assign_attributes(update_params.merge(transform_hash_time))
time_changed = @profile.checkin_reminder_at_changed? || @profile.time_zone_name_changed?
@profile.save!
if time_changed || initial_onboarding_reminder
delete_old_job(@profile.reminder_job_id)
job_id = CheckinReminderJob.perform_in(get_reminder_time.minutes, @profile.id, @profile.checkin_reminder_at)
@profile.update_column(:reminder_job_id, job_id)
end
current_user.profile.reload
set_locale
render json: @profile
end
private
def update_params
params.require(:profile).permit(
:country_id, :sex_id, :onboarding_step_id, :birth_date,
:day_habit_id, :education_level_id, :day_walking_hours,
:pressure_units, :temperature_units, :screen_name, :notify,
:checkin_reminder, :time_zone_name, :notify_top_posts, ethnicity_ids: []
)
end
def transform_hash_time
checkin_reminder_at = params.require(:profile)[:checkin_reminder_at]
user_time = checkin_reminder_at && checkin_reminder_at.values.join(":")
{checkin_reminder_at: user_time.try(:to_time, :utc)}
end
def get_reminder_time
time_zone_name = @profile.time_zone_name
checkin_at_timezone = @profile.checkin_reminder_at.strftime("%H:%M").in_time_zone(time_zone_name)
# Select minutes
(checkin_at_timezone - Time.current.in_time_zone(time_zone_name)).divmod(1.day)[1].divmod(1.minute)[0]
end
def delete_old_job(enqueued_job_id)
Sidekiq::ScheduledSet.new.find_job(enqueued_job_id)&.delete
end
end
end
end

View File

@@ -0,0 +1,35 @@
module Api
module V1
class PromotionRatesController < ApplicationController
load_and_authorize_resource
def show
render json: @promotion_rate
end
def create
@promotion_rate.save
render json: @promotion_rate
end
def update
@promotion_rate.update(resource_params.merge(additional_params))
render json: @promotion_rate
end
private
def resource_params
params.require(:promotion_rate).permit(:checkin_id, :score, :feedback)
end
def additional_params
user = @promotion_rate.checkin.user
{user_created_at: user.created_at}
end
end
end
end

View File

@@ -0,0 +1,17 @@
module Api
module V1
class PushersController < ApplicationController
def create
render json: Flaredown.pusher.authenticate!(current_user, socket_id)
rescue
render json: {errors: "Bad authentication"}, status: "403"
end
private
def socket_id
params.require(:socket_id)
end
end
end
end

View File

@@ -0,0 +1,77 @@
module Api
module V1
class ReactionsController < ApplicationController
def create
react(__method__)
end
def update
react(__method__)
end
def destroy
reaction = Reaction.where(reaction_params).first
authorize! :destroy, reaction
if reaction.destroy
UpdatePostCountersJob.perform_async(parent_id: reaction_params[:reactable_id],
parent_type: reaction_params[:reactable_type])
head :no_content
else
render json: {errors: reaction.errors}, status: :unprocessable_entity
end
end
private
def react(method_name)
reaction = Reaction.find_or_initialize_by(reaction_params)
authorize! method_name, reaction
if reaction.save
UpdatePostCountersJob.perform_async(parent_id: reaction_params[:reactable_id],
parent_type: reaction_params[:reactable_type])
unless reaction.encrypted_user_id == reaction.reactable.encrypted_user_id
Notification.create(
kind: :reaction,
notificateable: reaction.reactable,
encrypted_user_id: reaction.encrypted_user_id,
encrypted_notify_user_id: reaction.reactable.encrypted_user_id
)
end
reaction.id = params[:id] if params[:id].present?
render json: serialized_reaction(reaction), status: :created
else
render json: {errors: reaction.errors}, status: :unprocessable_entity
end
end
def reaction_params
reaction = params.require(:reaction)
{
value: reaction[:value],
reactable_id: reaction[:reactable_id],
reactable_type: reaction[:reactable_type].titleize,
encrypted_user_id: current_user.encrypted_id
}
end
def serialized_reaction(reaction)
ReactionSerializer
.new(
Reaction.similar_to(reaction).values_count_with_participated(current_user.encrypted_id),
reaction.reactable_id.to_s,
reaction.reactable_type
)
.serialize_one
end
end
end
end

View File

@@ -0,0 +1,15 @@
module Api
module V1
class RegistrationsController < ApplicationController
skip_before_action :authenticate_user!
def create
render json: Registration.create!(params)
end
def destroy
render json: Registration.delete!(params)
end
end
end
end

View File

@@ -0,0 +1,34 @@
module Api
module V1
class SearchesController < ApplicationController
SEARCH_MAPPER = {
"dose" => Search::ForDose,
"food" => Search::ForFood,
"topic" => Search::ForTopic
}.freeze
skip_before_action :authenticate_user!, only: :show
def show
search = (SEARCH_MAPPER[resource_param] || Search).new(search_params)
# FIXME
# rubocop:disable Style/SignalException
fail(ActiveRecord::RecordInvalid, search) if search.invalid?
# rubocop:enable Style/SignalException
render json: search, serializer: SearchSerializer
end
def search_params
params.permit(:resource, :scope, query: [:name, :treatment_id]).tap do |params|
params[:user] = current_user
end
end
def resource_param
params[:resource]
end
end
end
end

View File

@@ -0,0 +1,29 @@
module Api
module V1
class SessionsController < ApplicationController
skip_before_action :authenticate_user!
def create
# FIXME
# rubocop:disable Style/SignalException
fail "missing information" if params[:user].nil?
fail "invalid email or password" if user.nil?
# rubocop:enable Style/SignalException
render json: user, root: false, serializer: SessionSerializer
rescue => e
render json: {errors: Array(e.message)}, status: 401
end
private
def user
@user ||=
begin
user = User.find_for_database_authentication(email: params[:user][:email])
user if user && user.valid_password?(params[:user][:password])
end
end
end
end
end

View File

@@ -0,0 +1,27 @@
module Api
module V1
class SexesController < ApplicationController
skip_before_action :authenticate_user!
def index
render json: Sex.all
end
def show
sex = Sex.find(sex_id)
render json: sex
end
private
def sex_id
id = params.require(:id)
# FIXME
# rubocop:disable Style/SignalException
fail(ActionController::BadRequest, "id param is not a valid sex id") unless Sex.all_ids.include?(id)
# rubocop:enable Style/SignalException
id
end
end
end
end

View File

@@ -0,0 +1,33 @@
module Api
module V1
class SymptomsController < ApplicationController
load_and_authorize_resource
skip_before_action :authenticate_user!, only: [:show]
def index
@symptoms = @symptoms.includes(:translations)
@symptoms = ids.present? ? @symptoms.where(id: ids) : @symptoms.order(:name).limit(50)
render json: @symptoms
end
def show
render json: @symptom
end
def create
render json: TrackableCreator.new(@symptom, current_user).create!
end
private
def create_params
params.require(:symptom).permit(:name)
end
def ids
@ids ||= params[:ids] if params[:ids].is_a?(Array)
end
end
end
end

View File

@@ -0,0 +1,40 @@
module Api
module V1
class TagsController < ApplicationController
load_and_authorize_resource
skip_before_action :authenticate_user!, only: [:show]
def index
@tags = @tags.includes(:translations)
if ids.present?
@tags = @tags.where(id: ids)
elsif scope.present?
@tags = CollectionRetriever.new(Tag, scope, current_user).retrieve
end
render json: @tags
end
def show
render json: @tag
end
def create
render json: TrackableCreator.new(@tag, current_user).create!
end
private
def create_params
params.require(:tag).permit(:name)
end
def ids
@ids ||= params[:ids]
end
def scope
@scope ||= params[:scope].try(:to_sym)
end
end
end
end

View File

@@ -0,0 +1,32 @@
module Api
module V1
class TopicFollowingsController < ApplicationController
load_and_authorize_resource
def show
render json: @topic_following
end
def update
if @topic_following.update(update_params)
render json: @topic_following, status: :ok
else
render json: {errors: @topic_following.errors}, status: :unprocessable_entity
end
end
private
def update_params
empty_params = {
"tag_ids" => [],
"symptom_ids" => [],
"condition_ids" => [],
"treatment_ids" => []
}
empty_params.merge(params.require(:topic_following).permit(empty_params))
end
end
end
end

View File

@@ -0,0 +1,46 @@
module Api
module V1
class TrackingsController < ApplicationController
load_and_authorize_resource except: :create
def index
render json: @trackings.by_trackable_type(trackable_type).active_at(at)
end
def show
render json: @tracking
end
def create
tracking = Tracking.new(create_params.merge(start_at: Time.zone.today, user: current_user))
authorize! :create, tracking
tracking.save!
current_user.topic_following.add_topic("#{tracking.trackable_type.downcase}_ids", tracking.trackable_id)
render json: tracking
end
def destroy
TrackingDestroyer.new(current_user, @tracking, Time.zone.today).destroy
head :no_content
end
private
def at
Time.zone.parse(params.require(:at))
end
def trackable_type
params.require(:trackable_type)
end
def create_params
params.require(:tracking).permit(:trackable_id, :trackable_type, :color_id)
end
end
end
end

View File

@@ -0,0 +1,33 @@
module Api
module V1
class TreatmentsController < ApplicationController
load_and_authorize_resource
skip_before_action :authenticate_user!, only: [:show]
def index
@treatments = @treatments.includes(:translations)
@treatments = ids.present? ? @treatments.where(id: ids) : @treatments.order(:name).limit(50)
render json: @treatments
end
def show
render json: @treatment
end
def create
render json: TrackableCreator.new(@treatment, current_user).create!
end
private
def create_params
params.require(:treatment).permit(:name)
end
def ids
@ids ||= params[:ids] if params[:ids].is_a?(Array)
end
end
end
end

View File

@@ -0,0 +1,30 @@
module Api
module V1
class UnsubscribesController < ApplicationController
skip_before_action :authenticate_user!
def update
@profile = Profile.find_by(notify_token: activation_params[:notify_token])
@profile&.update_column(attribute_dispatcher_key, false)
render json: @profile
end
private
def activation_params
params.permit(:notify_token, :notify_top_posts, :stop_remind)
end
def attribute_dispatcher_key
if activation_params[:stop_remind]
:checkin_reminder
elsif activation_params[:notify_top_posts]
:notify_top_posts
else
:notify
end
end
end
end
end

View File

@@ -0,0 +1,23 @@
module Api
module V1
class UsersController < ApplicationController
load_and_authorize_resource
def show
render json: @user
end
def update
@user.update!(user_params)
render json: @user
end
private
def user_params
params.require(:user).permit(:email)
end
end
end
end

View File

@@ -0,0 +1,19 @@
module Api
module V1
class WeathersController < ApplicationController
def index
unless params[:postal_code].blank?
weather = WeatherRetriever.get(Date.parse(params.require(:date)), params.require(:postal_code))
end
authorize! :read, weather
if weather.present?
render json: weather
else
render json: {weathers: []}
end
end
end
end
end

View File

@@ -0,0 +1,50 @@
class ApplicationController < ActionController::API
include CanCan::ControllerAdditions
include ActionController::Serialization
include ExceptionLogger
before_action :authenticate_user_from_token!, if: :presence_of_authentication_token?
before_action :authenticate_user!, except: [:root]
before_action :set_locale
def root
render json: {
ios: {
major: 0,
minor: 0
},
android: {
major: 0,
minor: 0
}
}
end
protected
def set_locale
I18n.locale = user_signed_in? ? current_user.locale : I18n.default_locale
rescue I18n::InvalidLocale
# FIXME
Rails.logger.warn("'#{current_user.profile.locale}' locale for user '#{current_user.email}' not available or invalid, using default")
I18n.locale = I18n.default_locale
end
private
def authenticate_user_from_token!
user = User.find_by(authorization)
sign_in user, store: false if user
end
def authorization
@authorization ||=
/^Token token="(?<token>.*)", email="(?<email>.*)"$/.match(request.headers["Authorization"]) || {}
{authentication_token: @authorization[:token], email: @authorization[:email]}
end
def presence_of_authentication_token?
authorization[:authentication_token].present?
end
end

View File

@@ -0,0 +1,56 @@
module ExceptionLogger
extend ActiveSupport::Concern
included do
# keep this on top
if Rails.env.production?
rescue_from "Exception" do |exception|
render json: {errors: exception.message}, status: :unprocessable_entity
end
end
rescue_from "ActiveRecord::RecordNotFound", "Mongoid::Errors::DocumentNotFound" do
render json: {errors: ["Resource Not Found"]}, status: 404
end
rescue_from "ActionController::UnknownFormat" do
render json: {errors: ["Format not supported"]}, status: 406
end
rescue_from "ActiveRecord::RecordInvalid", "Mongoid::Errors::Validations" do |exception|
log_exception(exception)
render json: {errors: exception.record.errors}, status: :unprocessable_entity
end
rescue_from "ActionController::ParameterMissing" do |exception|
render json: {errors: ["Required parameter missing: #{exception.param}"]}, status: :unprocessable_entity
end
rescue_from "ActionController::BadRequest" do |exception|
render json: {errors: ["Bad request: #{exception.message}"]}, status: :bad_request
end
rescue_from "CanCan::AccessDenied" do |exception|
log_exception(exception)
render json: {errors: ["Unauthorized"]}, status: :unauthorized
end
end
protected
# --- FOR RAILS 4: ---
## @exception = env["action_dispatch.exception"]
## exception_wrapper = ActionDispatch::ExceptionWrapper.new(env, @exception)
# --- FOR RAILS 5: ---
def log_exception(exception)
application_trace = ActionDispatch::ExceptionWrapper.new(request.env["action_dispatch.backtrace_cleaner"], exception)
trace = application_trace.application_trace
trace = trace.map! { |t| " #{t}\n" }
Rails.logger.error "\n#{exception.class.name} (#{exception.message}):\n#{trace.join}"
end
end