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 +4 -4
- data/app/controllers/audiences/contexts_controller.rb +3 -3
- data/app/controllers/audiences/scim_proxy_controller.rb +2 -4
- data/app/models/audiences/context/locating.rb +39 -0
- data/app/models/audiences/context.rb +1 -8
- data/app/models/audiences/context_users.rb +1 -1
- data/app/models/audiences/criterion_users.rb +2 -1
- data/app/models/audiences/external_user.rb +8 -2
- data/config/routes.rb +4 -5
- data/db/migrate/20240722025634_add_relation_to_context.rb +11 -0
- data/docs/CHANGELOG.md +10 -0
- data/docs/README.md +78 -38
- data/lib/audiences/configuration.rb +84 -0
- data/lib/audiences/engine.rb +8 -0
- data/lib/audiences/model.rb +34 -0
- data/lib/audiences/scim/resource.rb +19 -0
- data/lib/audiences/scim/resources_query.rb +6 -7
- data/lib/audiences/scim.rb +10 -8
- data/lib/audiences/version.rb +1 -1
- data/lib/audiences.rb +7 -31
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a764dbb90f7727e7b0054227fedb0d8eee691ab7e65a5c0007413121de492965
|
4
|
+
data.tar.gz: f4c0fc7843aae456f8df4e2aed78615fc3169257cdebe9a28578443ad32c0949
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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: [:
|
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.
|
7
|
-
|
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)
|
@@ -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.
|
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
|
-
|
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["
|
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
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
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
|
-
##
|
5
|
+
## Installation
|
6
6
|
|
7
|
-
|
7
|
+
Add this line to your application's Gemfile:
|
8
8
|
|
9
|
-
|
9
|
+
```ruby
|
10
|
+
gem "audiences"
|
11
|
+
```
|
10
12
|
|
11
|
-
|
13
|
+
Then execute:
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
```bash
|
16
|
+
$ bundle install
|
17
|
+
```
|
15
18
|
|
16
|
-
|
19
|
+
Or install it yourself with:
|
17
20
|
|
18
|
-
|
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
|
-
|
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
|
24
|
-
|
25
|
-
|
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
|
-
###
|
49
|
+
### Adding Audiences to a Model
|
30
50
|
|
31
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
65
|
+
class Survey < ApplicationRecord
|
66
|
+
has_audience :responders
|
67
|
+
has_audience :supervisors
|
47
68
|
end
|
48
69
|
```
|
49
70
|
|
50
|
-
|
71
|
+
### Listening to Audience Changes
|
51
72
|
|
52
|
-
|
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
|
-
|
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
|
-
|
85
|
+
Or schedule an ActiveJob:
|
58
86
|
|
59
87
|
```ruby
|
60
|
-
|
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
|
-
|
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
|
-
|
66
|
-
$ bundle
|
67
|
-
```
|
98
|
+
See a working example in our dummy app:
|
68
99
|
|
69
|
-
|
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
|
-
|
72
|
-
|
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
|
-
|
117
|
+
For more information, see the [development guide](../../docs/development.md).
|
78
118
|
|
79
119
|
## License
|
80
120
|
|
81
|
-
|
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
|
data/lib/audiences/engine.rb
CHANGED
@@ -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 :
|
12
|
+
attr_reader :options, :resource
|
13
13
|
|
14
|
-
def initialize(client,
|
14
|
+
def initialize(client, resource:, **options)
|
15
15
|
@client = client
|
16
|
-
@
|
17
|
-
@
|
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,
|
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: @
|
62
|
+
@response ||= @client.perform_request(path: @resource.type, method: :Get, query: @options)
|
64
63
|
end
|
65
64
|
end
|
66
65
|
end
|
data/lib/audiences/scim.rb
CHANGED
@@ -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
|
-
|
9
|
-
|
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
|
14
|
-
|
11
|
+
def client
|
12
|
+
Client.new(**Audiences.config.scim)
|
13
|
+
end
|
15
14
|
|
16
|
-
|
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
|
data/lib/audiences/version.rb
CHANGED
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
|
-
|
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
|
-
|
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.
|
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-
|
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
|