audiences 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76331d54059446a90b80c23d1c861254acecbeacd2724b9ecf7db435637b8386
4
- data.tar.gz: c17b4fa514d26dbc3f1bf8083df0adee99deedb0afd8356d61505188e86c7ad4
3
+ metadata.gz: a764dbb90f7727e7b0054227fedb0d8eee691ab7e65a5c0007413121de492965
4
+ data.tar.gz: f4c0fc7843aae456f8df4e2aed78615fc3169257cdebe9a28578443ad32c0949
5
5
  SHA512:
6
- metadata.gz: d49dd1a67058d2f9f87aaaff313592237824dba4f94065d42a3e64e74f6261da94b350a845f2e0942ac88bcfa922e4c90b26e94d376b21898819df81e3a7b063
7
- data.tar.gz: '0814d1f51e253f0f266bdc113bd72893dea24efe50db09ea5e7da78f44e3f1c82554c1cb85019f69eb00a747c677737af2aa704624c2581cf63946d9cbefe1a6'
6
+ metadata.gz: 4b04b83f5314e11bdd03d373632683bfe59e39eb2ded010c8b49ccbaf914904f4a05dc9788589488a670ae168df19293a175bc9ade59044568d0f27b8e1769d1
7
+ data.tar.gz: a4abbd7408fceb3cc6349000003cddc78ceb564f2824d93dfbb27e340373603dc6cb9c844136954a95e12a374e1439216cd0d202cb75c6dd8b4feb9f91441545
@@ -3,7 +3,7 @@
3
3
  module Audiences
4
4
  class ContextsController < ApplicationController
5
5
  def show
6
- render_context Audiences.load(params.require(:key))
6
+ render_context Audiences::Context.load(params.require(:key))
7
7
  end
8
8
 
9
9
  def update
@@ -23,7 +23,7 @@ module Audiences
23
23
  private
24
24
 
25
25
  def current_context
26
- @current_context ||= Audiences.load(params.require(:key))
26
+ @current_context ||= Audiences::Context.load(params.require(:key))
27
27
  end
28
28
 
29
29
  def current_criterion
@@ -44,7 +44,7 @@ module Audiences
44
44
  params.permit(
45
45
  :match_all,
46
46
  criteria: [groups: {}],
47
- extra_users: [:id, :displayName, { photos: %i[type value] }]
47
+ extra_users: [:externalId, :displayName, { photos: %i[type value] }]
48
48
  ).to_h.symbolize_keys
49
49
  end
50
50
  end
@@ -3,10 +3,8 @@
3
3
  module Audiences
4
4
  class ScimProxyController < ApplicationController
5
5
  def get
6
- resources = Audiences::Scim.resources(
7
- type: params[:scim_path].to_sym,
8
- filter: params[:filter]
9
- )
6
+ resources = Audiences::Scim.resource(params[:scim_path].to_sym)
7
+ .query(filter: params[:filter])
10
8
 
11
9
  render json: resources
12
10
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Audiences
4
+ class Context
5
+ module Locating
6
+ extend ActiveSupport::Concern
7
+
8
+ SIGNED_GID_RESOURCE = "audiences"
9
+
10
+ class_methods do
11
+ # Finds or creates a context for the given owner/relation
12
+ #
13
+ # @private
14
+ # @param owner [Class<ActiveRecord::Base>] an active record owning the context
15
+ # @Param relation [String,Symbol] a context relation (i.e.: :members)
16
+ # @return [Audiences::Context]
17
+ def for(owner, relation: nil)
18
+ where(owner: owner, relation: relation).first_or_create!
19
+ end
20
+
21
+ # Loads a context given a signed GlobalID key
22
+ #
23
+ # @private
24
+ # @param key [String] signed GlobalID key
25
+ # @return [Audiences::Context]
26
+ # @yield [Audiences::Context]
27
+ def load(key)
28
+ GlobalID::Locator.locate_signed(key, for: SIGNED_GID_RESOURCE).tap do |ctx|
29
+ yield ctx if block_given?
30
+ end
31
+ end
32
+ end
33
+
34
+ def signed_key
35
+ to_sgid(for: SIGNED_GID_RESOURCE)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -6,6 +6,7 @@ module Audiences
6
6
  # users (#criteria, #match_all, #extra_users).
7
7
  #
8
8
  class Context < ApplicationRecord
9
+ include Locating
9
10
  include ::Audiences::MembershipGroup
10
11
 
11
12
  belongs_to :owner, polymorphic: true
@@ -18,14 +19,6 @@ module Audiences
18
19
  self.extra_users = []
19
20
  end
20
21
 
21
- # Finds or creates a context for the given owner
22
- #
23
- # @private
24
- # @return [Audiences::Context]
25
- def self.for(owner)
26
- where(owner: owner).first_or_create!
27
- end
28
-
29
22
  def refresh_users!
30
23
  criteria.each(&:refresh_users!)
31
24
  update!(users: ContextUsers.new(self).to_a)
@@ -20,7 +20,7 @@ module Audiences
20
20
  private
21
21
 
22
22
  def all_users
23
- users = Scim.resources(type: :Users)
23
+ users = Scim.resource(:Users).query
24
24
  ExternalUser.wrap(users.all)
25
25
  end
26
26
 
@@ -11,6 +11,7 @@ module Audiences
11
11
 
12
12
  def each(...)
13
13
  @groups.values
14
+ .reject(&:empty?)
14
15
  .map { |groups| groups_users(groups.pluck("id")) }
15
16
  .reduce(&:&)
16
17
  &.each(...)
@@ -20,7 +21,7 @@ module Audiences
20
21
 
21
22
  def groups_users(group_ids)
22
23
  filter = group_ids.map { "groups.value eq #{_1}" }.join(" OR ")
23
- users = Audiences::Scim.resources(type: :Users, filter: filter)
24
+ users = Audiences::Scim.resource(:Users).query(filter: filter)
24
25
  ExternalUser.wrap(users.all)
25
26
  end
26
27
  end
@@ -2,13 +2,19 @@
2
2
 
3
3
  module Audiences
4
4
  class ExternalUser < ApplicationRecord
5
- has_many :memberships
5
+ if Audiences.config.identity_class
6
+ belongs_to :identity, class_name: Audiences.config.identity_class, # rubocop:disable Rails/ReflectionClassName
7
+ primary_key: Audiences.config.identity_key,
8
+ foreign_key: :user_id,
9
+ optional: true,
10
+ inverse_of: false
11
+ end
6
12
 
7
13
  def self.wrap(resources)
8
14
  return [] unless resources&.any?
9
15
 
10
16
  attrs = resources.map do |data|
11
- { user_id: data["id"], data: data, created_at: Time.current, updated_at: Time.current }
17
+ { user_id: data["externalId"], data: data, created_at: Time.current, updated_at: Time.current }
12
18
  end
13
19
  unique_by = :user_id if connection.supports_insert_conflict_target?
14
20
  upsert_all(attrs, unique_by: unique_by) # rubocop:disable Rails/SkipsModelValidations
data/config/routes.rb CHANGED
@@ -8,13 +8,12 @@ Audiences::Engine.routes.draw do
8
8
  end
9
9
 
10
10
  Rails.application.routes.draw do
11
- mount Audiences::Engine, at: "/audiences", as: :audiences
12
-
13
- direct :audience_context do |owner, options|
14
- audiences.route_for(:signed_context, key: Audiences.sign(owner), **options)
11
+ direct :audience_context do |owner, relation = nil|
12
+ context = Audiences::Context.for(owner, relation: relation)
13
+ Audiences::Engine.routes.url_helpers.route_for(:signed_context, key: context.signed_key, **url_options)
15
14
  end
16
15
 
17
16
  direct :audience_scim_proxy do |options|
18
- audiences.route_for(:scim_proxy, **options)
17
+ Audiences::Engine.routes.url_helpers.route_for(:scim_proxy, **url_options, **options)
19
18
  end
20
19
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddRelationToContext < ActiveRecord::Migration[6.1]
4
+ def change
5
+ add_column :audiences_contexts, :relation, :string, null: true
6
+ remove_index :audiences_contexts, %w[owner_type owner_id], unique: true
7
+ add_index :audiences_contexts, %w[owner_type owner_id relation],
8
+ unique: true,
9
+ name: "index_audiences_contexts_on_owner_type_owner_id_relation"
10
+ end
11
+ end
data/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Unreleased
2
2
 
3
+ # Version 1.2.0 (2024-07-24)
4
+
5
+ - Add `has_audience` and the ability to attach multiple audiences to the same owner [#363](https://github.com/powerhome/audiences/pull/363)
6
+ - Audiences.config/configure helpers [#359](https://github.com/powerhome/audiences/pull/359)
7
+ - Adjust user id to SCIM Protocol [#356](https://github.com/powerhome/audiences/pull/356)
8
+
9
+ # Version 1.1.2 (2024-06-18)
10
+
11
+ - Ignore empty groups in criterion [#354](https://github.com/powerhome/audiences/pull/354)
12
+
3
13
  # Version 1.1.1 (2024-06-18)
4
14
 
5
15
  - Fix default resource attributes [#342](https://github.com/powerhome/audiences/pull/342)
data/docs/README.md CHANGED
@@ -2,80 +2,120 @@
2
2
 
3
3
  "Audiences" is a SCIM-integrated notifier for real-time Rails actions based on group changes.
4
4
 
5
- ## Usage
5
+ ## Installation
6
6
 
7
- ### Creating/Managing audiences
7
+ Add this line to your application's Gemfile:
8
8
 
9
- An audience is tied to an owning model withing your application. For the rest of this document we're going to assume a model Team. To create audiences for a team, using `audiences-react`, you'll render an audiences editor for your model.
9
+ ```ruby
10
+ gem "audiences"
11
+ ```
10
12
 
11
- That can be done with a unobstrusive JS renderer like react-rails, or a custom one as in [our dummy app](../audiences/spec/dummy/app/frontend/entrypoints/application.js). The editor will need two arguments:
13
+ Then execute:
12
14
 
13
- - The context URI: `audience_context_url(owner)` helper
14
- - The SCIM endpoint: `audience_scim_proxy_url` helper if using the [proxy](#configuring-the-scim-proxy), or the SCIM endpoint.
15
+ ```bash
16
+ $ bundle install
17
+ ```
15
18
 
16
- ### Configuring the SCIM backend
19
+ Or install it yourself with:
17
20
 
18
- The Audience::Scim should point to the real SCIM endpoint. The service allows you to configure the endpoint and the credentials/headers:
21
+ ```bash
22
+ $ gem install audiences
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Creating/Managing Audiences
28
+
29
+ An audience is tied to an owning model within your application. In this document, we'll use a `Team` model as an example. To create audiences for a team using `audiences-react`, render an audiences editor for your model.
19
30
 
20
- I.e.:
31
+ This can be done with an unobtrusive JS renderer like `react-rails` or a custom one as shown in [our dummy app](../audiences/spec/dummy/app/frontend/entrypoints/application.js). The editor requires two arguments:
32
+
33
+ - The context URI: `audience_context_url(owner, relation)` helper
34
+ - The SCIM endpoint: `audience_scim_proxy_url` helper if using the [proxy](#configuring-the-scim-proxy), or the SCIM endpoint directly.
35
+
36
+ ### Configuring Audiences
37
+
38
+ The `Audience.config.scim` should point to the SCIM endpoint. Configure the endpoint and the credentials/headers as follows:
21
39
 
22
40
  ```ruby
23
- Audiences::Scim.client = Audiences::Scim::Client.new(
24
- uri: ENV.fetch("SCIM_V2_API"),
25
- headers: { "Authorization" => "Bearer #{ENV.fetch('SCIM_V2_TOKEN')}" }
26
- )
41
+ Audiences.configure do |config|
42
+ config.scim = {
43
+ uri: ENV.fetch("SCIM_V2_API"),
44
+ headers: { "Authorization" => "Bearer #{ENV.fetch('SCIM_V2_TOKEN')}" }
45
+ }
46
+ end
27
47
  ```
28
48
 
29
- ### Listening to audience changes
49
+ ### Adding Audiences to a Model
30
50
 
31
- The goal of audiences is to allow the app to keep up with a mutable group of people. To allow that, `Audiences` includes the `Audiences::Notifications` module, to allow the hosting app to subscribe to audiences related to a certain owner type, and react to that through a block:
51
+ A model object can contain multiple audience contexts using the `has_audience` module helper, which is added to ActiveRecord automatically when configured:
32
52
 
33
53
  ```ruby
34
- Rails.application.config.to_prepare do
35
- Audiences::Notifications.subscribe Team do |context|
36
- team.update_memberships(context.users)
37
- end
54
+ Audiences.configure do |config|
55
+ config.identity_class = "User"
56
+ config.identity_key = "login"
38
57
  end
39
58
  ```
40
59
 
41
- or scheduling an AcitiveJob:
60
+ The `identity_class` represents the SCIM user within the app domain, and the `identity_key` maps directly to the SCIM User's `externalId`.
61
+
62
+ Once configured, add audience contexts to a model:
42
63
 
43
64
  ```ruby
44
- Rails.application.config.to_prepare do
45
- Audiences::Notifications.subscribe Group, job: UpdateGroupMembershipsJob
46
- Audiences::Notifications.subscribe Team, job: UpdateTeamMembershipsJob.set(queue: "low")
65
+ class Survey < ApplicationRecord
66
+ has_audience :responders
67
+ has_audience :supervisors
47
68
  end
48
69
  ```
49
70
 
50
- You can find a working example in our dummy app:
71
+ ### Listening to Audience Changes
51
72
 
52
- - [initializer](../spec/dummy/config/initializers/audiences.rb)
53
- - [job class](../spec/dummy/app/jobs/update_memberships_job.rb)
73
+ Audiences allows your app to keep up with mutable groups of people. To react to audience changes, subscribe to audiences related to a certain owner type and handle changes through a block:
54
74
 
55
- ## Installation
75
+ ```ruby
76
+ Audiences.configure do |config|
77
+ config.notifications do
78
+ subscribe Team do |context|
79
+ team.update_memberships(context.users)
80
+ end
81
+ end
82
+ end
83
+ ```
56
84
 
57
- Add this line to your application's Gemfile:
85
+ Or schedule an ActiveJob:
58
86
 
59
87
  ```ruby
60
- gem "audiences"
88
+ Audiences.configure do |config|
89
+ config.notifications do
90
+ subscribe Group, job: UpdateGroupMembershipsJob
91
+ subscribe Team, job: UpdateTeamMembershipsJob.set(queue: "low")
92
+ end
93
+ end
61
94
  ```
62
95
 
63
- And then execute:
96
+ The notifications block is executed every time the app is loaded or reloaded through a `to_prepare` block, allowing autoloaded constants such as model and job classes to be referenced.
64
97
 
65
- ```bash
66
- $ bundle
67
- ```
98
+ See a working example in our dummy app:
68
99
 
69
- Or install it yourself as:
100
+ - [Initializer](../spec/dummy/config/initializers/audiences.rb)
101
+ - [Job class](../spec/dummy/app/jobs/update_memberships_job.rb)
102
+ - [Example owning model](../spec/dummy/app/models/example_owner.rb)
70
103
 
71
- ```bash
72
- $ gem install audiences
104
+ ### SCIM Resource Attributes
105
+
106
+ Configure which attributes are requested from the SCIM backend for each resource type. `Audiences` requires at least `id` and `displayName`, and also requests `photos` and `externalId` for users by default. To request additional attributes:
107
+
108
+ ```ruby
109
+ Audiences.configure do |config|
110
+ config.resource :Users, attributes: "id,externalId,displayName,photos,name"
111
+ config.resource :Groups, attributes: "id,displayName,mfaRequired"
112
+ end
73
113
  ```
74
114
 
75
115
  ## Contributing
76
116
 
77
- See [development guide](../../docs/development.md).
117
+ For more information, see the [development guide](../../docs/development.md).
78
118
 
79
119
  ## License
80
120
 
81
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
121
+ This gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Audiences
4
+ include ActiveSupport::Configurable
5
+
6
+ # Configuration options
7
+
8
+ #
9
+ # Identity model representing a SCIM User in the current application. I.e.: "User"
10
+ #
11
+ config_accessor :identity_class
12
+
13
+ #
14
+ # The key attribute on `identity_class` matching with the SCIM User externalId.
15
+ # This configuration defaults to `:id`
16
+ #
17
+ config_accessor(:identity_key) { :id }
18
+
19
+ #
20
+ # SCIM service configurations. This should be a Hash containint, at least, the URI.
21
+ #
22
+ # I.e.:
23
+ #
24
+ # Audiences.configure do |config|
25
+ # config.scim = { uri: "http://localhost/api/scim" }
26
+ # end
27
+ #
28
+ # It can also contain HTTP headers, such as "Authorization":
29
+ #
30
+ # I.e.:
31
+ #
32
+ # Audiences.configure do |config|
33
+ # config.scim = {
34
+ # uri: "http://localhost/api/scim",
35
+ # headers: { "Authorization" => "Bearer auth-token" }
36
+ # }
37
+ # end
38
+ #
39
+ config_accessor :scim
40
+
41
+ #
42
+ # Resources defaults. Change this configuration via the `resource` helper.
43
+ # This configuration lists the current Audiences accessible resource defaults,
44
+ # and defaults to Users only. To add other resource types for criteria building.
45
+ #
46
+ # @see `resource`.
47
+ #
48
+ config_accessor :resources do
49
+ { Users: Scim::Resource.new(type: :Users, attributes: "id,externalId,displayName,photos") }
50
+ end
51
+
52
+ #
53
+ # Configures a resource default.
54
+ #
55
+ # @param type [Symbol] the resource type in plural, as in scim (i.e.: :Users)
56
+ # @param attributes [String] the list of attributes to fetch for the resource (i.e.: "id,externalId,displayName")
57
+ # @see [Audiences::Scim::Resource]
58
+ def config.resource(type:, **kwargs)
59
+ config.resources[type] = Scim::Resource.new(type: type, **kwargs)
60
+ end
61
+
62
+ #
63
+ # Notifications configurations.
64
+ # Within this block, you should be able to easily register job classes to execute as
65
+ # audiences are changed. Notice: this block is executed every time your app initializes
66
+ # or reloads. This allows you to reference reloaded constants, like job names:
67
+ #
68
+ # I.e.:
69
+ #
70
+ # Audiences.configure do |config|
71
+ # config.notifications do
72
+ # subscribe CallQueue, job: CallQueueMembersSyncJob
73
+ # subscribe ChatRoom, job: ChatRoomMembersSyncJob
74
+ # end
75
+ # end
76
+ #
77
+ # This block is executed
78
+ # @see [Audiences::Notifications]
79
+ def config.notifications(&block)
80
+ Rails.application.config.to_prepare do
81
+ Notifications.class_eval(&block)
82
+ end
83
+ end
84
+ end
@@ -9,5 +9,13 @@ module Audiences
9
9
  #
10
10
  class Engine < ::Rails::Engine
11
11
  isolate_namespace Audiences
12
+
13
+ initializer "audiences.model" do
14
+ if Audiences.config.identity_class
15
+ ActiveSupport.on_load(:active_record) do
16
+ include Audiences::Model
17
+ end
18
+ end
19
+ end
12
20
  end
13
21
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Audiences
4
+ module Model
5
+ extend ActiveSupport::Concern
6
+
7
+ class_methods do
8
+ #
9
+ # Adds relationships between the audience context and the owner object
10
+ #
11
+ # @param name [Symbol,String] the member relationship name
12
+ #
13
+ # rubocop:disable Naming/PredicateName,Metrics/MethodLength,Metrics/AbcSize
14
+ def has_audience(name)
15
+ has_one :"#{name}_context", -> { where(relation: name) },
16
+ as: :owner, dependent: :destroy,
17
+ class_name: "Audiences::Context"
18
+ has_many :"#{name}_external_users",
19
+ through: :"#{name}_context", source: :users,
20
+ class_name: "Audiences::ExternalUser"
21
+ has_many name, -> { readonly }, through: :"#{name}_external_users", source: :identity
22
+
23
+ scope :"with_#{name}", -> { includes(name) }
24
+ scope :"with_#{name}_context", -> { includes(:"#{name}_context") }
25
+ scope :"with_#{name}_external_users", -> { includes(:"#{name}_external_users") }
26
+
27
+ after_initialize if: :new_record? do
28
+ association(:"#{name}_context").build
29
+ end
30
+ end
31
+ # rubocop:enable Naming/PredicateName,Metrics/MethodLength,Metrics/AbcSize
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Audiences
4
+ module Scim
5
+ class Resource
6
+ attr_accessor :options, :type
7
+
8
+ def initialize(type:, attributes: "id,externalId,displayName", **options)
9
+ @type = type
10
+ @options = options
11
+ @options[:attributes] = attributes
12
+ end
13
+
14
+ def query(**options)
15
+ ResourcesQuery.new(Scim.client, resource: self, **@options, **options)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -9,12 +9,12 @@ module Audiences
9
9
  class ResourcesQuery
10
10
  include Enumerable
11
11
 
12
- attr_reader :query_options
12
+ attr_reader :options, :resource
13
13
 
14
- def initialize(client, resource_type:, **query_options)
14
+ def initialize(client, resource:, **options)
15
15
  @client = client
16
- @resource_type = resource_type
17
- @query_options = query_options
16
+ @resource = resource
17
+ @options = options
18
18
  end
19
19
 
20
20
  def all
@@ -53,14 +53,13 @@ module Audiences
53
53
  def next_page
54
54
  return unless next_page?
55
55
 
56
- ResourcesQuery.new(@client, resource_type: @resource_type, **@query_options,
57
- startIndex: next_index)
56
+ ResourcesQuery.new(@client, resource: @resource, **@options, startIndex: next_index)
58
57
  end
59
58
 
60
59
  private
61
60
 
62
61
  def response
63
- @response ||= @client.perform_request(path: @resource_type, method: :Get, query: @query_options)
62
+ @response ||= @client.perform_request(path: @resource.type, method: :Get, query: @options)
64
63
  end
65
64
  end
66
65
  end
@@ -1,19 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "scim/client"
4
- require_relative "scim/resources_query"
5
-
6
3
  module Audiences
7
4
  module Scim
8
- mattr_accessor :client
9
- mattr_accessor :defaults, default: Hash.new(attributes: "id,displayName")
5
+ autoload :Client, "audiences/scim/client"
6
+ autoload :Resource, "audiences/scim/resource"
7
+ autoload :ResourcesQuery, "audiences/scim/resources_query"
10
8
 
11
9
  module_function
12
10
 
13
- def resources(type:, client: Scim.client, **options)
14
- options = (defaults[type] || {}).merge(options)
11
+ def client
12
+ Client.new(**Audiences.config.scim)
13
+ end
15
14
 
16
- ResourcesQuery.new(client, resource_type: type, **options)
15
+ def resource(type, **options)
16
+ Audiences.config.resources.fetch(type) do
17
+ Resource.new(type: type, **options)
18
+ end
17
19
  end
18
20
  end
19
21
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Audiences
4
- VERSION = "1.1.1"
4
+ VERSION = "1.2.0"
5
5
  end
data/lib/audiences.rb CHANGED
@@ -1,37 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "audiences/version"
4
- require "audiences/scim"
5
- require "audiences/notifications"
6
-
7
3
  # Audiences system
8
4
  # Audiences pushes notifications to your rails app when a
9
5
  # SCIM backend updates a user, notifying matching audiences.
10
6
  #
11
7
  module Audiences
12
- GID_RESOURCE = "audiences"
8
+ autoload :Model, "audiences/model"
9
+ autoload :Notifications, "audiences/notifications"
10
+ autoload :Scim, "audiences/scim"
11
+ autoload :VERSION, "audiences/version"
13
12
 
14
13
  module_function
15
14
 
16
- # Provides a key to load an audience context for the given owner.
17
- # An owner should implment GlobalID::Identification.
18
- #
19
- # @param owner [GlobalID::Identification] an owning model
20
- # @return [String] context key
21
- #
22
- def sign(owner)
23
- owner.to_sgid(for: GID_RESOURCE)
24
- end
25
-
26
- # Loads a context for the given context key
27
- #
28
- # @param token [String] a signed token (see #sign)
29
- # @return Audience::Context
30
- #
31
- def load(key)
32
- locate_context(key, &:readonly!)
33
- end
34
-
35
15
  # Updates the given context
36
16
  #
37
17
  # Params might contain:
@@ -44,18 +24,14 @@ module_function
44
24
  # @return Audience::Context
45
25
  #
46
26
  def update(key, criteria: [], **attrs)
47
- locate_context(key) do |context|
27
+ Audiences::Context.load(key) do |context|
48
28
  context.update!(
49
29
  criteria: ::Audiences::Criterion.map(criteria),
50
30
  **attrs
51
31
  )
52
32
  context.refresh_users!
53
- context.readonly!
54
33
  end
55
34
  end
56
-
57
- private_class_method def locate_context(key, &block)
58
- owner = GlobalID::Locator.locate_signed(key, for: GID_RESOURCE)
59
- ::Audiences::Context.for(owner).tap(&block)
60
- end
61
35
  end
36
+
37
+ require "audiences/configuration"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: audiences
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Palhares
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-18 00:00:00.000000000 Z
11
+ date: 2024-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -39,6 +39,7 @@ files:
39
39
  - app/jobs/audiences/application_job.rb
40
40
  - app/models/audiences/application_record.rb
41
41
  - app/models/audiences/context.rb
42
+ - app/models/audiences/context/locating.rb
42
43
  - app/models/audiences/context_users.rb
43
44
  - app/models/audiences/criterion.rb
44
45
  - app/models/audiences/criterion_users.rb
@@ -55,13 +56,17 @@ files:
55
56
  - db/migrate/20231130165945_create_audiences_external_users.rb
56
57
  - db/migrate/20231130172718_create_audiences_memberships.rb
57
58
  - db/migrate/20231205140046_remove_serialized_users_from_criterion.rb
59
+ - db/migrate/20240722025634_add_relation_to_context.rb
58
60
  - docs/CHANGELOG.md
59
61
  - docs/README.md
60
62
  - lib/audiences.rb
63
+ - lib/audiences/configuration.rb
61
64
  - lib/audiences/engine.rb
65
+ - lib/audiences/model.rb
62
66
  - lib/audiences/notifications.rb
63
67
  - lib/audiences/scim.rb
64
68
  - lib/audiences/scim/client.rb
69
+ - lib/audiences/scim/resource.rb
65
70
  - lib/audiences/scim/resources_query.rb
66
71
  - lib/audiences/version.rb
67
72
  - lib/tasks/audiences_tasks.rake