Merge pull request #12676 from dacook/add-affiliate_sales_data-connected_app-12550

Add "Affiliate Sales Data" connected app option
This commit is contained in:
Rachel Arnould
2024-07-30 16:14:27 +02:00
committed by GitHub
17 changed files with 300 additions and 90 deletions

View File

@@ -5,12 +5,12 @@ module Admin
def create
authorize! :admin, enterprise
app = ConnectedApp.create!(enterprise_id: enterprise.id)
attributes = {}
attributes[:type] = connected_app_params[:type] if connected_app_params[:type]
ConnectAppJob.perform_later(
app, spree_current_user.spree_api_key,
channel: SessionChannel.for_request(request),
)
app = ConnectedApp.create!(enterprise_id: enterprise.id, **attributes)
app.connect(api_key: spree_current_user.spree_api_key,
channel: SessionChannel.for_request(request))
render_panel
end
@@ -18,15 +18,9 @@ module Admin
def destroy
authorize! :admin, enterprise
app = enterprise.connected_apps.first
app = enterprise.connected_apps.find(params.require(:id))
app.destroy
WebhookDeliveryJob.perform_later(
app.data["destroy"],
"disconnect-app",
nil
)
render_panel
end
@@ -39,5 +33,9 @@ module Admin
def render_panel
redirect_to "#{edit_admin_enterprise_path(enterprise)}#/connected_apps_panel"
end
def connected_app_params
params.permit(:type)
end
end
end

View File

@@ -19,8 +19,8 @@ class ConnectAppJob < ApplicationJob
selector = "#connected-app-discover-regen.enterprise_#{enterprise.id}"
html = ApplicationController.render(
partial: "admin/enterprises/form/connected_apps",
locals: { enterprise: },
partial: "admin/enterprises/form/connected_apps/discover_regen",
locals: { enterprise:, connected_app: enterprise.connected_apps.discover_regen.first },
)
cable_ready[channel].morph(selector:, html:).broadcast

View File

@@ -5,7 +5,31 @@
# Here we store keys and links to access the app.
class ConnectedApp < ApplicationRecord
belongs_to :enterprise
after_destroy :disconnect
scope :discover_regen, -> { where(type: "ConnectedApp") }
scope :affiliate_sales_data, -> { where(type: "ConnectedApps::AffiliateSalesData") }
scope :connecting, -> { where(data: nil) }
scope :ready, -> { where.not(data: nil) }
def connecting?
data.nil?
end
def ready?
!connecting?
end
def connect(api_key:, channel:)
ConnectAppJob.perform_later(self, api_key, channel:)
end
def disconnect
WebhookDeliveryJob.perform_later(
data["destroy"],
"disconnect-app",
nil
)
end
end

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
# An enterprise can opt-in for their data to be included in the affiliate_sales_data endpoint
#
module ConnectedApps
class AffiliateSalesData < ConnectedApp
def connect(_opts)
update! data: true # not-nil value indicates it is ready
end
def disconnect; end
end
end

View File

@@ -156,6 +156,12 @@ module Spree
self.disabled_at = value == '1' ? Time.zone.now : nil
end
def affiliate_enterprises
return [] unless Flipper.enabled?(:affiliate_sales_data, self)
Enterprise.joins(:connected_apps).merge(ConnectedApps::AffiliateSalesData.ready)
end
protected
def password_required?

View File

@@ -1,30 +1,4 @@
%div{ id: "connected-app-discover-regen", class: "enterprise_#{enterprise.id}" }
.connected-app__head
%div
%h3= t ".title"
%p= t ".tagline"
%div
- if enterprise.connected_apps.empty?
= button_to t(".enable"), admin_enterprise_connected_apps_path(enterprise.id), method: :post, disabled: !managed_by_user?(enterprise)
-# This is only seen by super-admins:
%em= t(".need_to_be_manager") unless managed_by_user?(enterprise)
- elsif enterprise.connected_apps.connecting.present?
%button{ disabled: true }
%i.spinner.fa.fa-spin.fa-circle-o-notch
&nbsp;
= t ".loading"
- else
= button_to t(".disable"), admin_enterprise_connected_app_path(0, enterprise_id: enterprise.id), method: :delete
.connected-app__connection
- if enterprise.connected_apps.ready.present?
.connected-app__note
- link = enterprise.connected_apps[0].data["link"]
%p= t ".note"
%div
%a{ href: link, target: "_blank", class: "button secondary" }
= t ".link_label"
%hr
.connected-app__description
= t ".description_html"
= render partial: "/admin/enterprises/form/connected_apps/discover_regen",
locals: { enterprise:, connected_app: enterprise.connected_apps.discover_regen.first }
= render partial: "/admin/enterprises/form/connected_apps/affiliate_sales_data",
locals: { enterprise:, connected_app: enterprise.connected_apps.affiliate_sales_data.first }

View File

@@ -0,0 +1,15 @@
%section.connected_app
.connected-app__head
%div
%h3= t ".title"
%p= t ".tagline"
%div
- if connected_app.nil?
= button_to t(".enable"), admin_enterprise_connected_apps_path(enterprise.id, type: "ConnectedApps::AffiliateSalesData"), method: :post, disabled: !managed_by_user?(enterprise)
-# This is only seen by super-admins:
%em= t(".need_to_be_manager") unless managed_by_user?(enterprise)
- else
= button_to t(".disable"), admin_enterprise_connected_app_path(connected_app.id, enterprise_id: enterprise.id), method: :delete
%hr
.connected-app__description
= t ".description_html"

View File

@@ -0,0 +1,30 @@
%section.connected_app{ id: "connected-app-discover-regen", class: "enterprise_#{enterprise.id}" }
.connected-app__head
%div
%h3= t ".title"
%p= t ".tagline"
%div
- if connected_app.nil?
= button_to t(".enable"), admin_enterprise_connected_apps_path(enterprise.id), method: :post, disabled: !managed_by_user?(enterprise)
-# This is only seen by super-admins:
%em= t(".need_to_be_manager") unless managed_by_user?(enterprise)
- elsif connected_app&.connecting?
%button{ disabled: true }
%i.spinner.fa.fa-spin.fa-circle-o-notch
&nbsp;
= t ".loading"
- else
= button_to t(".disable"), admin_enterprise_connected_app_path(connected_app.id, enterprise_id: enterprise.id), method: :delete
.connected-app__connection
- if connected_app&.ready?
.connected-app__note
- link = connected_app.data["link"]
%p= t ".note"
%div
%a{ href: link, target: "_blank", class: "button secondary" }
= t ".link_label"
%hr
.connected-app__description
= t ".description_html"

View File

@@ -2,10 +2,18 @@
max-width: 615px;
}
.connected_app {
margin-bottom: 2rem;
&:not(:first-of-type) {
border-top: 1px solid $color-border;
}
}
.connected-app__head {
display: flex;
justify-content: space-between;
margin-bottom: 1em;
margin: 1em 0;
h3 {
margin-bottom: 1em;

View File

@@ -1358,28 +1358,45 @@ en:
custom_tab_content: "Content for custom tab"
connected_apps:
legend: "Connected apps"
title: "Discover Regenerative"
tagline: "Allow Discover Regenerative to publish your enterprise information."
enable: "Allow data sharing"
disable: "Stop sharing"
loading: "Loading"
need_to_be_manager: "Only managers can connect apps."
note: |
Your Open Food Network account is connected to Discover Regenerative.
Add or update information on your Discover Regenerative listing here.
link_label: "Manage listing"
description_html: |
<p>
Eligible producers can showcase their regenerative credentials,
farming practices and more through a profile listing.
Simplifying how buyers can find regenerative produce and connect
with producers of interest.
</p>
<p>
<a href="https://about.openfoodnetwork.org.au/regen-produce-portal/"
target="_blank"><b>Learn more about Discover Regenerative</b>
<i class="icon-external-link"></i></a>
</p>
affiliate_sales_data:
title: "INRAE / UFC QUE CHOISIR Research"
tagline: "Allow this research project to access your orders data anonymously"
enable: "Allow data sharing"
disable: "Stop sharing"
loading: "Loading"
need_to_be_manager: "Only managers can connect apps."
description_html: |
<p>
INRAE and UFC QUE CHOISIR are teaming up to study food prices in short food systems and compare them with prices in the supermarket, for a given set of products. The data that is used by INRAE is mixed with data coming from other short food chain platforms in France. No individual product prices will be publicly disclosed through this project.
</p>
<p>
<a href="https://apropos.coopcircuits.fr/"
target="_blank"><b>Learn more about this research project</b>
<i class="icon-external-link"></i></a>
</p>
discover_regen:
title: "Discover Regenerative"
tagline: "Allow Discover Regenerative to publish your enterprise information."
enable: "Allow data sharing"
disable: "Stop sharing"
loading: "Loading"
need_to_be_manager: "Only managers can connect apps."
note: |
Your Open Food Network account is connected to Discover Regenerative.
Add or update information on your Discover Regenerative listing here.
link_label: "Manage listing"
description_html: |
<p>
Eligible producers can showcase their regenerative credentials,
farming practices and more through a profile listing.
Simplifying how buyers can find regenerative produce and connect
with producers of interest.
</p>
<p>
<a href="https://about.openfoodnetwork.org.au/regen-produce-portal/"
target="_blank"><b>Learn more about Discover Regenerative</b>
<i class="icon-external-link"></i></a>
</p>
actions:
edit_profile: Settings
properties: Properties

View File

@@ -0,0 +1,5 @@
class AddTypeToConnectedApps < ActiveRecord::Migration[7.0]
def change
add_column :connected_apps, :type, :string, default: "ConnectedApp", null: false
end
end

View File

@@ -68,6 +68,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_07_18_150852) do
t.json "data"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "type", default: "ConnectedApp", null: false
t.index ["enterprise_id"], name: "index_connected_apps_on_enterprise_id"
end

View File

@@ -12,6 +12,18 @@ namespace :ofn do
enterprise.destroy
end
namespace :enterprises do
desc "Activate connected app type for ALL enterprises"
task :activate_connected_app_type, [:type] => :environment do |_task, args|
Enterprise.find_each do |enterprise|
next if enterprise.connected_apps.public_send(args.type.underscore).exists?
"ConnectedApps::#{args.type.camelize}".constantize.new(enterprise:).connect({})
puts "Enterprise #{enterprise.id} connected."
end
end
end
namespace :dev do
desc 'export enterprises to CSV'
task export_enterprises: :environment do

View File

@@ -20,4 +20,26 @@ RSpec.describe 'enterprises.rake' do
end
end
end
describe ':enterprises' do
describe ':activate_connected_app_type' do
it 'updates only disconnected enterprises' do
# enterprise with affiliate sales data
enterprise_asd = create(:enterprise)
enterprise_asd.connected_apps.create type: 'ConnectedApps::AffiliateSalesData'
# enterprise with different type
enterprise_diff = create(:enterprise)
enterprise_diff.connected_apps.create
expect {
Rake.application.invoke_task(
"ofn:enterprises:activate_connected_app_type[affiliate_sales_data]"
)
}.to change { ConnectedApps::AffiliateSalesData.count }.by(1)
expect(enterprise_asd.connected_apps.affiliate_sales_data.count).to eq 1
expect(enterprise_diff.connected_apps.affiliate_sales_data.count).to eq 1
end
end
end
end

View File

@@ -268,4 +268,34 @@ RSpec.describe Spree::User do
expect(user.disabled).to be_falsey
end
end
describe "#affiliate_enterprises" do
let(:user) { create(:user) }
let(:affiliate_enterprise) { create(:enterprise) }
let(:other_connected_enterprise) { create(:enterprise) }
let(:other_enterprise) { create(:enterprise) }
subject{ user.affiliate_enterprises }
before do
ConnectedApps::AffiliateSalesData.create(enterprise: affiliate_enterprise, data: true)
ConnectedApp.create(enterprise: other_connected_enterprise, data: true)
end
context "user does not have feature" do
it { is_expected.to be_empty }
end
context "user has feature affiliate_sales_data" do
before do
Flipper.enable_actor(:affiliate_sales_data, user)
user.reload
end
it "includes only affiliate enterprises" do
is_expected.to include affiliate_enterprise
is_expected.not_to include other_connected_enterprise
is_expected.not_to include other_enterprise
end
end
end
end

View File

@@ -28,36 +28,91 @@ RSpec.describe "Connected Apps", feature: :connected_apps, vcr: true do
expect(page).to have_content "CONNECTED APPS"
end
it "can be enabled and disabled" do
visit edit_admin_enterprise_path(enterprise)
describe "Discover Regenerative" do
let(:section_heading) { self.class.description }
scroll_to :bottom
click_link "Connected apps"
expect(page).to have_content "Discover Regenerative"
it "can be enabled and disabled" do
visit edit_admin_enterprise_path(enterprise)
click_button "Allow data sharing"
expect(page).not_to have_button "Allow data sharing"
expect(page).to have_button "Loading", disabled: true
scroll_to :bottom
click_link "Connected apps"
perform_enqueued_jobs(only: ConnectAppJob)
expect(page).not_to have_button "Loading", disabled: true
expect(page).to have_content "account is connected"
expect(page).to have_link "Manage listing"
within section_containing_heading do
click_button "Allow data sharing"
end
click_button "Stop sharing"
expect(page).to have_button "Allow data sharing"
expect(page).not_to have_button "Stop sharing"
expect(page).not_to have_content "account is connected"
expect(page).not_to have_link "Manage listing"
# (page is reloaded so we need to evaluate within block again)
within section_containing_heading do
expect(page).not_to have_button "Allow data sharing"
expect(page).to have_button "Loading", disabled: true
perform_enqueued_jobs(only: ConnectAppJob)
expect(page).not_to have_button "Loading", disabled: true
expect(page).to have_content "account is connected"
expect(page).to have_link "Manage listing"
click_button "Stop sharing"
end
within section_containing_heading do
expect(page).to have_button "Allow data sharing"
expect(page).not_to have_button "Stop sharing"
expect(page).not_to have_content "account is connected"
expect(page).not_to have_link "Manage listing"
end
end
it "can't be enabled by non-manager" do
login_as create(:admin_user)
visit "#{edit_admin_enterprise_path(enterprise)}#/connected_apps_panel"
within section_containing_heading do
expect(page).to have_button("Allow data sharing", disabled: true)
expect(page).to have_content "Only managers can connect apps."
end
end
end
it "can't be enabled by non-manager" do
login_as create(:admin_user)
describe "Affiliate Sales Data" do
let(:section_heading) { "INRAE / UFC QUE CHOISIR Research" }
visit "#{edit_admin_enterprise_path(enterprise)}#/connected_apps_panel"
expect(page).to have_content "Discover Regenerative"
it "can be enabled and disabled" do
visit edit_admin_enterprise_path(enterprise)
expect(page).to have_button("Allow data sharing", disabled: true)
expect(page).to have_content "Only managers can connect apps."
scroll_to :bottom
click_link "Connected apps"
within section_containing_heading do
click_button "Allow data sharing"
end
# (page is reloaded so we need to evaluate within block again)
within section_containing_heading do
expect(page).not_to have_button "Allow data sharing"
click_button "Stop sharing"
end
within section_containing_heading do
expect(page).to have_button "Allow data sharing"
expect(page).not_to have_button "Stop sharing"
end
end
it "can't be enabled by non-manager" do
login_as create(:admin_user)
visit "#{edit_admin_enterprise_path(enterprise)}#/connected_apps_panel"
within section_containing_heading do
expect(page).to have_button("Allow data sharing", disabled: true)
expect(page).to have_content "Only managers can connect apps."
end
end
end
def section_containing_heading(heading = section_heading)
page.find("h3", text: heading).ancestor("section")
end
end