Merge branch 'develop' into CW-1823
This commit is contained in:
@@ -157,7 +157,7 @@ jobs:
|
||||
- run:
|
||||
name: Code Climate Test Coverage
|
||||
command: |
|
||||
~/tmp/cc-test-reporter format-coverage -t lcov -o coverage/codeclimate.frontend_$CIRCLE_NODE_INDEX.json buildreports/lcov.info
|
||||
~/tmp/cc-test-reporter format-coverage -t lcov -o "coverage/codeclimate.frontend_$CIRCLE_NODE_INDEX.json"
|
||||
|
||||
- persist_to_workspace:
|
||||
root: coverage
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts::Contacts::BaseController
|
||||
include HmacConcern
|
||||
before_action :ensure_inbox, only: [:create]
|
||||
|
||||
def create
|
||||
@contact_inbox = ContactInboxBuilder.new(
|
||||
contact: @contact,
|
||||
inbox: @inbox,
|
||||
source_id: params[:source_id]
|
||||
source_id: params[:source_id],
|
||||
hmac_verified: hmac_verified?
|
||||
).perform
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseController
|
||||
include Events::Types
|
||||
include DateRangeHelper
|
||||
include HmacConcern
|
||||
|
||||
before_action :conversation, except: [:index, :meta, :search, :create, :filter]
|
||||
before_action :inbox, :contact, :contact_inbox, only: [:create]
|
||||
@@ -104,9 +105,6 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
||||
end
|
||||
|
||||
def set_conversation_status
|
||||
# TODO: temporary fallback for the old bot status in conversation, we will remove after couple of releases
|
||||
# commenting this out to see if there are any errors, if not we can remove this in subsequent releases
|
||||
# status = params[:status] == 'bot' ? 'pending' : params[:status]
|
||||
@conversation.status = params[:status]
|
||||
@conversation.snoozed_until = parse_date_time(params[:snoozed_until].to_s) if params[:snoozed_until]
|
||||
end
|
||||
@@ -152,7 +150,8 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
||||
ContactInboxBuilder.new(
|
||||
contact: @contact,
|
||||
inbox: @inbox,
|
||||
source_id: params[:source_id]
|
||||
source_id: params[:source_id],
|
||||
hmac_verified: hmac_verified?
|
||||
).perform
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module HmacConcern
|
||||
def hmac_verified?
|
||||
ActiveModel::Type::Boolean.new.cast(params[:hmac_verified]).present?
|
||||
end
|
||||
end
|
||||
@@ -50,6 +50,13 @@ class SuperAdmin::AccountsController < SuperAdmin::ApplicationController
|
||||
# rubocop:enable Rails/I18nLocaleTexts
|
||||
end
|
||||
|
||||
def reset_cache
|
||||
requested_resource.reset_cache_keys
|
||||
# rubocop:disable Rails/I18nLocaleTexts
|
||||
redirect_back(fallback_location: [namespace, requested_resource], notice: 'Cache keys cleared')
|
||||
# rubocop:enable Rails/I18nLocaleTexts
|
||||
end
|
||||
|
||||
def destroy
|
||||
account = Account.find(params[:id])
|
||||
|
||||
|
||||
@@ -10,15 +10,22 @@
|
||||
"TITLE": "Manage Audit Logs",
|
||||
"DESC": "Audit Logs are trails for events and actions in a Chatwoot System.",
|
||||
"TABLE_HEADER": [
|
||||
"User",
|
||||
"Action",
|
||||
"IP Address",
|
||||
"Time"
|
||||
"Activity",
|
||||
"Time",
|
||||
"IP Address"
|
||||
]
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "AuditLogs retrieved successfully",
|
||||
"ERROR_MESSAGE": "Could not connect to Woot Server, Please try again later"
|
||||
},
|
||||
"ACTION": {
|
||||
"ADD": "created",
|
||||
"EDIT": "updated",
|
||||
"DELETE": "deleted",
|
||||
"SIGN_IN": "signed in",
|
||||
"SIGN_OUT": "signed out",
|
||||
"SYSTEM": "System"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="column content-box">
|
||||
<div class="column content-box audit-log--settings">
|
||||
<!-- List Audit Logs -->
|
||||
<div class="row">
|
||||
<div class="small-8 columns with-right-space ">
|
||||
<div>
|
||||
<div>
|
||||
<p
|
||||
v-if="!uiFlags.fetchingList && !records.length"
|
||||
class="no-items-error-message"
|
||||
@@ -16,8 +16,13 @@
|
||||
|
||||
<table
|
||||
v-if="!uiFlags.fetchingList && records.length"
|
||||
class="woot-table"
|
||||
class="woot-table width-100"
|
||||
>
|
||||
<colgroup>
|
||||
<col class="column-activity" />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<!-- Header -->
|
||||
<th
|
||||
@@ -29,12 +34,8 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="auditLogItem in records" :key="auditLogItem.id">
|
||||
<td class="wrap-break-words">{{ auditLogItem.username }}</td>
|
||||
<td class="wrap-break-words">
|
||||
{{ auditLogItem.auditable_type }}.{{ auditLogItem.action }}
|
||||
</td>
|
||||
<td class="remote-address">
|
||||
{{ auditLogItem.remote_address }}
|
||||
{{ generateLogText(auditLogItem) }}
|
||||
</td>
|
||||
<td class="wrap-break-words">
|
||||
{{
|
||||
@@ -44,6 +45,9 @@
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="remote-address">
|
||||
{{ auditLogItem.remote_address }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -81,13 +85,48 @@ export default {
|
||||
records: 'auditlogs/getAuditLogs',
|
||||
uiFlags: 'auditlogs/getUIFlags',
|
||||
meta: 'auditlogs/getMeta',
|
||||
agentList: 'agents/getAgents',
|
||||
}),
|
||||
},
|
||||
mounted() {
|
||||
// Fetch API Call
|
||||
this.$store.dispatch('auditlogs/fetch', { page: 1 });
|
||||
this.$store.dispatch('agents/get');
|
||||
},
|
||||
methods: {
|
||||
getAgentName(email) {
|
||||
if (email === null) {
|
||||
return this.$t('AUDIT_LOGS.ACTION.SYSTEM');
|
||||
}
|
||||
const agentName = this.agentList.find(agent => agent.email === email)
|
||||
?.name;
|
||||
// If agent does not exist(removed/deleted), return email from audit log
|
||||
return agentName || email;
|
||||
},
|
||||
generateLogText(auditLogItem) {
|
||||
const username = this.getAgentName(auditLogItem.username);
|
||||
const auditableType = auditLogItem.auditable_type.toLowerCase();
|
||||
const action = auditLogItem.action.toLowerCase();
|
||||
|
||||
const logActions = {
|
||||
create: this.$t('AUDIT_LOGS.ACTION.ADD'),
|
||||
destroy: this.$t('AUDIT_LOGS.ACTION.DELETE'),
|
||||
update: this.$t('AUDIT_LOGS.ACTION.EDIT'),
|
||||
sign_in: this.$t('AUDIT_LOGS.ACTION.SIGN_IN'),
|
||||
sign_out: this.$t('AUDIT_LOGS.ACTION.SIGN_OUT'),
|
||||
};
|
||||
|
||||
// detect if the action is custom user action, which involves
|
||||
// only the user, such as signing in, signing out etc.
|
||||
// if it is, then do not show the auditable type
|
||||
const userActions = this.getUserActions(action);
|
||||
return `${username} ${logActions[action] || action} ${
|
||||
userActions ? '' : auditableType
|
||||
}`;
|
||||
},
|
||||
getUserActions(action) {
|
||||
return ['sign_in', 'sign_out'].includes(action);
|
||||
},
|
||||
onPageChange(page) {
|
||||
window.history.pushState({}, null, `${this.$route.path}?page=${page}`);
|
||||
try {
|
||||
@@ -101,12 +140,24 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.remote-address {
|
||||
width: 14rem;
|
||||
}
|
||||
.wrap-break-words {
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.audit-log--settings {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
|
||||
.remote-address {
|
||||
width: 14rem;
|
||||
}
|
||||
|
||||
.wrap-break-words {
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.column-activity {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -42,11 +42,10 @@
|
||||
</div>
|
||||
<p
|
||||
v-if="message.showAvatar || hasRecordedResponse"
|
||||
v-dompurify-html="agentName"
|
||||
class="agent-name"
|
||||
:class="$dm('text-slate-700', 'dark:text-slate-200')"
|
||||
>
|
||||
{{ agentName }}
|
||||
</p>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,10 +3,8 @@ class Channels::Whatsapp::TemplatesSyncSchedulerJob < ApplicationJob
|
||||
|
||||
def perform
|
||||
Channel::Whatsapp.where('message_templates_last_updated <= ? OR message_templates_last_updated IS NULL',
|
||||
15.minutes.ago).find_in_batches do |channels_batch|
|
||||
channels_batch.each do |channel|
|
||||
Channels::Whatsapp::TemplatesSyncJob.perform_later(channel)
|
||||
end
|
||||
3.hours.ago).limit(Limits::BULK_EXTERNAL_HTTP_CALLS_LIMIT).all.each do |channel|
|
||||
Channels::Whatsapp::TemplatesSyncJob.perform_later(channel)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,29 +4,41 @@ module CacheKeys
|
||||
include CacheKeysHelper
|
||||
include Events::Types
|
||||
|
||||
included do
|
||||
class_attribute :cacheable_models
|
||||
self.cacheable_models = [Label, Inbox, Team]
|
||||
end
|
||||
|
||||
def cache_keys
|
||||
{
|
||||
label: fetch_value_for_key(id, Label.name.underscore),
|
||||
inbox: fetch_value_for_key(id, Inbox.name.underscore),
|
||||
team: fetch_value_for_key(id, Team.name.underscore)
|
||||
}
|
||||
keys = {}
|
||||
self.class.cacheable_models.each do |model|
|
||||
keys[model.name.underscore.to_sym] = fetch_value_for_key(id, model.name.underscore)
|
||||
end
|
||||
|
||||
keys
|
||||
end
|
||||
|
||||
def invalidate_cache_key_for(key)
|
||||
prefixed_cache_key = get_prefixed_cache_key(id, key)
|
||||
Redis::Alfred.del(prefixed_cache_key)
|
||||
dispatch_cache_udpate_event
|
||||
Redis::Alfred.delete(prefixed_cache_key)
|
||||
dispatch_cache_update_event
|
||||
end
|
||||
|
||||
def update_cache_key(key)
|
||||
prefixed_cache_key = get_prefixed_cache_key(id, key)
|
||||
Redis::Alfred.set(prefixed_cache_key, Time.now.utc.to_i)
|
||||
dispatch_cache_udpate_event
|
||||
dispatch_cache_update_event
|
||||
end
|
||||
|
||||
def reset_cache_keys
|
||||
self.class.cacheable_models.each do |model|
|
||||
invalidate_cache_key_for(model.name.underscore)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dispatch_cache_udpate_event
|
||||
def dispatch_cache_update_event
|
||||
Rails.configuration.dispatcher.dispatch(ACCOUNT_CACHE_INVALIDATED, Time.zone.now, cache_keys: cache_keys, account: self)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,7 +23,8 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
|
||||
|
||||
def sync_templates
|
||||
response = HTTParty.get("#{api_base_path}/configs/templates", headers: api_headers)
|
||||
whatsapp_channel.update(message_templates: response['waba_templates'], message_templates_last_updated: Time.now.utc) if response.success?
|
||||
whatsapp_channel[:message_templates] = response['waba_templates'] if response.success?
|
||||
whatsapp_channel.update(message_templates_last_updated: Time.now.utc)
|
||||
end
|
||||
|
||||
def validate_provider_config?
|
||||
|
||||
@@ -24,8 +24,8 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
|
||||
|
||||
def sync_templates
|
||||
templates = fetch_whatsapp_templates("#{business_account_path}/message_templates?access_token=#{whatsapp_channel.provider_config['api_key']}")
|
||||
|
||||
whatsapp_channel.update(message_templates: templates, message_templates_last_updated: Time.now.utc) if templates.present?
|
||||
whatsapp_channel[:message_templates] = templates if templates.present?
|
||||
whatsapp_channel.update(message_templates_last_updated: Time.now.utc)
|
||||
end
|
||||
|
||||
def fetch_whatsapp_templates(url)
|
||||
|
||||
@@ -35,7 +35,9 @@
|
||||
<div class="flex flex-col items-start justify-between w-full md:flex-row md:items-center pt-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
<% if @article.author&.avatar_url&.present? %>
|
||||
<img src="<%= @article.author.avatar_url %>" alt="<%= @article.author.display_name %>" class="w-12 h-12 border rounded-full pr-1">
|
||||
<div class="pr-1">
|
||||
<img src="<%= @article.author.avatar_url %>" alt="<%= @article.author.display_name %>" class="w-12 h-12 border rounded-full">
|
||||
</div>
|
||||
<% end %>
|
||||
<div>
|
||||
<h5 class="text-base font-medium text-slate-900 mb-2"><%= @article.author.available_name %></h5>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<section class="main-content__body">
|
||||
<hr/>
|
||||
<%= form_for([:reset_cache, namespace, page.resource], method: :post, html: { class: "form" }) do |f| %>
|
||||
<div class="form-actions">
|
||||
<p>This will clear the IndexedDB cache keys from redis. <br>The next load will fetch the data from backend.</p>
|
||||
<%= f.submit 'Reset Frontend Cache' %>
|
||||
</div>
|
||||
<% end %>
|
||||
</section>
|
||||
@@ -87,3 +87,5 @@ as well as a link to its edit page.
|
||||
</section>
|
||||
|
||||
<%= render partial: "seed_data", locals: {page: page} %>
|
||||
|
||||
<%= render partial: "reset_cache", locals: {page: page} %>
|
||||
|
||||
@@ -397,6 +397,7 @@ Rails.application.routes.draw do
|
||||
# order of resources affect the order of sidebar navigation in super admin
|
||||
resources :accounts, only: [:index, :new, :create, :show, :edit, :update, :destroy] do
|
||||
post :seed, on: :member
|
||||
post :reset_cache, on: :member
|
||||
end
|
||||
resources :users, only: [:index, :new, :create, :show, :edit, :update, :destroy]
|
||||
resources :access_tokens, only: [:index, :show]
|
||||
|
||||
+8
-4
@@ -13,11 +13,15 @@ module.exports = {
|
||||
'^.+\\.(js|jsx)?$': 'babel-jest',
|
||||
},
|
||||
cacheDirectory: '<rootDir>/.jest-cache',
|
||||
collectCoverage: false,
|
||||
coverageDirectory: 'buildreports',
|
||||
collectCoverageFrom: ['**/app/javascript/**/*.{js,vue}'],
|
||||
collectCoverage: true,
|
||||
coverageDirectory: 'coverage',
|
||||
coverageReporters: ['text', 'lcov'],
|
||||
collectCoverageFrom: [
|
||||
'**/app/javascript/**/*.js',
|
||||
'!**/*.stories.js',
|
||||
'!**/i18n/locale/**/*.js',
|
||||
],
|
||||
reporters: ['default'],
|
||||
// setupTestFrameworkScriptFile: './tests/setup.ts',
|
||||
transformIgnorePatterns: ['node_modules/*'],
|
||||
moduleNameMapper: {
|
||||
'^@/(.*)$': '<rootDir>/app/javascript/$1',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
module Limits
|
||||
BULK_ACTIONS_LIMIT = 100
|
||||
BULK_EXTERNAL_HTTP_CALLS_LIMIT = 25
|
||||
end
|
||||
|
||||
@@ -27,7 +27,9 @@ RSpec.describe '/api/v1/accounts/{account.id}/contacts/:id/contact_inboxes', typ
|
||||
end.to change(ContactInbox, :count).by(1)
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(contact.reload.contact_inboxes.map(&:inbox_id)).to include(channel_api.inbox.id)
|
||||
contact_inbox = contact.reload.contact_inboxes.find_by(inbox_id: channel_api.inbox.id)
|
||||
expect(contact_inbox).to be_present
|
||||
expect(contact_inbox.hmac_verified).to be(false)
|
||||
end
|
||||
|
||||
it 'creates a valid email contact inbox' do
|
||||
@@ -43,6 +45,21 @@ RSpec.describe '/api/v1/accounts/{account.id}/contacts/:id/contact_inboxes', typ
|
||||
expect(contact.reload.contact_inboxes.map(&:inbox_id)).to include(channel_email.inbox.id)
|
||||
end
|
||||
|
||||
it 'creates an hmac verified contact inbox' do
|
||||
create(:inbox_member, inbox: channel_api.inbox, user: agent)
|
||||
expect do
|
||||
post "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/contact_inboxes",
|
||||
params: { inbox_id: channel_api.inbox.id, hmac_verified: true },
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
end.to change(ContactInbox, :count).by(1)
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
contact_inbox = contact.reload.contact_inboxes.find_by(inbox_id: channel_api.inbox.id)
|
||||
expect(contact_inbox).to be_present
|
||||
expect(contact_inbox.hmac_verified).to be(true)
|
||||
end
|
||||
|
||||
it 'throws error for invalid source id' do
|
||||
create(:inbox_member, inbox: channel_twilio_sms.inbox, user: agent)
|
||||
expect do
|
||||
|
||||
@@ -273,21 +273,6 @@ RSpec.describe 'Conversations API', type: :request do
|
||||
expect(response_data[:status]).to eq('pending')
|
||||
end
|
||||
|
||||
# TODO: remove this spec when we remove the condition check in controller
|
||||
# Added for backwards compatibility for bot status
|
||||
# remove this in subsequent release
|
||||
# it 'creates a conversation as pending if status is specified as bot' do
|
||||
# allow(Rails.configuration.dispatcher).to receive(:dispatch)
|
||||
# post "/api/v1/accounts/#{account.id}/conversations",
|
||||
# headers: agent.create_new_auth_token,
|
||||
# params: { source_id: contact_inbox.source_id, status: 'bot' },
|
||||
# as: :json
|
||||
|
||||
# expect(response).to have_http_status(:success)
|
||||
# response_data = JSON.parse(response.body, symbolize_names: true)
|
||||
# expect(response_data[:status]).to eq('pending')
|
||||
# end
|
||||
|
||||
it 'creates a new conversation with message when message is passed' do
|
||||
allow(Rails.configuration.dispatcher).to receive(:dispatch)
|
||||
post "/api/v1/accounts/#{account.id}/conversations",
|
||||
@@ -304,13 +289,13 @@ RSpec.describe 'Conversations API', type: :request do
|
||||
it 'calls contact inbox builder if contact_id and inbox_id is present' do
|
||||
builder = double
|
||||
allow(Rails.configuration.dispatcher).to receive(:dispatch)
|
||||
allow(ContactInboxBuilder).to receive(:new).and_return(builder)
|
||||
allow(ContactInboxBuilder).to receive(:new).with(contact: contact, inbox: inbox, source_id: nil, hmac_verified: false).and_return(builder)
|
||||
allow(builder).to receive(:perform)
|
||||
expect(builder).to receive(:perform)
|
||||
|
||||
post "/api/v1/accounts/#{account.id}/conversations",
|
||||
headers: agent.create_new_auth_token,
|
||||
params: { contact_id: contact.id, inbox_id: inbox.id },
|
||||
params: { contact_id: contact.id, inbox_id: inbox.id, hmac_verified: 'false' },
|
||||
as: :json
|
||||
end
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@ RSpec.describe 'Super Admin accounts API', type: :request do
|
||||
end
|
||||
|
||||
context 'when it is an authenticated user' do
|
||||
let!(:account) { create(:account) }
|
||||
|
||||
it 'shows the list of accounts' do
|
||||
sign_in(super_admin, scope: :super_admin)
|
||||
get '/super_admin/accounts'
|
||||
@@ -27,6 +25,32 @@ RSpec.describe 'Super Admin accounts API', type: :request do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /super_admin/accounts/{account_id}/reset_cache' do
|
||||
before do
|
||||
create(:label, account: account)
|
||||
create(:inbox, account: account)
|
||||
create(:team, account: account)
|
||||
end
|
||||
|
||||
context 'when it is an unauthenticated user' do
|
||||
it 'returns unauthorized' do
|
||||
post "/super_admin/accounts/#{account.id}/reset_cache"
|
||||
expect(response).to have_http_status(:redirect)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an authenticated user' do
|
||||
it 'shows the list of accounts' do
|
||||
expect(account.cache_keys.keys).to contain_exactly(:inbox, :label, :team)
|
||||
sign_in(super_admin, scope: :super_admin)
|
||||
post "/super_admin/accounts/#{account.id}/reset_cache"
|
||||
expect(response).to have_http_status(:redirect)
|
||||
expect(flash[:notice]).to eq('Cache keys cleared')
|
||||
expect(account.reload.cache_keys.values.map(&:to_i)).to eq([0, 0, 0])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /super_admin/accounts/{account_id}' do
|
||||
context 'when it is an unauthenticated user' do
|
||||
it 'returns unauthorized' do
|
||||
|
||||
@@ -11,7 +11,7 @@ RSpec.describe Channels::Whatsapp::TemplatesSyncSchedulerJob do
|
||||
stub_request(:post, 'https://waba.360dialog.io/v1/configs/webhook')
|
||||
non_synced = create(:channel_whatsapp, sync_templates: false, message_templates_last_updated: nil)
|
||||
synced_recently = create(:channel_whatsapp, sync_templates: false, message_templates_last_updated: Time.zone.now)
|
||||
synced_old = create(:channel_whatsapp, sync_templates: false, message_templates_last_updated: 16.minutes.ago)
|
||||
synced_old = create(:channel_whatsapp, sync_templates: false, message_templates_last_updated: 4.hours.ago)
|
||||
described_class.perform_now
|
||||
expect(Channels::Whatsapp::TemplatesSyncJob).not_to(
|
||||
have_been_enqueued.with(synced_recently).on_queue('low')
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe CacheKeys do
|
||||
let(:test_model) do
|
||||
Struct.new(:id) do
|
||||
include CacheKeys
|
||||
|
||||
def fetch_value_for_key(_id, _key)
|
||||
'value'
|
||||
end
|
||||
end.new(1)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Redis::Alfred).to receive(:delete)
|
||||
allow(Redis::Alfred).to receive(:set)
|
||||
allow(Rails.configuration.dispatcher).to receive(:dispatch)
|
||||
end
|
||||
|
||||
describe '#cache_keys' do
|
||||
it 'returns a hash of cache keys' do
|
||||
expected_keys = test_model.class.cacheable_models.map do |model|
|
||||
[model.name.underscore.to_sym, 'value']
|
||||
end.to_h
|
||||
|
||||
expect(test_model.cache_keys).to eq(expected_keys)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#invalidate_cache_key_for' do
|
||||
it 'deletes the cache key' do
|
||||
test_model.invalidate_cache_key_for('label')
|
||||
expect(Redis::Alfred).to have_received(:delete).with('idb-cache-key-account-1-label')
|
||||
end
|
||||
|
||||
it 'dispatches a cache update event' do
|
||||
test_model.invalidate_cache_key_for('label')
|
||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch).with(
|
||||
CacheKeys::ACCOUNT_CACHE_INVALIDATED,
|
||||
kind_of(ActiveSupport::TimeWithZone),
|
||||
cache_keys: test_model.cache_keys,
|
||||
account: test_model
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_cache_key' do
|
||||
it 'updates the cache key' do
|
||||
allow(Time).to receive(:now).and_return(Time.parse('2023-05-29 00:00:00 UTC'))
|
||||
test_model.update_cache_key('label')
|
||||
expect(Redis::Alfred).to have_received(:set).with('idb-cache-key-account-1-label', Time.now.utc.to_i)
|
||||
end
|
||||
|
||||
it 'dispatches a cache update event' do
|
||||
test_model.update_cache_key('label')
|
||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch).with(
|
||||
CacheKeys::ACCOUNT_CACHE_INVALIDATED,
|
||||
kind_of(ActiveSupport::TimeWithZone),
|
||||
cache_keys: test_model.cache_keys,
|
||||
account: test_model
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reset_cache_keys' do
|
||||
it 'invalidates all cache keys for cacheable models' do
|
||||
test_model.reset_cache_keys
|
||||
test_model.class.cacheable_models.each do |model|
|
||||
expect(Redis::Alfred).to have_received(:delete).with("idb-cache-key-account-1-#{model.name.underscore}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
## the specs are covered in send in spec/services/whatsapp/send_on_whatsapp_service_spec.rb
|
||||
require 'rails_helper'
|
||||
|
||||
describe Whatsapp::Providers::Whatsapp360DialogService do
|
||||
subject(:service) { described_class.new(whatsapp_channel: whatsapp_channel) }
|
||||
|
||||
let!(:whatsapp_channel) { create(:channel_whatsapp, sync_templates: false, validate_provider_config: false) }
|
||||
|
||||
describe '#sync_templates' do
|
||||
context 'when called' do
|
||||
it 'updates message_templates_last_updated even when template request fails' do
|
||||
stub_request(:get, 'https://waba.360dialog.io/v1/configs/templates')
|
||||
.to_return(status: 401)
|
||||
|
||||
timstamp = whatsapp_channel.reload.message_templates_last_updated
|
||||
subject.sync_templates
|
||||
expect(whatsapp_channel.reload.message_templates_last_updated).not_to eq(timstamp)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1 +0,0 @@
|
||||
## the specs are covered in send in spec/services/whatsapp/send_on_whatsapp_service_spec.rb
|
||||
@@ -127,10 +127,21 @@ describe Whatsapp::Providers::WhatsappCloudService do
|
||||
], paging: { prev: 'https://graph.facebook.com/v14.0/123456789/message_templates?access_token=test_key' } }.to_json }
|
||||
)
|
||||
|
||||
timstamp = whatsapp_channel.reload.message_templates_last_updated
|
||||
expect(subject.sync_templates).to be(true)
|
||||
expect(whatsapp_channel.reload.message_templates.first).to eq({ id: '123456789', name: 'test_template' }.stringify_keys)
|
||||
expect(whatsapp_channel.reload.message_templates.second).to eq({ id: '123456789', name: 'next_template' }.stringify_keys)
|
||||
expect(whatsapp_channel.reload.message_templates.last).to eq({ id: '123456789', name: 'last_template' }.stringify_keys)
|
||||
expect(whatsapp_channel.reload.message_templates_last_updated).not_to eq(timstamp)
|
||||
end
|
||||
|
||||
it 'updates message_templates_last_updated even when template request fails' do
|
||||
stub_request(:get, 'https://graph.facebook.com/v14.0/123456789/message_templates?access_token=test_key')
|
||||
.to_return(status: 401)
|
||||
|
||||
timstamp = whatsapp_channel.reload.message_templates_last_updated
|
||||
subject.sync_templates
|
||||
expect(whatsapp_channel.reload.message_templates_last_updated).not_to eq(timstamp)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user