audiences 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7daef3af618db14ea1ebcabaeb1c9268f86071f15c7c0373b56dd0d63a097b9a
4
- data.tar.gz: eed9638cb086173bb514fac0e9764a58ec4a3e202f259243bceff64892acd472
3
+ metadata.gz: a764dbb90f7727e7b0054227fedb0d8eee691ab7e65a5c0007413121de492965
4
+ data.tar.gz: f4c0fc7843aae456f8df4e2aed78615fc3169257cdebe9a28578443ad32c0949
5
5
  SHA512:
6
- metadata.gz: 9aa05ec4daf0bf89945aa15800e26ba610ff6c149c27cd664961b6883b319f263592ac2b876b5f9f1fc3fb93c7c00c7e5d493988693ed629fc32689af70e2a4d
7
- data.tar.gz: 15c4e951c645ad76dd5b8f6c684f47b1920555b93bb0fd954daf6ac7bec1394996fa073a7ee91dcff4b56eab99d73c467b736baaa0db3fb2dda196c850c43881
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
 
@@ -21,7 +21,7 @@ module Audiences
21
21
 
22
22
  def groups_users(group_ids)
23
23
  filter = group_ids.map { "groups.value eq #{_1}" }.join(" OR ")
24
- users = Audiences::Scim.resources(type: :Users, filter: filter)
24
+ users = Audiences::Scim.resource(:Users).query(filter: filter)
25
25
  ExternalUser.wrap(users.all)
26
26
  end
27
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,11 @@
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
+
3
9
  # Version 1.1.2 (2024-06-18)
4
10
 
5
11
  - Ignore empty groups in criterion [#354](https://github.com/powerhome/audiences/pull/354)
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.2"
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.2
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-07-11 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