<commit_before>require 'rack/push-notification'

require 'sinatra/base'
require 'sinatra/param'

require 'houston'

class Helios::Backend::PushNotification < Sinatra::Base
  helpers Sinatra::Param
  attr_reader :apn_certificate, :apn_environment

  def initialize(app, options = {}, &block)
    super(Rack::PushNotification.new)

    @apn_certificate = options[:apn_certificate] || ENV['APN_CERTIFICATE']
    @apn_environment = options[:apn_environment] || ENV['APN_ENVIRONMENT']
  end

  before do
    content_type :json
  end

  get '/devices/?' do
    param :q, String

    devices = ::Rack::PushNotification::Device.dataset
    devices = devices.filter("tsv @@ to_tsquery('english', ?)", "#{params[:q]}:*") if params[:q] and not params[:q].empty?

    if params[:page] or params[:per_page]
      param :page, Integer, default: 1, min: 1
      param :per_page, Integer, default: 100, in: (1..100)

      {
        devices: devices.limit(params[:per_page], (params[:page] - 1) * params[:per_page]),
        page: params[:page],
        total: devices.count
      }.to_json
    else
      param :limit, Integer, default: 100, in: (1..100)
      param :offset, Integer, default: 0, min: 0

      {
        devices: devices.limit(params[:limit], params[:offset])
      }.to_json
    end
  end

  get '/devices/:token/?' do
    record = ::Rack::PushNotification::Device.find(token: params[:token])

    if record
      {device: record}.to_json
    else
      status 404
    end
  end

  head '/message' do
    status 503 and return unless client

    status 204
  end

  post '/message' do
    status 503 and return unless client

    param :payload, String, empty: false
    param :tokens, Array, empty: false

    tokens = params[:tokens] || ::Rack::PushNotification::Device.all.collect(&:token)

    options = JSON.parse(params[:payload])
    options[:alert] = options["aps"]["alert"]
    options[:badge] = options["aps"]["badge"]
    options[:sound] = options["aps"]["sound"]
    options[:'content-available'] = options["aps"]["content-available"]
    options.delete("aps")

    begin
      notifications = tokens.collect{|token| Houston::Notification.new(options.update({device: token}))}
      client.push(*notifications)

      status 204
    rescue => error
      status 500

      {error: error}.to_json
    end
  end

  private

  def client
    begin
      return nil unless apn_certificate and ::File.exist?(apn_certificate)

      client = case apn_environment.to_sym
               when :development
                 Houston::Client.development
               when :production
                 Houston::Client.production
               end

      client.certificate = ::File.read(apn_certificate)

      return client
    rescue
      return nil
    end<commit_msg>[Issue #86] Fixing behavior for multiple push notifications<commit_after>require 'rack/push-notification'

require 'sinatra/base'
require 'sinatra/param'

require 'houston'

class Helios::Backend::PushNotification < Sinatra::Base
  helpers Sinatra::Param
  attr_reader :apn_certificate, :apn_environment

  def initialize(app, options = {}, &block)
    super(Rack::PushNotification.new)

    @apn_certificate = options[:apn_certificate] || ENV['APN_CERTIFICATE']
    @apn_environment = options[:apn_environment] || ENV['APN_ENVIRONMENT']
  end

  before do
    content_type :json
  end

  get '/devices/?' do
    param :q, String

    devices = ::Rack::PushNotification::Device.dataset
    devices = devices.filter("tsv @@ to_tsquery('english', ?)", "#{params[:q]}:*") if params[:q] and not params[:q].empty?

    if params[:page] or params[:per_page]
      param :page, Integer, default: 1, min: 1
      param :per_page, Integer, default: 100, in: (1..100)

      {
        devices: devices.limit(params[:per_page], (params[:page] - 1) * params[:per_page]),
        page: params[:page],
        total: devices.count
      }.to_json
    else
      param :limit, Integer, default: 100, in: (1..100)
      param :offset, Integer, default: 0, min: 0

      {
        devices: devices.limit(params[:limit], params[:offset])
      }.to_json
    end
  end

  get '/devices/:token/?' do
    record = ::Rack::PushNotification::Device.find(token: params[:token])

    if record
      {device: record}.to_json
    else
      status 404
    end
  end

  head '/message' do
    status 503 and return unless client

    status 204
  end

  post '/message' do
    status 503 and return unless client

    param :payload, String, empty: false
    param :tokens, Array, empty: false

    tokens = params[:tokens] || ::Rack::PushNotification::Device.all.collect(&:token)

    options = JSON.parse(params[:payload])
    options[:alert] = options["aps"]["alert"]
    options[:badge] = options["aps"]["badge"]
    options[:sound] = options["aps"]["sound"]
    options[:'content-available'] = options["aps"]["content-available"]
    options.delete("aps")

    begin
      notifications = tokens.collect{|token| Houston::Notification.new(options.dup.update({device: token}))}
      client.push(*notifications)

      status 204
    rescue => error
      status 500

      {error: error}.to_json
    end
  end

  private

  def client
    begin
      return nil unless apn_certificate and ::File.exist?(apn_certificate)

      client = case apn_environment.to_sym
               when :development
                 Houston::Client.development
               when :production
                 Houston::Client.production
               end

      client.certificate = ::File.read(apn_certificate)

      return client
    rescue
      return nil
    end
{'fraction_non_alphanumeric': 0.1302640970735189, 'fraction_numerical': 0.013561741613133477, 'mean_word_length': 3.029475197699497, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4214785', 'n_tokens_mistral': 1773, 'n_tokens_neox': 1664, 'n_words': 412}